From bb3f6c5c682cd3f0af3b00711055436c8d5f8683 Mon Sep 17 00:00:00 2001 From: Nitraiolo Date: Sat, 17 Jan 2015 10:11:08 +0000 Subject: [PATCH] Initial clean Cfg USB Loader MOD r78 import --- LICENSE.txt | 339 + Languages/DE.lang | 1892 + Languages/DK.lang | 2365 ++ Languages/ES.lang | 2360 ++ Languages/FI.lang | 2349 ++ Languages/FR.lang | 2382 ++ Languages/GR.lang | 2355 ++ Languages/IT.lang | 2363 ++ Languages/JA.lang | 1931 + Languages/KO.lang | 1879 + Languages/Makefile | 14 + Languages/NL.lang | 2352 ++ Languages/NO.lang | 1880 + Languages/PT_BR.lang | 1911 + Languages/PT_PT.lang | 1893 + Languages/TR.lang | 2451 ++ Languages/ZH_CN-clamis.lang | 1846 + Languages/ZH_CN.lang | 2363 ++ Languages/ZH_TW.lang | 1865 + Languages/add_bom.sh | 8 + Languages/del_bom.sh | 2 + Languages/lang.pot | 2282 + Languages/misc.sh | 13 + Languages/msg_merge.sh | 33 + Languages/msg_miss.sh | 18 + Languages/msgreplace.pl | 40 + Languages/msgreplace.sh | 11 + Makefile | 320 + README-CFG.txt | 3722 ++ USBLoader.pnproj | 1 + cios/dip_frag/LICENSE.txt | 674 + cios/dip_frag/Makefile | 58 + cios/dip_frag/dip.c | 188 + cios/dip_frag/dip.h | 40 + cios/dip_frag/dip_calls.h | 35 + cios/dip_frag/dip_calls.s | 104 + cios/dip_frag/dma.c | 55 + cios/dip_frag/dma.h | 28 + cios/dip_frag/errno.h | 30 + cios/dip_frag/file.c | 61 + cios/dip_frag/file.h | 30 + cios/dip_frag/frag.c | 263 + cios/dip_frag/frag.h | 33 + cios/dip_frag/ioctl.h | 62 + cios/dip_frag/ipc.c | 30 + cios/dip_frag/ipc.h | 44 + cios/dip_frag/link.ld | 70 + cios/dip_frag/main.c | 71 + cios/dip_frag/main.h | 40 + cios/dip_frag/patches.c | 115 + cios/dip_frag/patches.h | 29 + cios/dip_frag/plugin.c | 618 + cios/dip_frag/plugin.h | 77 + cios/dip_frag/sdhc.c | 99 + cios/dip_frag/sdhc.h | 11 + cios/dip_frag/start.s | 71 + cios/dip_frag/swi_mload.c | 58 + cios/dip_frag/swi_mload.h | 32 + cios/dip_frag/syscalls.h | 69 + cios/dip_frag/syscalls.s | 281 + cios/dip_frag/tools.h | 51 + cios/dip_frag/tools.s | 85 + cios/dip_frag/types.h | 40 + cios/dip_frag/usbstorage.c | 300 + cios/dip_frag/usbstorage.h | 16 + cios/dip_frag/wbfs.c | 97 + cios/dip_frag/wbfs.h | 28 + cios/dip_orig/LICENSE.txt | 674 + cios/dip_orig/Makefile | 54 + cios/dip_orig/dip.c | 188 + cios/dip_orig/dip.h | 40 + cios/dip_orig/dip_calls.h | 35 + cios/dip_orig/dip_calls.s | 104 + cios/dip_orig/dma.c | 55 + cios/dip_orig/dma.h | 28 + cios/dip_orig/errno.h | 30 + cios/dip_orig/file.c | 61 + cios/dip_orig/file.h | 30 + cios/dip_orig/ioctl.h | 58 + cios/dip_orig/ipc.c | 30 + cios/dip_orig/ipc.h | 44 + cios/dip_orig/link.ld | 60 + cios/dip_orig/main.c | 71 + cios/dip_orig/main.h | 40 + cios/dip_orig/patches.c | 115 + cios/dip_orig/patches.h | 29 + cios/dip_orig/plugin.c | 566 + cios/dip_orig/plugin.h | 75 + cios/dip_orig/start.s | 71 + cios/dip_orig/swi_mload.c | 58 + cios/dip_orig/swi_mload.h | 32 + cios/dip_orig/syscalls.h | 69 + cios/dip_orig/syscalls.s | 281 + cios/dip_orig/tools.h | 51 + cios/dip_orig/tools.s | 85 + cios/dip_orig/types.h | 40 + cios/dip_orig/wbfs.c | 97 + cios/dip_orig/wbfs.h | 28 + cios/odip_frag/.gitignore | 2 + cios/odip_frag/COPYING | 340 + cios/odip_frag/LEEME.txt | 32 + cios/odip_frag/MakeIt.bat | 4 + cios/odip_frag/Makefile | 149 + cios/odip_frag/bin/odip_frag.bin | Bin 0 -> 9120 bytes cios/odip_frag/include/debug.h | 12 + cios/odip_frag/include/di.h | 27 + cios/odip_frag/include/es.h | 7 + cios/odip_frag/include/ipc.h | 51 + cios/odip_frag/include/module.h | 84 + cios/odip_frag/include/syscalls.h | 57 + cios/odip_frag/include/types.h | 40 + cios/odip_frag/include/utils.h | 24 + cios/odip_frag/include/wbfs.h | 14 + cios/odip_frag/scripts/link.ld | 104 + cios/odip_frag/scripts/nostart.specs | 2 + cios/odip_frag/source/ES_ioctlv_low.s | 40 + cios/odip_frag/source/crt0.s | 107 + cios/odip_frag/source/debug.c | 180 + cios/odip_frag/source/di.c | 195 + cios/odip_frag/source/es_ioctlv.c | 58 + cios/odip_frag/source/frag.c | 267 + cios/odip_frag/source/frag.h | 33 + cios/odip_frag/source/module.c | 632 + cios/odip_frag/source/sdhc.c | 101 + cios/odip_frag/source/sdhc.h | 11 + cios/odip_frag/source/syscalls.s | 109 + cios/odip_frag/source/usb.c | 234 + cios/odip_frag/source/usb.h | 40 + cios/odip_frag/source/usbstorage.c | 303 + cios/odip_frag/source/usbstorage.h | 16 + cios/odip_frag/source/utils.c | 54 + cios/odip_frag/source/wbfs.c | 289 + cios/odip_frag/source/wrappers.s | 111 + cios/odip_plugin/COPYING | 340 + cios/odip_plugin/LEEME.txt | 32 + cios/odip_plugin/MakeIt.bat | 4 + cios/odip_plugin/Makefile | 148 + cios/odip_plugin/bin/odip_plugin.bin | Bin 0 -> 5920 bytes cios/odip_plugin/include/debug.h | 6 + cios/odip_plugin/include/di.h | 27 + cios/odip_plugin/include/es.h | 7 + cios/odip_plugin/include/ipc.h | 51 + cios/odip_plugin/include/module.h | 79 + cios/odip_plugin/include/syscalls.h | 57 + cios/odip_plugin/include/types.h | 40 + cios/odip_plugin/include/utils.h | 20 + cios/odip_plugin/include/wbfs.h | 14 + cios/odip_plugin/scripts/link.ld | 62 + cios/odip_plugin/scripts/nostart.specs | 2 + cios/odip_plugin/source/ES_ioctlv_low.s | 40 + cios/odip_plugin/source/crt0.s | 107 + cios/odip_plugin/source/debug.c | 176 + cios/odip_plugin/source/di.c | 195 + cios/odip_plugin/source/es_ioctlv.c | 58 + cios/odip_plugin/source/module.c | 568 + cios/odip_plugin/source/syscalls.s | 109 + cios/odip_plugin/source/utils.c | 54 + cios/odip_plugin/source/wbfs.c | 289 + cios/odip_plugin/source/wrappers.s | 98 + cios/readme.txt | 67 + cios/sdhc/LICENSE.txt | 339 + cios/sdhc/Makefile | 54 + cios/sdhc/es.c | 65 + cios/sdhc/es.h | 10 + cios/sdhc/ipc.c | 52 + cios/sdhc/ipc.h | 81 + cios/sdhc/libwbfs/libwbfs.c | 541 + cios/sdhc/libwbfs/libwbfs.h | 215 + cios/sdhc/libwbfs/rijndael.c | 398 + cios/sdhc/libwbfs/wiidisc.c | 337 + cios/sdhc/libwbfs/wiidisc.h | 67 + cios/sdhc/libwbfs_os.h | 27 + cios/sdhc/link.ld | 71 + cios/sdhc/main.c | 223 + cios/sdhc/mem.c | 54 + cios/sdhc/mem.h | 11 + cios/sdhc/module.h | 19 + cios/sdhc/sdio.c | 704 + cios/sdhc/sdio.h | 13 + cios/sdhc/start.s | 74 + cios/sdhc/syscalls.h | 72 + cios/sdhc/syscalls.s | 284 + cios/sdhc/timer.c | 69 + cios/sdhc/timer.h | 13 + cios/sdhc/types.h | 40 + cios/sdhc/wbfs.c | 92 + cios/sdhc/wbfs.h | 9 + cios/sdhc_orig/LICENSE.txt | 339 + cios/sdhc_orig/Makefile | 53 + cios/sdhc_orig/es.c | 65 + cios/sdhc_orig/es.h | 10 + cios/sdhc_orig/ipc.c | 52 + cios/sdhc_orig/ipc.h | 81 + cios/sdhc_orig/libwbfs/libwbfs.c | 541 + cios/sdhc_orig/libwbfs/libwbfs.h | 215 + cios/sdhc_orig/libwbfs/rijndael.c | 398 + cios/sdhc_orig/libwbfs/wiidisc.c | 337 + cios/sdhc_orig/libwbfs/wiidisc.h | 67 + cios/sdhc_orig/libwbfs_os.h | 27 + cios/sdhc_orig/link.ld | 60 + cios/sdhc_orig/main.c | 223 + cios/sdhc_orig/mem.c | 54 + cios/sdhc_orig/mem.h | 11 + cios/sdhc_orig/module.h | 19 + cios/sdhc_orig/sdio.c | 704 + cios/sdhc_orig/sdio.h | 13 + cios/sdhc_orig/start.s | 74 + cios/sdhc_orig/syscalls.h | 72 + cios/sdhc_orig/syscalls.s | 284 + cios/sdhc_orig/timer.c | 69 + cios/sdhc_orig/timer.h | 13 + cios/sdhc_orig/types.h | 40 + cios/sdhc_orig/wbfs.c | 92 + cios/sdhc_orig/wbfs.h | 9 + cios/stripios/MakeIt.bat | 4 + cios/stripios/Makefile | 5 + cios/stripios/main.cpp | 371 + cios/stripios/stripios.bin | Bin 0 -> 11074 bytes cios/stripios/stripios.exe | Bin 0 -> 51620 bytes data/background | Bin 0 -> 6369 bytes data/bg_gui | Bin 0 -> 5053 bytes data/button.png | Bin 0 -> 17380 bytes data/button_old.png | Bin 0 -> 7717 bytes data/cover_front | Bin 0 -> 3349 bytes data/cover_side | Bin 0 -> 3002 bytes data/cover_top | Bin 0 -> 2887 bytes data/dip_frag_249_r21 | Bin 0 -> 5680 bytes data/dip_plugin2_bin | Bin 0 -> 3304 bytes data/dip_plugin3_bin | Bin 0 -> 3352 bytes data/dip_plugin4_bin | Bin 0 -> 3224 bytes data/dip_plugin4_bin_31 | Bin 0 -> 3080 bytes data/dip_plugin_249 | Bin 0 -> 5264 bytes data/ehcmodule2_elf | Bin 0 -> 20340 bytes data/ehcmodule3_elf | Bin 0 -> 22264 bytes data/ehcmodule4_elf | Bin 0 -> 32400 bytes data/ehcmodule5_elf | Bin 0 -> 27134 bytes data/ehcmodule_frag | Bin 0 -> 70613 bytes data/gc.wav | Bin 0 -> 214316 bytes data/gui_font | Bin 0 -> 26189 bytes data/hourglass | Bin 0 -> 5593 bytes data/intro | Bin 0 -> 52889 bytes data/intro2 | Bin 0 -> 32080 bytes data/intro3 | Bin 0 -> 20761 bytes data/intro4.jpg | Bin 0 -> 16723 bytes data/intro41 | Bin 0 -> 20776 bytes data/nocover | Bin 0 -> 4997 bytes data/nocover_full | Bin 0 -> 17500 bytes data/odip_frag_bin | Bin 0 -> 9120 bytes data/odip_plugin_bin | Bin 0 -> 5920 bytes data/pointer | Bin 0 -> 2200 bytes data/sdhc_module_elf | Bin 0 -> 5672 bytes data/star | Bin 0 -> 4805 bytes data/window.png | Bin 0 -> 3744 bytes lib/Makefile | 12 + lib/grrlib/GRRLIB/GRRLIB_3D.c | 754 + lib/grrlib/GRRLIB/GRRLIB_bmf.c | 121 + lib/grrlib/GRRLIB/GRRLIB_bmfx.c | 242 + lib/grrlib/GRRLIB/GRRLIB_core.c | 211 + lib/grrlib/GRRLIB/GRRLIB_fbAdvanced.c | 55 + lib/grrlib/GRRLIB/GRRLIB_fileIO.c | 114 + lib/grrlib/GRRLIB/GRRLIB_gecko.c | 59 + lib/grrlib/GRRLIB/GRRLIB_print.c | 100 + lib/grrlib/GRRLIB/GRRLIB_render.c | 423 + lib/grrlib/GRRLIB/GRRLIB_snapshot.c | 66 + lib/grrlib/GRRLIB/GRRLIB_texEdit.c | 388 + lib/grrlib/GRRLIB/GRRLIB_ttf.c | 252 + lib/grrlib/GRRLIB/Makefile | 62 + lib/grrlib/GRRLIB/Makefile-orig | 45 + lib/grrlib/GRRLIB/grrlib.h | 264 + lib/grrlib/GRRLIB/grrlib/GRRLIB__inline.h | 145 + lib/grrlib/GRRLIB/grrlib/GRRLIB__lib.h | 183 + lib/grrlib/GRRLIB/grrlib/GRRLIB_cExtn.h | 43 + lib/grrlib/GRRLIB/grrlib/GRRLIB_clipping.h | 49 + lib/grrlib/GRRLIB/grrlib/GRRLIB_collision.h | 89 + lib/grrlib/GRRLIB/grrlib/GRRLIB_fbComplex.h | 59 + lib/grrlib/GRRLIB/grrlib/GRRLIB_fbGX.h | 46 + lib/grrlib/GRRLIB/grrlib/GRRLIB_fbSimple.h | 114 + lib/grrlib/GRRLIB/grrlib/GRRLIB_handle.h | 68 + lib/grrlib/GRRLIB/grrlib/GRRLIB_pixel.h | 103 + lib/grrlib/GRRLIB/grrlib/GRRLIB_private.h | 50 + lib/grrlib/GRRLIB/grrlib/GRRLIB_settings.h | 91 + lib/grrlib/GRRLIB/grrlib/GRRLIB_texSetup.h | 92 + lib/grrlib/include/grrlib.h | 264 + lib/grrlib/include/grrlib/GRRLIB__inline.h | 145 + lib/grrlib/include/grrlib/GRRLIB__lib.h | 183 + lib/grrlib/include/grrlib/GRRLIB_cExtn.h | 43 + lib/grrlib/include/grrlib/GRRLIB_clipping.h | 49 + lib/grrlib/include/grrlib/GRRLIB_collision.h | 89 + lib/grrlib/include/grrlib/GRRLIB_fbComplex.h | 59 + lib/grrlib/include/grrlib/GRRLIB_fbGX.h | 46 + lib/grrlib/include/grrlib/GRRLIB_fbSimple.h | 114 + lib/grrlib/include/grrlib/GRRLIB_handle.h | 68 + lib/grrlib/include/grrlib/GRRLIB_pixel.h | 103 + lib/grrlib/include/grrlib/GRRLIB_private.h | 50 + lib/grrlib/include/grrlib/GRRLIB_settings.h | 91 + lib/grrlib/include/grrlib/GRRLIB_texSetup.h | 92 + lib/jpeg/include/jconfig.h | 46 + lib/jpeg/include/jmorecfg.h | 371 + lib/jpeg/include/jpeglib.h | 1158 + lib/jpeg/src/Makefile | 53 + lib/jpeg/src/Makefile-orig | 40 + lib/jpeg/src/cderror.h | 134 + lib/jpeg/src/cdjpeg.c | 181 + lib/jpeg/src/cdjpeg.d | 16 + lib/jpeg/src/cdjpeg.h | 187 + lib/jpeg/src/cjpeg.c | 616 + lib/jpeg/src/cjpeg.d | 18 + lib/jpeg/src/djpeg.c | 617 + lib/jpeg/src/djpeg.d | 18 + lib/jpeg/src/jaricom.c | 153 + lib/jpeg/src/jaricom.d | 14 + lib/jpeg/src/jcapimin.c | 288 + lib/jpeg/src/jcapimin.d | 14 + lib/jpeg/src/jcapistd.c | 161 + lib/jpeg/src/jcapistd.d | 14 + lib/jpeg/src/jcarith.c | 934 + lib/jpeg/src/jcarith.d | 14 + lib/jpeg/src/jccoefct.c | 453 + lib/jpeg/src/jccoefct.d | 14 + lib/jpeg/src/jccolor.c | 459 + lib/jpeg/src/jccolor.d | 14 + lib/jpeg/src/jcdctmgr.c | 482 + lib/jpeg/src/jcdctmgr.d | 16 + lib/jpeg/src/jchuff.c | 1576 + lib/jpeg/src/jchuff.d | 14 + lib/jpeg/src/jcinit.c | 65 + lib/jpeg/src/jcinit.d | 14 + lib/jpeg/src/jcmainct.c | 293 + lib/jpeg/src/jcmainct.d | 14 + lib/jpeg/src/jcmarker.c | 680 + lib/jpeg/src/jcmarker.d | 14 + lib/jpeg/src/jcmaster.c | 838 + lib/jpeg/src/jcmaster.d | 14 + lib/jpeg/src/jcomapi.c | 106 + lib/jpeg/src/jcomapi.d | 14 + lib/jpeg/src/jconfig.h | 46 + lib/jpeg/src/jcparam.c | 632 + lib/jpeg/src/jcparam.d | 14 + lib/jpeg/src/jcprepct.c | 358 + lib/jpeg/src/jcprepct.d | 14 + lib/jpeg/src/jcsample.c | 545 + lib/jpeg/src/jcsample.d | 14 + lib/jpeg/src/jctrans.c | 382 + lib/jpeg/src/jctrans.d | 14 + lib/jpeg/src/jdapimin.c | 396 + lib/jpeg/src/jdapimin.d | 14 + lib/jpeg/src/jdapistd.c | 275 + lib/jpeg/src/jdapistd.d | 14 + lib/jpeg/src/jdarith.c | 772 + lib/jpeg/src/jdarith.d | 14 + lib/jpeg/src/jdatadst.c | 267 + lib/jpeg/src/jdatadst.d | 11 + lib/jpeg/src/jdatasrc.c | 274 + lib/jpeg/src/jdatasrc.d | 11 + lib/jpeg/src/jdcoefct.c | 736 + lib/jpeg/src/jdcoefct.d | 14 + lib/jpeg/src/jdcolor.c | 396 + lib/jpeg/src/jdcolor.d | 14 + lib/jpeg/src/jdct.h | 393 + lib/jpeg/src/jddctmgr.c | 384 + lib/jpeg/src/jddctmgr.d | 16 + lib/jpeg/src/jdhuff.c | 1541 + lib/jpeg/src/jdhuff.d | 14 + lib/jpeg/src/jdinput.c | 661 + lib/jpeg/src/jdinput.d | 14 + lib/jpeg/src/jdmainct.c | 512 + lib/jpeg/src/jdmainct.d | 14 + lib/jpeg/src/jdmarker.c | 1406 + lib/jpeg/src/jdmarker.d | 14 + lib/jpeg/src/jdmaster.c | 533 + lib/jpeg/src/jdmaster.d | 14 + lib/jpeg/src/jdmerge.c | 400 + lib/jpeg/src/jdmerge.d | 14 + lib/jpeg/src/jdpostct.c | 290 + lib/jpeg/src/jdpostct.d | 14 + lib/jpeg/src/jdsample.c | 361 + lib/jpeg/src/jdsample.d | 14 + lib/jpeg/src/jdtrans.c | 140 + lib/jpeg/src/jdtrans.d | 14 + lib/jpeg/src/jerror.c | 252 + lib/jpeg/src/jerror.d | 14 + lib/jpeg/src/jerror.h | 304 + lib/jpeg/src/jfdctflt.c | 174 + lib/jpeg/src/jfdctflt.d | 16 + lib/jpeg/src/jfdctfst.c | 230 + lib/jpeg/src/jfdctfst.d | 16 + lib/jpeg/src/jfdctint.c | 4348 ++ lib/jpeg/src/jfdctint.d | 16 + lib/jpeg/src/jidctflt.c | 235 + lib/jpeg/src/jidctflt.d | 16 + lib/jpeg/src/jidctfst.c | 368 + lib/jpeg/src/jidctfst.d | 16 + lib/jpeg/src/jidctint.c | 5137 +++ lib/jpeg/src/jidctint.d | 16 + lib/jpeg/src/jinclude.h | 91 + lib/jpeg/src/jmemmgr.c | 1118 + lib/jpeg/src/jmemmgr.d | 16 + lib/jpeg/src/jmemnobs.c | 109 + lib/jpeg/src/jmemnobs.d | 16 + lib/jpeg/src/jmemsys.h | 198 + lib/jpeg/src/jmorecfg.h | 371 + lib/jpeg/src/jpegint.h | 407 + lib/jpeg/src/jpeglib.h | 1158 + lib/jpeg/src/jpegtran.c | 560 + lib/jpeg/src/jpegtran.d | 20 + lib/jpeg/src/jquant1.c | 856 + lib/jpeg/src/jquant1.d | 14 + lib/jpeg/src/jquant2.c | 1310 + lib/jpeg/src/jquant2.d | 14 + lib/jpeg/src/jutils.c | 231 + lib/jpeg/src/jutils.d | 14 + lib/jpeg/src/jversion.h | 14 + lib/jpeg/src/rdbmp.c | 480 + lib/jpeg/src/rdbmp.d | 16 + lib/jpeg/src/rdcolmap.c | 253 + lib/jpeg/src/rdcolmap.d | 16 + lib/jpeg/src/rdgif.c | 38 + lib/jpeg/src/rdgif.d | 16 + lib/jpeg/src/rdppm.c | 459 + lib/jpeg/src/rdppm.d | 16 + lib/jpeg/src/rdrle.c | 387 + lib/jpeg/src/rdrle.d | 16 + lib/jpeg/src/rdswitch.c | 365 + lib/jpeg/src/rdswitch.d | 16 + lib/jpeg/src/rdtarga.c | 500 + lib/jpeg/src/rdtarga.d | 16 + lib/jpeg/src/transupp.c | 1583 + lib/jpeg/src/transupp.d | 16 + lib/jpeg/src/transupp.h | 210 + lib/jpeg/src/wrbmp.c | 442 + lib/jpeg/src/wrbmp.d | 16 + lib/jpeg/src/wrgif.c | 399 + lib/jpeg/src/wrgif.d | 16 + lib/jpeg/src/wrppm.c | 269 + lib/jpeg/src/wrppm.d | 16 + lib/jpeg/src/wrrle.c | 305 + lib/jpeg/src/wrrle.d | 16 + lib/jpeg/src/wrtarga.c | 253 + lib/jpeg/src/wrtarga.d | 16 + lib/jpeg8a/include/jconfig.h | 46 + lib/jpeg8a/include/jerror.h | 304 + lib/jpeg8a/include/jmorecfg.h | 371 + lib/jpeg8a/include/jpeglib.h | 1158 + lib/jpeg8a/include/jpgogc.h | 29 + lib/libext2fs/AUTHORS | 11 + lib/libext2fs/CREDITS | 9 + lib/libext2fs/LICENSE | 340 + lib/libext2fs/Makefile | 41 + lib/libext2fs/include/ext2.h | 99 + lib/libext2fs/include/ext2_frag.h | 16 + lib/libext2fs/orig/Makefile | 132 + lib/libext2fs/orig/alloc.c | 308 + lib/libext2fs/orig/alloc_sb.c | 86 + lib/libext2fs/orig/alloc_stats.c | 106 + lib/libext2fs/orig/alloc_tables.c | 239 + lib/libext2fs/orig/badblocks.c | 327 + lib/libext2fs/orig/bb_compat.c | 63 + lib/libext2fs/orig/bb_inode.c | 266 + lib/libext2fs/orig/bit_ops.h | 57 + lib/libext2fs/orig/bitmaps.c | 256 + lib/libext2fs/orig/bitops.c | 117 + lib/libext2fs/orig/bitops.h | 638 + lib/libext2fs/orig/blkmap64_ba.c | 327 + lib/libext2fs/orig/blknum.c | 477 + lib/libext2fs/orig/block.c | 623 + lib/libext2fs/orig/bmap.c | 335 + lib/libext2fs/orig/bmap64.h | 61 + lib/libext2fs/orig/bmove.c | 166 + lib/libext2fs/orig/brel.h | 86 + lib/libext2fs/orig/brel_ma.c | 198 + lib/libext2fs/orig/check_desc.c | 103 + lib/libext2fs/orig/closefs.c | 461 + lib/libext2fs/orig/com_err.c | 35 + lib/libext2fs/orig/com_err.h | 70 + lib/libext2fs/orig/config.h | 10 + lib/libext2fs/orig/crc16.c | 73 + lib/libext2fs/orig/crc16.h | 26 + lib/libext2fs/orig/csum.c | 288 + lib/libext2fs/orig/dblist.c | 414 + lib/libext2fs/orig/dblist_dir.c | 79 + lib/libext2fs/orig/dir_iterate.c | 270 + lib/libext2fs/orig/dirblock.c | 126 + lib/libext2fs/orig/dirhash.c | 257 + lib/libext2fs/orig/disc_cache.c | 374 + lib/libext2fs/orig/disc_cache.h | 118 + lib/libext2fs/orig/dupfs.c | 96 + lib/libext2fs/orig/e2image.h | 51 + lib/libext2fs/orig/e2p/e2p.h | 74 + lib/libext2fs/orig/e2p/feature.c | 385 + lib/libext2fs/orig/e2p/fgetflags.c | 95 + lib/libext2fs/orig/e2p/fgetversion.c | 69 + lib/libext2fs/orig/e2p/fsetflags.c | 100 + lib/libext2fs/orig/e2p/fsetversion.c | 67 + lib/libext2fs/orig/e2p/getflags.c | 66 + lib/libext2fs/orig/e2p/getversion.c | 41 + lib/libext2fs/orig/e2p/hashstr.c | 71 + lib/libext2fs/orig/e2p/iod.c | 75 + lib/libext2fs/orig/e2p/ls.c | 403 + lib/libext2fs/orig/e2p/mntopts.c | 147 + lib/libext2fs/orig/e2p/ostype.c | 77 + lib/libext2fs/orig/e2p/parse_num.c | 71 + lib/libext2fs/orig/e2p/pe.c | 39 + lib/libext2fs/orig/e2p/percent.c | 66 + lib/libext2fs/orig/e2p/pf.c | 78 + lib/libext2fs/orig/e2p/ps.c | 31 + lib/libext2fs/orig/e2p/setflags.c | 74 + lib/libext2fs/orig/e2p/setversion.c | 40 + lib/libext2fs/orig/e2p/uuid.c | 84 + lib/libext2fs/orig/expanddir.c | 126 + lib/libext2fs/orig/ext2.c | 420 + lib/libext2fs/orig/ext2_err.h | 152 + lib/libext2fs/orig/ext2_ext_attr.h | 71 + lib/libext2fs/orig/ext2_fs.h | 799 + lib/libext2fs/orig/ext2_internal.c | 1076 + lib/libext2fs/orig/ext2_internal.h | 102 + lib/libext2fs/orig/ext2_io.h | 144 + lib/libext2fs/orig/ext2_types.h | 18 + lib/libext2fs/orig/ext2dir.c | 657 + lib/libext2fs/orig/ext2dir.h | 69 + lib/libext2fs/orig/ext2file.c | 413 + lib/libext2fs/orig/ext2file.h | 60 + lib/libext2fs/orig/ext2fs.h | 1572 + lib/libext2fs/orig/ext2fsP.h | 143 + lib/libext2fs/orig/ext3_extents.h | 109 + lib/libext2fs/orig/ext_attr.c | 155 + lib/libext2fs/orig/extent.c | 2007 + lib/libext2fs/orig/fiemap.h | 68 + lib/libext2fs/orig/fileio.c | 412 + lib/libext2fs/orig/finddev.c | 208 + lib/libext2fs/orig/flushb.c | 74 + lib/libext2fs/orig/freefs.c | 112 + lib/libext2fs/orig/gekko_io.c | 561 + lib/libext2fs/orig/gekko_io.h | 53 + lib/libext2fs/orig/gen_bitmap.c | 560 + lib/libext2fs/orig/gen_bitmap64.c | 563 + lib/libext2fs/orig/get_pathname.c | 160 + lib/libext2fs/orig/getsectsize.c | 91 + lib/libext2fs/orig/getsize.c | 311 + lib/libext2fs/orig/i_block.c | 89 + lib/libext2fs/orig/icount.c | 862 + lib/libext2fs/orig/imager.c | 408 + lib/libext2fs/orig/ind_block.c | 66 + lib/libext2fs/orig/initialize.c | 447 + lib/libext2fs/orig/inline.c | 32 + lib/libext2fs/orig/inode.c | 834 + lib/libext2fs/orig/inode_io.c | 291 + lib/libext2fs/orig/io_manager.c | 112 + lib/libext2fs/orig/irel.h | 114 + lib/libext2fs/orig/irel_ma.c | 372 + lib/libext2fs/orig/ismounted.c | 383 + lib/libext2fs/orig/jfs_compat.h | 68 + lib/libext2fs/orig/jfs_dat.h | 64 + lib/libext2fs/orig/jfs_user.h | 8 + lib/libext2fs/orig/kernel-jbd.h | 952 + lib/libext2fs/orig/kernel-list.h | 112 + lib/libext2fs/orig/link.c | 153 + lib/libext2fs/orig/llseek.c | 139 + lib/libext2fs/orig/lookup.c | 69 + lib/libext2fs/orig/mem_allocate.h | 26 + lib/libext2fs/orig/mkdir.c | 142 + lib/libext2fs/orig/mkjournal.c | 601 + lib/libext2fs/orig/namei.c | 206 + lib/libext2fs/orig/native.c | 27 + lib/libext2fs/orig/newdir.c | 77 + lib/libext2fs/orig/openfs.c | 411 + lib/libext2fs/orig/partitions.h | 49 + lib/libext2fs/orig/progress.c | 86 + lib/libext2fs/orig/punch.c | 324 + lib/libext2fs/orig/read_bb.c | 102 + lib/libext2fs/orig/read_bb_file.c | 105 + lib/libext2fs/orig/res_gdt.c | 220 + lib/libext2fs/orig/rw_bitmaps.c | 351 + lib/libext2fs/orig/sparse.c | 52 + lib/libext2fs/orig/swapfs.c | 315 + lib/libext2fs/orig/tdb.c | 4145 ++ lib/libext2fs/orig/tdb.h | 215 + lib/libext2fs/orig/unlink.c | 99 + lib/libext2fs/orig/valid_blk.c | 55 + lib/libext2fs/orig/version.c | 54 + lib/libext2fs/orig/write_bb_file.c | 34 + lib/libext2fs/source/Makefile | 132 + lib/libext2fs/source/alloc.c | 308 + lib/libext2fs/source/alloc_sb.c | 86 + lib/libext2fs/source/alloc_stats.c | 106 + lib/libext2fs/source/alloc_tables.c | 239 + lib/libext2fs/source/badblocks.c | 327 + lib/libext2fs/source/bb_compat.c | 63 + lib/libext2fs/source/bb_inode.c | 266 + lib/libext2fs/source/bit_ops.h | 57 + lib/libext2fs/source/bitmaps.c | 256 + lib/libext2fs/source/bitops.c | 117 + lib/libext2fs/source/bitops.h | 638 + lib/libext2fs/source/blkmap64_ba.c | 327 + lib/libext2fs/source/blknum.c | 477 + lib/libext2fs/source/block.c | 623 + lib/libext2fs/source/bmap.c | 335 + lib/libext2fs/source/bmap64.h | 61 + lib/libext2fs/source/bmove.c | 166 + lib/libext2fs/source/brel.h | 86 + lib/libext2fs/source/brel_ma.c | 198 + lib/libext2fs/source/check_desc.c | 103 + lib/libext2fs/source/closefs.c | 461 + lib/libext2fs/source/com_err.c | 35 + lib/libext2fs/source/com_err.h | 70 + lib/libext2fs/source/config.h | 10 + lib/libext2fs/source/crc16.c | 73 + lib/libext2fs/source/crc16.h | 26 + lib/libext2fs/source/csum.c | 288 + lib/libext2fs/source/dblist.c | 414 + lib/libext2fs/source/dblist_dir.c | 79 + lib/libext2fs/source/dir_iterate.c | 270 + lib/libext2fs/source/dirblock.c | 126 + lib/libext2fs/source/dirhash.c | 257 + lib/libext2fs/source/disc_cache.c | 374 + lib/libext2fs/source/disc_cache.h | 118 + lib/libext2fs/source/dupfs.c | 96 + lib/libext2fs/source/e2image.h | 51 + lib/libext2fs/source/e2p/e2p.h | 74 + lib/libext2fs/source/e2p/feature.c | 385 + lib/libext2fs/source/e2p/fgetflags.c | 95 + lib/libext2fs/source/e2p/fgetversion.c | 69 + lib/libext2fs/source/e2p/fsetflags.c | 100 + lib/libext2fs/source/e2p/fsetversion.c | 67 + lib/libext2fs/source/e2p/getflags.c | 66 + lib/libext2fs/source/e2p/getversion.c | 41 + lib/libext2fs/source/e2p/hashstr.c | 71 + lib/libext2fs/source/e2p/iod.c | 75 + lib/libext2fs/source/e2p/ls.c | 403 + lib/libext2fs/source/e2p/mntopts.c | 147 + lib/libext2fs/source/e2p/ostype.c | 77 + lib/libext2fs/source/e2p/parse_num.c | 71 + lib/libext2fs/source/e2p/pe.c | 39 + lib/libext2fs/source/e2p/percent.c | 66 + lib/libext2fs/source/e2p/pf.c | 78 + lib/libext2fs/source/e2p/ps.c | 31 + lib/libext2fs/source/e2p/setflags.c | 74 + lib/libext2fs/source/e2p/setversion.c | 40 + lib/libext2fs/source/e2p/uuid.c | 84 + lib/libext2fs/source/expanddir.c | 126 + lib/libext2fs/source/ext2.c | 420 + lib/libext2fs/source/ext2_err.h | 152 + lib/libext2fs/source/ext2_ext_attr.h | 71 + lib/libext2fs/source/ext2_frag.c | 60 + lib/libext2fs/source/ext2_frag.h | 16 + lib/libext2fs/source/ext2_fs.h | 799 + lib/libext2fs/source/ext2_internal.c | 1076 + lib/libext2fs/source/ext2_internal.h | 102 + lib/libext2fs/source/ext2_io.h | 144 + lib/libext2fs/source/ext2_types.h | 18 + lib/libext2fs/source/ext2dir.c | 657 + lib/libext2fs/source/ext2dir.h | 69 + lib/libext2fs/source/ext2file.c | 413 + lib/libext2fs/source/ext2file.h | 60 + lib/libext2fs/source/ext2fs.h | 1572 + lib/libext2fs/source/ext2fsP.h | 143 + lib/libext2fs/source/ext3_extents.h | 109 + lib/libext2fs/source/ext_attr.c | 155 + lib/libext2fs/source/extent.c | 2007 + lib/libext2fs/source/fiemap.h | 68 + lib/libext2fs/source/fileio.c | 412 + lib/libext2fs/source/finddev.c | 208 + lib/libext2fs/source/flushb.c | 74 + lib/libext2fs/source/freefs.c | 112 + lib/libext2fs/source/gekko_io.c | 561 + lib/libext2fs/source/gekko_io.h | 53 + lib/libext2fs/source/gen_bitmap.c | 560 + lib/libext2fs/source/gen_bitmap64.c | 563 + lib/libext2fs/source/get_pathname.c | 160 + lib/libext2fs/source/getsectsize.c | 91 + lib/libext2fs/source/getsize.c | 311 + lib/libext2fs/source/i_block.c | 89 + lib/libext2fs/source/icount.c | 862 + lib/libext2fs/source/imager.c | 408 + lib/libext2fs/source/ind_block.c | 66 + lib/libext2fs/source/initialize.c | 447 + lib/libext2fs/source/inline.c | 32 + lib/libext2fs/source/inode.c | 834 + lib/libext2fs/source/inode_io.c | 291 + lib/libext2fs/source/io_manager.c | 112 + lib/libext2fs/source/irel.h | 114 + lib/libext2fs/source/irel_ma.c | 372 + lib/libext2fs/source/ismounted.c | 383 + lib/libext2fs/source/jfs_compat.h | 68 + lib/libext2fs/source/jfs_dat.h | 64 + lib/libext2fs/source/jfs_user.h | 8 + lib/libext2fs/source/kernel-jbd.h | 952 + lib/libext2fs/source/kernel-list.h | 112 + lib/libext2fs/source/link.c | 153 + lib/libext2fs/source/llseek.c | 139 + lib/libext2fs/source/lookup.c | 69 + lib/libext2fs/source/mem_allocate.h | 30 + lib/libext2fs/source/mkdir.c | 142 + lib/libext2fs/source/mkjournal.c | 601 + lib/libext2fs/source/namei.c | 206 + lib/libext2fs/source/native.c | 27 + lib/libext2fs/source/newdir.c | 77 + lib/libext2fs/source/openfs.c | 411 + lib/libext2fs/source/partitions.h | 49 + lib/libext2fs/source/progress.c | 86 + lib/libext2fs/source/punch.c | 324 + lib/libext2fs/source/read_bb.c | 102 + lib/libext2fs/source/read_bb_file.c | 105 + lib/libext2fs/source/res_gdt.c | 220 + lib/libext2fs/source/rw_bitmaps.c | 351 + lib/libext2fs/source/sparse.c | 52 + lib/libext2fs/source/swapfs.c | 315 + lib/libext2fs/source/tdb.c | 4145 ++ lib/libext2fs/source/tdb.h | 215 + lib/libext2fs/source/unlink.c | 99 + lib/libext2fs/source/valid_blk.c | 55 + lib/libext2fs/source/version.c | 54 + lib/libext2fs/source/wii_release/alloc.d | 31 + lib/libext2fs/source/wii_release/alloc_sb.d | 31 + .../source/wii_release/alloc_stats.d | 31 + .../source/wii_release/alloc_tables.d | 34 + lib/libext2fs/source/wii_release/badblocks.d | 34 + lib/libext2fs/source/wii_release/bb_compat.d | 34 + lib/libext2fs/source/wii_release/bb_inode.d | 31 + lib/libext2fs/source/wii_release/bitmaps.d | 34 + lib/libext2fs/source/wii_release/bitops.d | 31 + .../source/wii_release/blkmap64_ba.d | 37 + lib/libext2fs/source/wii_release/blknum.d | 31 + lib/libext2fs/source/wii_release/block.d | 31 + lib/libext2fs/source/wii_release/bmap.d | 31 + lib/libext2fs/source/wii_release/bmove.d | 34 + lib/libext2fs/source/wii_release/brel_ma.d | 34 + lib/libext2fs/source/wii_release/check_desc.d | 31 + lib/libext2fs/source/wii_release/closefs.d | 34 + lib/libext2fs/source/wii_release/com_err.d | 187 + lib/libext2fs/source/wii_release/crc16.d | 7 + lib/libext2fs/source/wii_release/csum.d | 34 + lib/libext2fs/source/wii_release/dblist.d | 34 + lib/libext2fs/source/wii_release/dblist_dir.d | 34 + .../source/wii_release/dir_iterate.d | 34 + lib/libext2fs/source/wii_release/dirblock.d | 31 + lib/libext2fs/source/wii_release/dirhash.d | 31 + lib/libext2fs/source/wii_release/disc_cache.d | 169 + lib/libext2fs/source/wii_release/dupfs.d | 34 + lib/libext2fs/source/wii_release/expanddir.d | 31 + lib/libext2fs/source/wii_release/ext2.d | 196 + lib/libext2fs/source/wii_release/ext2_frag.d | 190 + .../source/wii_release/ext2_internal.d | 199 + lib/libext2fs/source/wii_release/ext2dir.d | 190 + lib/libext2fs/source/wii_release/ext2file.d | 190 + lib/libext2fs/source/wii_release/ext_attr.d | 31 + lib/libext2fs/source/wii_release/extent.d | 37 + lib/libext2fs/source/wii_release/fileio.d | 31 + lib/libext2fs/source/wii_release/finddev.d | 31 + lib/libext2fs/source/wii_release/flushb.d | 31 + lib/libext2fs/source/wii_release/freefs.d | 34 + lib/libext2fs/source/wii_release/gekko_io.d | 193 + lib/libext2fs/source/wii_release/gen_bitmap.d | 34 + .../source/wii_release/gen_bitmap64.d | 37 + .../source/wii_release/get_pathname.d | 31 + .../source/wii_release/getsectsize.d | 31 + lib/libext2fs/source/wii_release/getsize.d | 31 + lib/libext2fs/source/wii_release/i_block.d | 31 + lib/libext2fs/source/wii_release/icount.d | 34 + lib/libext2fs/source/wii_release/imager.d | 31 + lib/libext2fs/source/wii_release/ind_block.d | 31 + lib/libext2fs/source/wii_release/initialize.d | 31 + lib/libext2fs/source/wii_release/inline.d | 31 + lib/libext2fs/source/wii_release/inode.d | 37 + lib/libext2fs/source/wii_release/inode_io.d | 31 + lib/libext2fs/source/wii_release/io_manager.d | 31 + lib/libext2fs/source/wii_release/irel_ma.d | 34 + lib/libext2fs/source/wii_release/ismounted.d | 31 + lib/libext2fs/source/wii_release/link.d | 31 + lib/libext2fs/source/wii_release/llseek.d | 31 + lib/libext2fs/source/wii_release/lookup.d | 31 + lib/libext2fs/source/wii_release/mkdir.d | 31 + lib/libext2fs/source/wii_release/mkjournal.d | 49 + lib/libext2fs/source/wii_release/namei.d | 31 + lib/libext2fs/source/wii_release/native.d | 31 + lib/libext2fs/source/wii_release/newdir.d | 31 + lib/libext2fs/source/wii_release/openfs.d | 34 + lib/libext2fs/source/wii_release/progress.d | 34 + lib/libext2fs/source/wii_release/punch.d | 31 + lib/libext2fs/source/wii_release/read_bb.d | 31 + .../source/wii_release/read_bb_file.d | 31 + lib/libext2fs/source/wii_release/res_gdt.d | 31 + lib/libext2fs/source/wii_release/rw_bitmaps.d | 34 + lib/libext2fs/source/wii_release/sparse.d | 34 + lib/libext2fs/source/wii_release/swapfs.d | 31 + lib/libext2fs/source/wii_release/tdb.d | 4 + lib/libext2fs/source/wii_release/unlink.d | 31 + lib/libext2fs/source/wii_release/valid_blk.d | 31 + lib/libext2fs/source/wii_release/version.d | 31 + .../source/wii_release/write_bb_file.d | 31 + lib/libext2fs/source/write_bb_file.c | 34 + lib/libfat/include/fat.h | 104 + lib/libfat/include/libfatversion.h | 10 + lib/libfat/src/Makefile | 118 + lib/libfat/src/gba/Makefile | 160 + lib/libfat/src/gba/include/fat.h | 81 + lib/libfat/src/include/fat.h | 104 + lib/libfat/src/include/libfatversion.h | 10 + lib/libfat/src/libogc/Makefile | 132 + lib/libfat/src/libogc/include/fat.h | 86 + lib/libfat/src/libogc/wii_release/cache.d | 181 + lib/libfat/src/libogc/wii_release/directory.d | 185 + lib/libfat/src/libogc/wii_release/disc.d | 172 + lib/libfat/src/libogc/wii_release/fat_frag.d | 187 + lib/libfat/src/libogc/wii_release/fatdir.d | 187 + lib/libfat/src/libogc/wii_release/fatfile.d | 187 + .../wii_release/file_allocation_table.d | 179 + lib/libfat/src/libogc/wii_release/filetime.d | 163 + lib/libfat/src/libogc/wii_release/libfat.d | 184 + lib/libfat/src/libogc/wii_release/lock.d | 160 + lib/libfat/src/libogc/wii_release/partition.d | 188 + lib/libfat/src/nds/Makefile | 131 + lib/libfat/src/nds/include/fat.h | 86 + lib/libfat/src/source/bit_ops.h | 57 + lib/libfat/src/source/cache.c | 323 + lib/libfat/src/source/cache.h | 128 + lib/libfat/src/source/common.h | 78 + lib/libfat/src/source/directory.c | 1126 + lib/libfat/src/source/directory.h | 181 + lib/libfat/src/source/disc.c | 111 + lib/libfat/src/source/disc.h | 110 + lib/libfat/src/source/fat_frag.c | 96 + lib/libfat/src/source/fatdir.c | 619 + lib/libfat/src/source/fatdir.h | 73 + lib/libfat/src/source/fatfile.c | 1133 + lib/libfat/src/source/fatfile.h | 105 + lib/libfat/src/source/file_allocation_table.c | 393 + lib/libfat/src/source/file_allocation_table.h | 70 + lib/libfat/src/source/filetime.c | 107 + lib/libfat/src/source/filetime.h | 41 + lib/libfat/src/source/libfat.c | 249 + lib/libfat/src/source/lock.c | 29 + lib/libfat/src/source/lock.h | 72 + lib/libfat/src/source/mem_allocate.h | 62 + lib/libfat/src/source/partition.c | 435 + lib/libfat/src/source/partition.h | 107 + lib/libntfs/include/ntfs.h | 148 + lib/libntfs/include/ntfs_frag.h | 15 + lib/libntfs/orig/AUTHORS | 27 + lib/libntfs/orig/COPYING | 340 + lib/libntfs/orig/CREDITS | 50 + lib/libntfs/orig/LICENSE | 340 + lib/libntfs/orig/Makefile | 31 + lib/libntfs/orig/READMII | 54 + lib/libntfs/orig/example/Makefile | 139 + lib/libntfs/orig/example/data/ehcmodule.elf | Bin 0 -> 24620 bytes .../orig/example/source/ehcmodule_elf.h | 3 + lib/libntfs/orig/example/source/main.c | 229 + lib/libntfs/orig/example/source/mload.c | 399 + lib/libntfs/orig/example/source/mload.h | 194 + lib/libntfs/orig/include/ntfs.h | 148 + lib/libntfs/orig/source/Makefile | 130 + lib/libntfs/orig/source/acls.c | 4266 ++ lib/libntfs/orig/source/acls.h | 199 + lib/libntfs/orig/source/attrib.c | 6773 +++ lib/libntfs/orig/source/attrib.h | 394 + lib/libntfs/orig/source/attrlist.c | 314 + lib/libntfs/orig/source/attrlist.h | 51 + lib/libntfs/orig/source/bit_ops.h | 57 + lib/libntfs/orig/source/bitmap.c | 300 + lib/libntfs/orig/source/bitmap.h | 96 + lib/libntfs/orig/source/bootsect.c | 285 + lib/libntfs/orig/source/bootsect.h | 42 + lib/libntfs/orig/source/cache.c | 609 + lib/libntfs/orig/source/cache.h | 118 + lib/libntfs/orig/source/cache2.c | 374 + lib/libntfs/orig/source/cache2.h | 135 + lib/libntfs/orig/source/collate.c | 271 + lib/libntfs/orig/source/collate.h | 34 + lib/libntfs/orig/source/compat.c | 250 + lib/libntfs/orig/source/compat.h | 86 + lib/libntfs/orig/source/compress.c | 1836 + lib/libntfs/orig/source/compress.h | 41 + lib/libntfs/orig/source/config.h | 388 + lib/libntfs/orig/source/debug.c | 79 + lib/libntfs/orig/source/debug.h | 47 + lib/libntfs/orig/source/device.c | 753 + lib/libntfs/orig/source/device.h | 134 + lib/libntfs/orig/source/device_io.c | 40 + lib/libntfs/orig/source/device_io.h | 82 + lib/libntfs/orig/source/dir.c | 2661 ++ lib/libntfs/orig/source/dir.h | 128 + lib/libntfs/orig/source/efs.c | 439 + lib/libntfs/orig/source/efs.h | 30 + lib/libntfs/orig/source/endians.h | 203 + lib/libntfs/orig/source/gekko_io.c | 658 + lib/libntfs/orig/source/gekko_io.h | 58 + lib/libntfs/orig/source/index.c | 2085 + lib/libntfs/orig/source/index.h | 167 + lib/libntfs/orig/source/inode.c | 1575 + lib/libntfs/orig/source/inode.h | 225 + lib/libntfs/orig/source/layout.h | 2662 ++ lib/libntfs/orig/source/lcnalloc.c | 771 + lib/libntfs/orig/source/lcnalloc.h | 51 + lib/libntfs/orig/source/logfile.c | 738 + lib/libntfs/orig/source/logfile.h | 394 + lib/libntfs/orig/source/logging.c | 637 + lib/libntfs/orig/source/logging.h | 121 + lib/libntfs/orig/source/mem_allocate.h | 43 + lib/libntfs/orig/source/mft.c | 1909 + lib/libntfs/orig/source/mft.h | 132 + lib/libntfs/orig/source/misc.c | 61 + lib/libntfs/orig/source/misc.h | 30 + lib/libntfs/orig/source/mst.c | 231 + lib/libntfs/orig/source/mst.h | 34 + lib/libntfs/orig/source/ntfs.c | 662 + lib/libntfs/orig/source/ntfsdir.c | 636 + lib/libntfs/orig/source/ntfsdir.h | 68 + lib/libntfs/orig/source/ntfsfile.c | 526 + lib/libntfs/orig/source/ntfsfile.h | 65 + lib/libntfs/orig/source/ntfsinternal.c | 868 + lib/libntfs/orig/source/ntfsinternal.h | 178 + lib/libntfs/orig/source/ntfstime.h | 121 + lib/libntfs/orig/source/object_id.c | 641 + lib/libntfs/orig/source/object_id.h | 35 + lib/libntfs/orig/source/param.h | 101 + lib/libntfs/orig/source/reparse.c | 1227 + lib/libntfs/orig/source/reparse.h | 39 + lib/libntfs/orig/source/runlist.c | 2171 + lib/libntfs/orig/source/runlist.h | 90 + lib/libntfs/orig/source/security.c | 5099 +++ lib/libntfs/orig/source/security.h | 361 + lib/libntfs/orig/source/support.h | 85 + lib/libntfs/orig/source/types.h | 141 + lib/libntfs/orig/source/unistr.c | 1521 + lib/libntfs/orig/source/unistr.h | 119 + lib/libntfs/orig/source/volume.c | 1732 + lib/libntfs/orig/source/volume.h | 307 + lib/libntfs/orig/source/xattrs.c | 791 + lib/libntfs/orig/source/xattrs.h | 75 + lib/libntfs/src/AUTHORS | 32 + lib/libntfs/src/CREDITS | 50 + lib/libntfs/src/LICENSE | 340 + lib/libntfs/src/Makefile | 35 + lib/libntfs/src/READMII | 54 + lib/libntfs/src/include/ntfs.h | 148 + lib/libntfs/src/include/ntfs_frag.h | 15 + lib/libntfs/src/source/Makefile | 143 + lib/libntfs/src/source/acls.c | 4256 ++ lib/libntfs/src/source/acls.h | 199 + lib/libntfs/src/source/attrib.c | 6794 +++ lib/libntfs/src/source/attrib.h | 394 + lib/libntfs/src/source/attrib_frag.c | 369 + lib/libntfs/src/source/attrlist.c | 314 + lib/libntfs/src/source/attrlist.h | 51 + lib/libntfs/src/source/bit_ops.h | 57 + lib/libntfs/src/source/bitmap.c | 300 + lib/libntfs/src/source/bitmap.h | 96 + lib/libntfs/src/source/bootsect.c | 285 + lib/libntfs/src/source/bootsect.h | 42 + lib/libntfs/src/source/cache.c | 606 + lib/libntfs/src/source/cache.h | 118 + lib/libntfs/src/source/cache2.c | 374 + lib/libntfs/src/source/cache2.h | 135 + lib/libntfs/src/source/collate.c | 271 + lib/libntfs/src/source/collate.h | 34 + lib/libntfs/src/source/compat.c | 250 + lib/libntfs/src/source/compat.h | 86 + lib/libntfs/src/source/compress.c | 1790 + lib/libntfs/src/source/compress.h | 41 + lib/libntfs/src/source/config.h | 394 + lib/libntfs/src/source/debug.c | 79 + lib/libntfs/src/source/debug.h | 47 + lib/libntfs/src/source/device.c | 808 + lib/libntfs/src/source/device.h | 134 + lib/libntfs/src/source/device_io.c | 40 + lib/libntfs/src/source/device_io.h | 82 + lib/libntfs/src/source/dir.c | 2657 ++ lib/libntfs/src/source/dir.h | 128 + lib/libntfs/src/source/efs.c | 437 + lib/libntfs/src/source/efs.h | 30 + lib/libntfs/src/source/endians.h | 203 + lib/libntfs/src/source/gekko_io.c | 658 + lib/libntfs/src/source/gekko_io.h | 58 + lib/libntfs/src/source/index.c | 2085 + lib/libntfs/src/source/index.h | 167 + lib/libntfs/src/source/inode.c | 1608 + lib/libntfs/src/source/inode.h | 225 + lib/libntfs/src/source/layout.h | 2662 ++ lib/libntfs/src/source/lcnalloc.c | 771 + lib/libntfs/src/source/lcnalloc.h | 51 + lib/libntfs/src/source/logfile.c | 737 + lib/libntfs/src/source/logfile.h | 394 + lib/libntfs/src/source/logging.c | 637 + lib/libntfs/src/source/logging.h | 121 + lib/libntfs/src/source/mem_allocate.h | 43 + lib/libntfs/src/source/mft.c | 1909 + lib/libntfs/src/source/mft.h | 132 + lib/libntfs/src/source/misc.c | 61 + lib/libntfs/src/source/misc.h | 30 + lib/libntfs/src/source/mst.c | 247 + lib/libntfs/src/source/mst.h | 37 + lib/libntfs/src/source/ntfs.c | 659 + lib/libntfs/src/source/ntfsdir.c | 636 + lib/libntfs/src/source/ntfsdir.h | 68 + lib/libntfs/src/source/ntfsfile.c | 526 + lib/libntfs/src/source/ntfsfile.h | 65 + lib/libntfs/src/source/ntfsfile_frag.c | 121 + lib/libntfs/src/source/ntfsinternal.c | 868 + lib/libntfs/src/source/ntfsinternal.h | 178 + lib/libntfs/src/source/ntfstime.h | 133 + lib/libntfs/src/source/object_id.c | 639 + lib/libntfs/src/source/object_id.h | 35 + lib/libntfs/src/source/param.h | 119 + lib/libntfs/src/source/realpath.c | 103 + lib/libntfs/src/source/realpath.h | 24 + lib/libntfs/src/source/reparse.c | 1227 + lib/libntfs/src/source/reparse.h | 39 + lib/libntfs/src/source/runlist.c | 2174 + lib/libntfs/src/source/runlist.h | 90 + lib/libntfs/src/source/security.c | 5093 +++ lib/libntfs/src/source/security.h | 361 + lib/libntfs/src/source/support.h | 85 + lib/libntfs/src/source/types.h | 141 + lib/libntfs/src/source/unistr.c | 1523 + lib/libntfs/src/source/unistr.h | 119 + lib/libntfs/src/source/volume.c | 1844 + lib/libntfs/src/source/volume.h | 313 + lib/libntfs/src/source/wii_release/acls.d | 76 + lib/libntfs/src/source/wii_release/attrib.d | 88 + .../src/source/wii_release/attrib_frag.d | 244 + lib/libntfs/src/source/wii_release/attrlist.d | 70 + lib/libntfs/src/source/wii_release/bitmap.d | 70 + lib/libntfs/src/source/wii_release/bootsect.d | 67 + lib/libntfs/src/source/wii_release/cache.d | 76 + lib/libntfs/src/source/wii_release/cache2.d | 169 + lib/libntfs/src/source/wii_release/collate.d | 67 + lib/libntfs/src/source/wii_release/compat.d | 10 + lib/libntfs/src/source/wii_release/compress.d | 73 + lib/libntfs/src/source/wii_release/debug.d | 64 + lib/libntfs/src/source/wii_release/device.d | 70 + .../src/source/wii_release/device_io.d | 4 + lib/libntfs/src/source/wii_release/dir.d | 85 + lib/libntfs/src/source/wii_release/efs.d | 73 + lib/libntfs/src/source/wii_release/gekko_io.d | 229 + lib/libntfs/src/source/wii_release/index.d | 82 + lib/libntfs/src/source/wii_release/inode.d | 79 + lib/libntfs/src/source/wii_release/lcnalloc.d | 73 + lib/libntfs/src/source/wii_release/logfile.d | 73 + lib/libntfs/src/source/wii_release/logging.d | 22 + lib/libntfs/src/source/wii_release/mft.d | 73 + lib/libntfs/src/source/wii_release/misc.d | 22 + lib/libntfs/src/source/wii_release/mst.d | 67 + lib/libntfs/src/source/wii_release/ntfs.d | 247 + lib/libntfs/src/source/wii_release/ntfsdir.d | 232 + lib/libntfs/src/source/wii_release/ntfsfile.d | 232 + .../src/source/wii_release/ntfsfile_frag.d | 239 + .../src/source/wii_release/ntfsinternal.d | 244 + .../src/source/wii_release/object_id.d | 76 + lib/libntfs/src/source/wii_release/realpath.d | 10 + lib/libntfs/src/source/wii_release/reparse.d | 76 + lib/libntfs/src/source/wii_release/runlist.d | 67 + lib/libntfs/src/source/wii_release/security.d | 82 + lib/libntfs/src/source/wii_release/unistr.d | 67 + lib/libntfs/src/source/wii_release/volume.d | 82 + lib/libntfs/src/source/wii_release/xattrs.d | 4 + lib/libntfs/src/source/xattrs.c | 791 + lib/libntfs/src/source/xattrs.h | 75 + lib/png-1.2.34/include/png.h | 3661 ++ lib/png-1.2.34/include/pngconf.h | 1487 + lib/png/include/png.h | 2699 ++ lib/png/include/pngconf.h | 1540 + lib/png/src/Makefile | 52 + lib/png/src/Makefile-orig | 40 + lib/png/src/png.c | 918 + lib/png/src/png.d | 12 + lib/png/src/png.h | 2699 ++ lib/png/src/pngconf.h | 1540 + lib/png/src/pngerror.c | 402 + lib/png/src/pngerror.d | 12 + lib/png/src/pngget.c | 925 + lib/png/src/pngget.d | 12 + lib/png/src/pngmem.c | 611 + lib/png/src/pngmem.d | 12 + lib/png/src/pngpread.c | 1765 + lib/png/src/pngpread.d | 12 + lib/png/src/pngpriv.h | 954 + lib/png/src/pngread.c | 1361 + lib/png/src/pngread.d | 12 + lib/png/src/pngrio.c | 163 + lib/png/src/pngrio.d | 12 + lib/png/src/pngrtran.c | 4203 ++ lib/png/src/pngrtran.d | 12 + lib/png/src/pngrutil.c | 3379 ++ lib/png/src/pngrutil.d | 12 + lib/png/src/pngset.c | 1167 + lib/png/src/pngset.d | 12 + lib/png/src/pngtrans.c | 677 + lib/png/src/pngtrans.d | 12 + lib/png/src/pngwio.c | 241 + lib/png/src/pngwio.d | 12 + lib/png/src/pngwrite.c | 1457 + lib/png/src/pngwrite.d | 12 + lib/png/src/pngwtran.c | 566 + lib/png/src/pngwtran.d | 12 + lib/png/src/pngwutil.c | 2786 ++ lib/png/src/pngwutil.d | 12 + lib/readme.txt | 19 + lib/zlib/include/zconf.h | 428 + lib/zlib/include/zlib.h | 1613 + lib/zlib/src/Makefile | 50 + lib/zlib/src/Makefile-orig | 40 + lib/zlib/src/adler32.c | 169 + lib/zlib/src/adler32.d | 7 + lib/zlib/src/compress.c | 80 + lib/zlib/src/compress.d | 5 + lib/zlib/src/crc32.c | 442 + lib/zlib/src/crc32.d | 9 + lib/zlib/src/crc32.h | 441 + lib/zlib/src/deflate.c | 1834 + lib/zlib/src/deflate.d | 9 + lib/zlib/src/deflate.h | 342 + lib/zlib/src/gzclose.c | 25 + lib/zlib/src/gzclose.d | 7 + lib/zlib/src/gzguts.h | 132 + lib/zlib/src/gzlib.c | 537 + lib/zlib/src/gzlib.d | 7 + lib/zlib/src/gzread.c | 653 + lib/zlib/src/gzread.d | 7 + lib/zlib/src/gzwrite.c | 531 + lib/zlib/src/gzwrite.d | 7 + lib/zlib/src/infback.c | 632 + lib/zlib/src/infback.d | 16 + lib/zlib/src/inffast.c | 340 + lib/zlib/src/inffast.d | 14 + lib/zlib/src/inffast.h | 11 + lib/zlib/src/inffixed.h | 94 + lib/zlib/src/inflate.c | 1480 + lib/zlib/src/inflate.d | 16 + lib/zlib/src/inflate.h | 122 + lib/zlib/src/inftrees.c | 330 + lib/zlib/src/inftrees.d | 9 + lib/zlib/src/inftrees.h | 62 + lib/zlib/src/trees.c | 1244 + lib/zlib/src/trees.d | 11 + lib/zlib/src/trees.h | 128 + lib/zlib/src/uncompr.c | 59 + lib/zlib/src/uncompr.d | 5 + lib/zlib/src/zconf.h | 428 + lib/zlib/src/zlib.h | 1613 + lib/zlib/src/zutil.c | 318 + lib/zlib/src/zutil.d | 7 + lib/zlib/src/zutil.h | 274 + plugins/mighty/Channel Emu Plugin.pnproj | 1 + plugins/mighty/Channel Emu Plugin.pnps | 1 + plugins/mighty/Makefile | 132 + plugins/mighty/source/codes/codehandler.h | 277 + plugins/mighty/source/codes/codehandleronly.h | 180 + .../mighty/source/codes/codehandlerslota.h | 277 + plugins/mighty/source/codes/codes.c | 313 + plugins/mighty/source/codes/codes.h | 7 + plugins/mighty/source/codes/multidol.c | 36 + plugins/mighty/source/codes/multidol.h | 14 + plugins/mighty/source/codes/patchcode.c | 602 + plugins/mighty/source/codes/patchcode.h | 25 + plugins/mighty/source/codes/patchhook.S | 505 + plugins/mighty/source/config.h | 27 + plugins/mighty/source/fat_mine.c | 156 + plugins/mighty/source/fat_mine.h | 15 + plugins/mighty/source/lz77.c | 219 + plugins/mighty/source/lz77.h | 26 + plugins/mighty/source/menu.c | 808 + plugins/mighty/source/menu.h | 36 + plugins/mighty/source/mighty_channels.c | 73 + plugins/mighty/source/nand.c | 222 + plugins/mighty/source/nand.h | 35 + plugins/mighty/source/patch.c | 421 + plugins/mighty/source/patch.h | 26 + plugins/mighty/source/sonido.c | 671 + plugins/mighty/source/sonido.h | 32 + plugins/mighty/source/stub.s | 18 + plugins/mighty/source/tools.c | 323 + plugins/mighty/source/tools.h | 37 + plugins/mighty/source/usbstorage.c | 291 + plugins/mighty/source/usbstorage.h | 19 + plugins/mighty/source/video.c | 92 + plugins/mighty/source/video.h | 18 + source/GRRLIB.c | 1600 + source/Makefile | 7 + source/NintendontConfig.h | 85 + source/RuntimeIOSPatch.c | 86 + source/RuntimeIOSPatch.h | 11 + source/apploader.c | 1290 + source/apploader.h | 12 + source/banner.c | 674 + source/cache.c | 2001 + source/cache.h | 56 + source/cfg.c | 3983 ++ source/cfg.h | 731 + source/cfgutil.c | 308 + source/cfgutil.h | 42 + source/channel.c | 366 + source/channel.h | 9 + source/cheats.c | 633 + source/codehandler.h | 277 + source/codehandleronly.h | 180 + source/confont512.c | 9221 ++++ source/console.c | 1197 + source/console.h | 51 + source/coverflow.c | 2933 ++ source/coverflow.h | 88 + source/debug.c | 352 + source/debug.h | 91 + source/dir.c | 98 + source/dir.h | 39 + source/disc.c | 820 + source/disc.h | 104 + source/dns.c | 131 + source/dns.h | 14 + source/dol.c | 126 + source/dol.h | 18 + source/dolmenu.c | 198 + source/dolmenu.h | 16 + source/dvd_broadway.c | 614 + source/dvd_broadway.h | 53 + source/fat.c | 751 + source/fat.h | 82 + source/fileOps.c | 485 + source/fileOps.h | 17 + source/frag.c | 385 + source/frag.h | 44 + source/fst.c | 939 + source/fst.h | 31 + source/fwrite_patch.h | 15 + source/gc.c | 740 + source/gc.h | 93 + source/gcmodplay.c | 376 + source/geckomenu.h | 60 + source/gettext.c | 304 + source/gettext.h | 26 + source/grid.c | 1379 + source/grid.h | 19 + source/gui.c | 3294 ++ source/gui.h | 121 + source/guimenu.c | 3240 ++ source/guimenu.h | 7 + source/http.c | 363 + source/http.h | 33 + source/libwbfs/libwbfs.c | 799 + source/libwbfs/libwbfs.h | 235 + source/libwbfs/libwbfs_os.h | 46 + source/libwbfs/rijndael.c | 398 + source/libwbfs/wiidisc.c | 398 + source/libwbfs/wiidisc.h | 73 + source/lz77.c | 264 + source/lz77.h | 25 + source/mem.c | 843 + source/mem.h | 132 + source/memcheck.c | 278 + source/memcheck.h | 42 + source/menu.c | 5662 +++ source/menu.h | 99 + source/mload.c | 544 + source/mload.h | 230 + source/mload_modules.c | 513 + source/mload_modules.h | 35 + source/mp3player.c | 485 + source/multidol.c | 36 + source/multidol.h | 14 + source/music.c | 460 + source/music.h | 11 + source/my_GRRLIB.h | 73 + source/nand.c | 572 + source/nand.h | 65 + source/net.c | 1219 + source/net.h | 17 + source/partition.c | 508 + source/partition.h | 77 + source/patchcode.c | 538 + source/patchcode.h | 39 + source/patchhook.S | 505 + source/playlog.c | 166 + source/playlog.h | 27 + source/pngu/orig/pngu.c | 1132 + source/pngu/orig/pngu.h | 171 + source/pngu/pngu.c | 1561 + source/pngu/pngu.h | 181 + source/pngu/pngu_grrlib.c | 302 + source/pngu/pngu_impl.h | 42 + source/ppc.h | 83 + source/restart.c | 34 + source/restart.h | 8 + source/savegame.c | 183 + source/savegame.h | 10 + source/sdhc.c | 274 + source/sdhc.h | 20 + source/sha1.c | 172 + source/sha1.h | 12 + source/sort.c | 1914 + source/sort.h | 108 + source/splits.c | 357 + source/splits.h | 41 + source/strutil.c | 231 + source/strutil.h | 35 + source/stub.S | 151 + source/subsystem.c | 43 + source/subsystem.h | 9 + source/sys.c | 2161 + source/sys.h | 54 + source/unicmap.c | 217 + source/unzip/crypt.h | 132 + source/unzip/ioapi.c | 177 + source/unzip/ioapi.h | 75 + source/unzip/miniunz.c | 276 + source/unzip/miniunz.h | 17 + source/unzip/mztools.c | 281 + source/unzip/mztools.h | 31 + source/unzip/unzip.c | 1607 + source/unzip/unzip.h | 354 + source/update.c | 1105 + source/usb-loader.c | 262 + source/usbstorage.c | 506 + source/usbstorage.h | 22 + source/util.c | 648 + source/util.h | 148 + source/utils.c | 8 + source/utils.h | 15 + source/version.h | 39 + source/video.c | 449 + source/video.h | 45 + source/wbfs.c | 1024 + source/wbfs.h | 67 + source/wbfs_fat.c | 936 + source/wbfs_fat.h | 22 + source/wdvd.c | 576 + source/wdvd.h | 37 + source/wgui.c | 2885 ++ source/wgui.h | 193 + source/wgui_f.h | 199 + source/wiip.c | 119 + source/wiip.h | 8 + source/wpad.c | 561 + source/wpad.h | 59 + source/xml.c | 1216 + source/xml.h | 67 + tools/gettext/libexpat.dll | Bin 0 -> 143360 bytes tools/gettext/libgettextlib.dll | Bin 0 -> 182784 bytes tools/gettext/libgettextpo.dll | Bin 0 -> 18944 bytes tools/gettext/libgettextsrc.dll | Bin 0 -> 275456 bytes tools/gettext/libiconv2.dll | Bin 0 -> 978432 bytes tools/gettext/libintl3.dll | Bin 0 -> 103424 bytes tools/gettext/msgmerge.exe | Bin 0 -> 37888 bytes tools/gettext/xgettext.exe | Bin 0 -> 481792 bytes tools/googlecode_upload.py | 248 + tools/hex2bin.c | 102 + tools/unifont.dat | Bin 0 -> 1320548 bytes tools/unifont.hex | 34869 ++++++++++++++++ updates.txt | 537 + usbloader.pnps | 1 + 1348 files changed, 483721 insertions(+) create mode 100644 LICENSE.txt create mode 100644 Languages/DE.lang create mode 100644 Languages/DK.lang create mode 100644 Languages/ES.lang create mode 100644 Languages/FI.lang create mode 100644 Languages/FR.lang create mode 100644 Languages/GR.lang create mode 100644 Languages/IT.lang create mode 100644 Languages/JA.lang create mode 100644 Languages/KO.lang create mode 100644 Languages/Makefile create mode 100644 Languages/NL.lang create mode 100644 Languages/NO.lang create mode 100644 Languages/PT_BR.lang create mode 100644 Languages/PT_PT.lang create mode 100644 Languages/TR.lang create mode 100644 Languages/ZH_CN-clamis.lang create mode 100644 Languages/ZH_CN.lang create mode 100644 Languages/ZH_TW.lang create mode 100644 Languages/add_bom.sh create mode 100644 Languages/del_bom.sh create mode 100644 Languages/lang.pot create mode 100644 Languages/misc.sh create mode 100644 Languages/msg_merge.sh create mode 100644 Languages/msg_miss.sh create mode 100644 Languages/msgreplace.pl create mode 100644 Languages/msgreplace.sh create mode 100644 Makefile create mode 100644 README-CFG.txt create mode 100644 USBLoader.pnproj create mode 100644 cios/dip_frag/LICENSE.txt create mode 100644 cios/dip_frag/Makefile create mode 100644 cios/dip_frag/dip.c create mode 100644 cios/dip_frag/dip.h create mode 100644 cios/dip_frag/dip_calls.h create mode 100644 cios/dip_frag/dip_calls.s create mode 100644 cios/dip_frag/dma.c create mode 100644 cios/dip_frag/dma.h create mode 100644 cios/dip_frag/errno.h create mode 100644 cios/dip_frag/file.c create mode 100644 cios/dip_frag/file.h create mode 100644 cios/dip_frag/frag.c create mode 100644 cios/dip_frag/frag.h create mode 100644 cios/dip_frag/ioctl.h create mode 100644 cios/dip_frag/ipc.c create mode 100644 cios/dip_frag/ipc.h create mode 100644 cios/dip_frag/link.ld create mode 100644 cios/dip_frag/main.c create mode 100644 cios/dip_frag/main.h create mode 100644 cios/dip_frag/patches.c create mode 100644 cios/dip_frag/patches.h create mode 100644 cios/dip_frag/plugin.c create mode 100644 cios/dip_frag/plugin.h create mode 100644 cios/dip_frag/sdhc.c create mode 100644 cios/dip_frag/sdhc.h create mode 100644 cios/dip_frag/start.s create mode 100644 cios/dip_frag/swi_mload.c create mode 100644 cios/dip_frag/swi_mload.h create mode 100644 cios/dip_frag/syscalls.h create mode 100644 cios/dip_frag/syscalls.s create mode 100644 cios/dip_frag/tools.h create mode 100644 cios/dip_frag/tools.s create mode 100644 cios/dip_frag/types.h create mode 100644 cios/dip_frag/usbstorage.c create mode 100644 cios/dip_frag/usbstorage.h create mode 100644 cios/dip_frag/wbfs.c create mode 100644 cios/dip_frag/wbfs.h create mode 100644 cios/dip_orig/LICENSE.txt create mode 100644 cios/dip_orig/Makefile create mode 100644 cios/dip_orig/dip.c create mode 100644 cios/dip_orig/dip.h create mode 100644 cios/dip_orig/dip_calls.h create mode 100644 cios/dip_orig/dip_calls.s create mode 100644 cios/dip_orig/dma.c create mode 100644 cios/dip_orig/dma.h create mode 100644 cios/dip_orig/errno.h create mode 100644 cios/dip_orig/file.c create mode 100644 cios/dip_orig/file.h create mode 100644 cios/dip_orig/ioctl.h create mode 100644 cios/dip_orig/ipc.c create mode 100644 cios/dip_orig/ipc.h create mode 100644 cios/dip_orig/link.ld create mode 100644 cios/dip_orig/main.c create mode 100644 cios/dip_orig/main.h create mode 100644 cios/dip_orig/patches.c create mode 100644 cios/dip_orig/patches.h create mode 100644 cios/dip_orig/plugin.c create mode 100644 cios/dip_orig/plugin.h create mode 100644 cios/dip_orig/start.s create mode 100644 cios/dip_orig/swi_mload.c create mode 100644 cios/dip_orig/swi_mload.h create mode 100644 cios/dip_orig/syscalls.h create mode 100644 cios/dip_orig/syscalls.s create mode 100644 cios/dip_orig/tools.h create mode 100644 cios/dip_orig/tools.s create mode 100644 cios/dip_orig/types.h create mode 100644 cios/dip_orig/wbfs.c create mode 100644 cios/dip_orig/wbfs.h create mode 100644 cios/odip_frag/.gitignore create mode 100644 cios/odip_frag/COPYING create mode 100644 cios/odip_frag/LEEME.txt create mode 100644 cios/odip_frag/MakeIt.bat create mode 100644 cios/odip_frag/Makefile create mode 100644 cios/odip_frag/bin/odip_frag.bin create mode 100644 cios/odip_frag/include/debug.h create mode 100644 cios/odip_frag/include/di.h create mode 100644 cios/odip_frag/include/es.h create mode 100644 cios/odip_frag/include/ipc.h create mode 100644 cios/odip_frag/include/module.h create mode 100644 cios/odip_frag/include/syscalls.h create mode 100644 cios/odip_frag/include/types.h create mode 100644 cios/odip_frag/include/utils.h create mode 100644 cios/odip_frag/include/wbfs.h create mode 100644 cios/odip_frag/scripts/link.ld create mode 100644 cios/odip_frag/scripts/nostart.specs create mode 100644 cios/odip_frag/source/ES_ioctlv_low.s create mode 100644 cios/odip_frag/source/crt0.s create mode 100644 cios/odip_frag/source/debug.c create mode 100644 cios/odip_frag/source/di.c create mode 100644 cios/odip_frag/source/es_ioctlv.c create mode 100644 cios/odip_frag/source/frag.c create mode 100644 cios/odip_frag/source/frag.h create mode 100644 cios/odip_frag/source/module.c create mode 100644 cios/odip_frag/source/sdhc.c create mode 100644 cios/odip_frag/source/sdhc.h create mode 100644 cios/odip_frag/source/syscalls.s create mode 100644 cios/odip_frag/source/usb.c create mode 100644 cios/odip_frag/source/usb.h create mode 100644 cios/odip_frag/source/usbstorage.c create mode 100644 cios/odip_frag/source/usbstorage.h create mode 100644 cios/odip_frag/source/utils.c create mode 100644 cios/odip_frag/source/wbfs.c create mode 100644 cios/odip_frag/source/wrappers.s create mode 100644 cios/odip_plugin/COPYING create mode 100644 cios/odip_plugin/LEEME.txt create mode 100644 cios/odip_plugin/MakeIt.bat create mode 100644 cios/odip_plugin/Makefile create mode 100644 cios/odip_plugin/bin/odip_plugin.bin create mode 100644 cios/odip_plugin/include/debug.h create mode 100644 cios/odip_plugin/include/di.h create mode 100644 cios/odip_plugin/include/es.h create mode 100644 cios/odip_plugin/include/ipc.h create mode 100644 cios/odip_plugin/include/module.h create mode 100644 cios/odip_plugin/include/syscalls.h create mode 100644 cios/odip_plugin/include/types.h create mode 100644 cios/odip_plugin/include/utils.h create mode 100644 cios/odip_plugin/include/wbfs.h create mode 100644 cios/odip_plugin/scripts/link.ld create mode 100644 cios/odip_plugin/scripts/nostart.specs create mode 100644 cios/odip_plugin/source/ES_ioctlv_low.s create mode 100644 cios/odip_plugin/source/crt0.s create mode 100644 cios/odip_plugin/source/debug.c create mode 100644 cios/odip_plugin/source/di.c create mode 100644 cios/odip_plugin/source/es_ioctlv.c create mode 100644 cios/odip_plugin/source/module.c create mode 100644 cios/odip_plugin/source/syscalls.s create mode 100644 cios/odip_plugin/source/utils.c create mode 100644 cios/odip_plugin/source/wbfs.c create mode 100644 cios/odip_plugin/source/wrappers.s create mode 100644 cios/readme.txt create mode 100644 cios/sdhc/LICENSE.txt create mode 100644 cios/sdhc/Makefile create mode 100644 cios/sdhc/es.c create mode 100644 cios/sdhc/es.h create mode 100644 cios/sdhc/ipc.c create mode 100644 cios/sdhc/ipc.h create mode 100644 cios/sdhc/libwbfs/libwbfs.c create mode 100644 cios/sdhc/libwbfs/libwbfs.h create mode 100644 cios/sdhc/libwbfs/rijndael.c create mode 100644 cios/sdhc/libwbfs/wiidisc.c create mode 100644 cios/sdhc/libwbfs/wiidisc.h create mode 100644 cios/sdhc/libwbfs_os.h create mode 100644 cios/sdhc/link.ld create mode 100644 cios/sdhc/main.c create mode 100644 cios/sdhc/mem.c create mode 100644 cios/sdhc/mem.h create mode 100644 cios/sdhc/module.h create mode 100644 cios/sdhc/sdio.c create mode 100644 cios/sdhc/sdio.h create mode 100644 cios/sdhc/start.s create mode 100644 cios/sdhc/syscalls.h create mode 100644 cios/sdhc/syscalls.s create mode 100644 cios/sdhc/timer.c create mode 100644 cios/sdhc/timer.h create mode 100644 cios/sdhc/types.h create mode 100644 cios/sdhc/wbfs.c create mode 100644 cios/sdhc/wbfs.h create mode 100644 cios/sdhc_orig/LICENSE.txt create mode 100644 cios/sdhc_orig/Makefile create mode 100644 cios/sdhc_orig/es.c create mode 100644 cios/sdhc_orig/es.h create mode 100644 cios/sdhc_orig/ipc.c create mode 100644 cios/sdhc_orig/ipc.h create mode 100644 cios/sdhc_orig/libwbfs/libwbfs.c create mode 100644 cios/sdhc_orig/libwbfs/libwbfs.h create mode 100644 cios/sdhc_orig/libwbfs/rijndael.c create mode 100644 cios/sdhc_orig/libwbfs/wiidisc.c create mode 100644 cios/sdhc_orig/libwbfs/wiidisc.h create mode 100644 cios/sdhc_orig/libwbfs_os.h create mode 100644 cios/sdhc_orig/link.ld create mode 100644 cios/sdhc_orig/main.c create mode 100644 cios/sdhc_orig/mem.c create mode 100644 cios/sdhc_orig/mem.h create mode 100644 cios/sdhc_orig/module.h create mode 100644 cios/sdhc_orig/sdio.c create mode 100644 cios/sdhc_orig/sdio.h create mode 100644 cios/sdhc_orig/start.s create mode 100644 cios/sdhc_orig/syscalls.h create mode 100644 cios/sdhc_orig/syscalls.s create mode 100644 cios/sdhc_orig/timer.c create mode 100644 cios/sdhc_orig/timer.h create mode 100644 cios/sdhc_orig/types.h create mode 100644 cios/sdhc_orig/wbfs.c create mode 100644 cios/sdhc_orig/wbfs.h create mode 100644 cios/stripios/MakeIt.bat create mode 100644 cios/stripios/Makefile create mode 100644 cios/stripios/main.cpp create mode 100644 cios/stripios/stripios.bin create mode 100644 cios/stripios/stripios.exe create mode 100644 data/background create mode 100644 data/bg_gui create mode 100644 data/button.png create mode 100644 data/button_old.png create mode 100644 data/cover_front create mode 100644 data/cover_side create mode 100644 data/cover_top create mode 100644 data/dip_frag_249_r21 create mode 100644 data/dip_plugin2_bin create mode 100644 data/dip_plugin3_bin create mode 100644 data/dip_plugin4_bin create mode 100644 data/dip_plugin4_bin_31 create mode 100644 data/dip_plugin_249 create mode 100644 data/ehcmodule2_elf create mode 100644 data/ehcmodule3_elf create mode 100644 data/ehcmodule4_elf create mode 100644 data/ehcmodule5_elf create mode 100644 data/ehcmodule_frag create mode 100644 data/gc.wav create mode 100644 data/gui_font create mode 100644 data/hourglass create mode 100644 data/intro create mode 100644 data/intro2 create mode 100644 data/intro3 create mode 100644 data/intro4.jpg create mode 100644 data/intro41 create mode 100644 data/nocover create mode 100644 data/nocover_full create mode 100644 data/odip_frag_bin create mode 100644 data/odip_plugin_bin create mode 100644 data/pointer create mode 100644 data/sdhc_module_elf create mode 100644 data/star create mode 100644 data/window.png create mode 100644 lib/Makefile create mode 100644 lib/grrlib/GRRLIB/GRRLIB_3D.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_bmf.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_bmfx.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_core.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_fbAdvanced.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_fileIO.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_gecko.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_print.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_render.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_snapshot.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_texEdit.c create mode 100644 lib/grrlib/GRRLIB/GRRLIB_ttf.c create mode 100644 lib/grrlib/GRRLIB/Makefile create mode 100644 lib/grrlib/GRRLIB/Makefile-orig create mode 100644 lib/grrlib/GRRLIB/grrlib.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB__inline.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB__lib.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_cExtn.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_clipping.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_collision.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_fbComplex.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_fbGX.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_fbSimple.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_handle.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_pixel.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_private.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_settings.h create mode 100644 lib/grrlib/GRRLIB/grrlib/GRRLIB_texSetup.h create mode 100644 lib/grrlib/include/grrlib.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB__inline.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB__lib.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_cExtn.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_clipping.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_collision.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_fbComplex.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_fbGX.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_fbSimple.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_handle.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_pixel.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_private.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_settings.h create mode 100644 lib/grrlib/include/grrlib/GRRLIB_texSetup.h create mode 100644 lib/jpeg/include/jconfig.h create mode 100644 lib/jpeg/include/jmorecfg.h create mode 100644 lib/jpeg/include/jpeglib.h create mode 100644 lib/jpeg/src/Makefile create mode 100644 lib/jpeg/src/Makefile-orig create mode 100644 lib/jpeg/src/cderror.h create mode 100644 lib/jpeg/src/cdjpeg.c create mode 100644 lib/jpeg/src/cdjpeg.d create mode 100644 lib/jpeg/src/cdjpeg.h create mode 100644 lib/jpeg/src/cjpeg.c create mode 100644 lib/jpeg/src/cjpeg.d create mode 100644 lib/jpeg/src/djpeg.c create mode 100644 lib/jpeg/src/djpeg.d create mode 100644 lib/jpeg/src/jaricom.c create mode 100644 lib/jpeg/src/jaricom.d create mode 100644 lib/jpeg/src/jcapimin.c create mode 100644 lib/jpeg/src/jcapimin.d create mode 100644 lib/jpeg/src/jcapistd.c create mode 100644 lib/jpeg/src/jcapistd.d create mode 100644 lib/jpeg/src/jcarith.c create mode 100644 lib/jpeg/src/jcarith.d create mode 100644 lib/jpeg/src/jccoefct.c create mode 100644 lib/jpeg/src/jccoefct.d create mode 100644 lib/jpeg/src/jccolor.c create mode 100644 lib/jpeg/src/jccolor.d create mode 100644 lib/jpeg/src/jcdctmgr.c create mode 100644 lib/jpeg/src/jcdctmgr.d create mode 100644 lib/jpeg/src/jchuff.c create mode 100644 lib/jpeg/src/jchuff.d create mode 100644 lib/jpeg/src/jcinit.c create mode 100644 lib/jpeg/src/jcinit.d create mode 100644 lib/jpeg/src/jcmainct.c create mode 100644 lib/jpeg/src/jcmainct.d create mode 100644 lib/jpeg/src/jcmarker.c create mode 100644 lib/jpeg/src/jcmarker.d create mode 100644 lib/jpeg/src/jcmaster.c create mode 100644 lib/jpeg/src/jcmaster.d create mode 100644 lib/jpeg/src/jcomapi.c create mode 100644 lib/jpeg/src/jcomapi.d create mode 100644 lib/jpeg/src/jconfig.h create mode 100644 lib/jpeg/src/jcparam.c create mode 100644 lib/jpeg/src/jcparam.d create mode 100644 lib/jpeg/src/jcprepct.c create mode 100644 lib/jpeg/src/jcprepct.d create mode 100644 lib/jpeg/src/jcsample.c create mode 100644 lib/jpeg/src/jcsample.d create mode 100644 lib/jpeg/src/jctrans.c create mode 100644 lib/jpeg/src/jctrans.d create mode 100644 lib/jpeg/src/jdapimin.c create mode 100644 lib/jpeg/src/jdapimin.d create mode 100644 lib/jpeg/src/jdapistd.c create mode 100644 lib/jpeg/src/jdapistd.d create mode 100644 lib/jpeg/src/jdarith.c create mode 100644 lib/jpeg/src/jdarith.d create mode 100644 lib/jpeg/src/jdatadst.c create mode 100644 lib/jpeg/src/jdatadst.d create mode 100644 lib/jpeg/src/jdatasrc.c create mode 100644 lib/jpeg/src/jdatasrc.d create mode 100644 lib/jpeg/src/jdcoefct.c create mode 100644 lib/jpeg/src/jdcoefct.d create mode 100644 lib/jpeg/src/jdcolor.c create mode 100644 lib/jpeg/src/jdcolor.d create mode 100644 lib/jpeg/src/jdct.h create mode 100644 lib/jpeg/src/jddctmgr.c create mode 100644 lib/jpeg/src/jddctmgr.d create mode 100644 lib/jpeg/src/jdhuff.c create mode 100644 lib/jpeg/src/jdhuff.d create mode 100644 lib/jpeg/src/jdinput.c create mode 100644 lib/jpeg/src/jdinput.d create mode 100644 lib/jpeg/src/jdmainct.c create mode 100644 lib/jpeg/src/jdmainct.d create mode 100644 lib/jpeg/src/jdmarker.c create mode 100644 lib/jpeg/src/jdmarker.d create mode 100644 lib/jpeg/src/jdmaster.c create mode 100644 lib/jpeg/src/jdmaster.d create mode 100644 lib/jpeg/src/jdmerge.c create mode 100644 lib/jpeg/src/jdmerge.d create mode 100644 lib/jpeg/src/jdpostct.c create mode 100644 lib/jpeg/src/jdpostct.d create mode 100644 lib/jpeg/src/jdsample.c create mode 100644 lib/jpeg/src/jdsample.d create mode 100644 lib/jpeg/src/jdtrans.c create mode 100644 lib/jpeg/src/jdtrans.d create mode 100644 lib/jpeg/src/jerror.c create mode 100644 lib/jpeg/src/jerror.d create mode 100644 lib/jpeg/src/jerror.h create mode 100644 lib/jpeg/src/jfdctflt.c create mode 100644 lib/jpeg/src/jfdctflt.d create mode 100644 lib/jpeg/src/jfdctfst.c create mode 100644 lib/jpeg/src/jfdctfst.d create mode 100644 lib/jpeg/src/jfdctint.c create mode 100644 lib/jpeg/src/jfdctint.d create mode 100644 lib/jpeg/src/jidctflt.c create mode 100644 lib/jpeg/src/jidctflt.d create mode 100644 lib/jpeg/src/jidctfst.c create mode 100644 lib/jpeg/src/jidctfst.d create mode 100644 lib/jpeg/src/jidctint.c create mode 100644 lib/jpeg/src/jidctint.d create mode 100644 lib/jpeg/src/jinclude.h create mode 100644 lib/jpeg/src/jmemmgr.c create mode 100644 lib/jpeg/src/jmemmgr.d create mode 100644 lib/jpeg/src/jmemnobs.c create mode 100644 lib/jpeg/src/jmemnobs.d create mode 100644 lib/jpeg/src/jmemsys.h create mode 100644 lib/jpeg/src/jmorecfg.h create mode 100644 lib/jpeg/src/jpegint.h create mode 100644 lib/jpeg/src/jpeglib.h create mode 100644 lib/jpeg/src/jpegtran.c create mode 100644 lib/jpeg/src/jpegtran.d create mode 100644 lib/jpeg/src/jquant1.c create mode 100644 lib/jpeg/src/jquant1.d create mode 100644 lib/jpeg/src/jquant2.c create mode 100644 lib/jpeg/src/jquant2.d create mode 100644 lib/jpeg/src/jutils.c create mode 100644 lib/jpeg/src/jutils.d create mode 100644 lib/jpeg/src/jversion.h create mode 100644 lib/jpeg/src/rdbmp.c create mode 100644 lib/jpeg/src/rdbmp.d create mode 100644 lib/jpeg/src/rdcolmap.c create mode 100644 lib/jpeg/src/rdcolmap.d create mode 100644 lib/jpeg/src/rdgif.c create mode 100644 lib/jpeg/src/rdgif.d create mode 100644 lib/jpeg/src/rdppm.c create mode 100644 lib/jpeg/src/rdppm.d create mode 100644 lib/jpeg/src/rdrle.c create mode 100644 lib/jpeg/src/rdrle.d create mode 100644 lib/jpeg/src/rdswitch.c create mode 100644 lib/jpeg/src/rdswitch.d create mode 100644 lib/jpeg/src/rdtarga.c create mode 100644 lib/jpeg/src/rdtarga.d create mode 100644 lib/jpeg/src/transupp.c create mode 100644 lib/jpeg/src/transupp.d create mode 100644 lib/jpeg/src/transupp.h create mode 100644 lib/jpeg/src/wrbmp.c create mode 100644 lib/jpeg/src/wrbmp.d create mode 100644 lib/jpeg/src/wrgif.c create mode 100644 lib/jpeg/src/wrgif.d create mode 100644 lib/jpeg/src/wrppm.c create mode 100644 lib/jpeg/src/wrppm.d create mode 100644 lib/jpeg/src/wrrle.c create mode 100644 lib/jpeg/src/wrrle.d create mode 100644 lib/jpeg/src/wrtarga.c create mode 100644 lib/jpeg/src/wrtarga.d create mode 100644 lib/jpeg8a/include/jconfig.h create mode 100644 lib/jpeg8a/include/jerror.h create mode 100644 lib/jpeg8a/include/jmorecfg.h create mode 100644 lib/jpeg8a/include/jpeglib.h create mode 100644 lib/jpeg8a/include/jpgogc.h create mode 100644 lib/libext2fs/AUTHORS create mode 100644 lib/libext2fs/CREDITS create mode 100644 lib/libext2fs/LICENSE create mode 100644 lib/libext2fs/Makefile create mode 100644 lib/libext2fs/include/ext2.h create mode 100644 lib/libext2fs/include/ext2_frag.h create mode 100644 lib/libext2fs/orig/Makefile create mode 100644 lib/libext2fs/orig/alloc.c create mode 100644 lib/libext2fs/orig/alloc_sb.c create mode 100644 lib/libext2fs/orig/alloc_stats.c create mode 100644 lib/libext2fs/orig/alloc_tables.c create mode 100644 lib/libext2fs/orig/badblocks.c create mode 100644 lib/libext2fs/orig/bb_compat.c create mode 100644 lib/libext2fs/orig/bb_inode.c create mode 100644 lib/libext2fs/orig/bit_ops.h create mode 100644 lib/libext2fs/orig/bitmaps.c create mode 100644 lib/libext2fs/orig/bitops.c create mode 100644 lib/libext2fs/orig/bitops.h create mode 100644 lib/libext2fs/orig/blkmap64_ba.c create mode 100644 lib/libext2fs/orig/blknum.c create mode 100644 lib/libext2fs/orig/block.c create mode 100644 lib/libext2fs/orig/bmap.c create mode 100644 lib/libext2fs/orig/bmap64.h create mode 100644 lib/libext2fs/orig/bmove.c create mode 100644 lib/libext2fs/orig/brel.h create mode 100644 lib/libext2fs/orig/brel_ma.c create mode 100644 lib/libext2fs/orig/check_desc.c create mode 100644 lib/libext2fs/orig/closefs.c create mode 100644 lib/libext2fs/orig/com_err.c create mode 100644 lib/libext2fs/orig/com_err.h create mode 100644 lib/libext2fs/orig/config.h create mode 100644 lib/libext2fs/orig/crc16.c create mode 100644 lib/libext2fs/orig/crc16.h create mode 100644 lib/libext2fs/orig/csum.c create mode 100644 lib/libext2fs/orig/dblist.c create mode 100644 lib/libext2fs/orig/dblist_dir.c create mode 100644 lib/libext2fs/orig/dir_iterate.c create mode 100644 lib/libext2fs/orig/dirblock.c create mode 100644 lib/libext2fs/orig/dirhash.c create mode 100644 lib/libext2fs/orig/disc_cache.c create mode 100644 lib/libext2fs/orig/disc_cache.h create mode 100644 lib/libext2fs/orig/dupfs.c create mode 100644 lib/libext2fs/orig/e2image.h create mode 100644 lib/libext2fs/orig/e2p/e2p.h create mode 100644 lib/libext2fs/orig/e2p/feature.c create mode 100644 lib/libext2fs/orig/e2p/fgetflags.c create mode 100644 lib/libext2fs/orig/e2p/fgetversion.c create mode 100644 lib/libext2fs/orig/e2p/fsetflags.c create mode 100644 lib/libext2fs/orig/e2p/fsetversion.c create mode 100644 lib/libext2fs/orig/e2p/getflags.c create mode 100644 lib/libext2fs/orig/e2p/getversion.c create mode 100644 lib/libext2fs/orig/e2p/hashstr.c create mode 100644 lib/libext2fs/orig/e2p/iod.c create mode 100644 lib/libext2fs/orig/e2p/ls.c create mode 100644 lib/libext2fs/orig/e2p/mntopts.c create mode 100644 lib/libext2fs/orig/e2p/ostype.c create mode 100644 lib/libext2fs/orig/e2p/parse_num.c create mode 100644 lib/libext2fs/orig/e2p/pe.c create mode 100644 lib/libext2fs/orig/e2p/percent.c create mode 100644 lib/libext2fs/orig/e2p/pf.c create mode 100644 lib/libext2fs/orig/e2p/ps.c create mode 100644 lib/libext2fs/orig/e2p/setflags.c create mode 100644 lib/libext2fs/orig/e2p/setversion.c create mode 100644 lib/libext2fs/orig/e2p/uuid.c create mode 100644 lib/libext2fs/orig/expanddir.c create mode 100644 lib/libext2fs/orig/ext2.c create mode 100644 lib/libext2fs/orig/ext2_err.h create mode 100644 lib/libext2fs/orig/ext2_ext_attr.h create mode 100644 lib/libext2fs/orig/ext2_fs.h create mode 100644 lib/libext2fs/orig/ext2_internal.c create mode 100644 lib/libext2fs/orig/ext2_internal.h create mode 100644 lib/libext2fs/orig/ext2_io.h create mode 100644 lib/libext2fs/orig/ext2_types.h create mode 100644 lib/libext2fs/orig/ext2dir.c create mode 100644 lib/libext2fs/orig/ext2dir.h create mode 100644 lib/libext2fs/orig/ext2file.c create mode 100644 lib/libext2fs/orig/ext2file.h create mode 100644 lib/libext2fs/orig/ext2fs.h create mode 100644 lib/libext2fs/orig/ext2fsP.h create mode 100644 lib/libext2fs/orig/ext3_extents.h create mode 100644 lib/libext2fs/orig/ext_attr.c create mode 100644 lib/libext2fs/orig/extent.c create mode 100644 lib/libext2fs/orig/fiemap.h create mode 100644 lib/libext2fs/orig/fileio.c create mode 100644 lib/libext2fs/orig/finddev.c create mode 100644 lib/libext2fs/orig/flushb.c create mode 100644 lib/libext2fs/orig/freefs.c create mode 100644 lib/libext2fs/orig/gekko_io.c create mode 100644 lib/libext2fs/orig/gekko_io.h create mode 100644 lib/libext2fs/orig/gen_bitmap.c create mode 100644 lib/libext2fs/orig/gen_bitmap64.c create mode 100644 lib/libext2fs/orig/get_pathname.c create mode 100644 lib/libext2fs/orig/getsectsize.c create mode 100644 lib/libext2fs/orig/getsize.c create mode 100644 lib/libext2fs/orig/i_block.c create mode 100644 lib/libext2fs/orig/icount.c create mode 100644 lib/libext2fs/orig/imager.c create mode 100644 lib/libext2fs/orig/ind_block.c create mode 100644 lib/libext2fs/orig/initialize.c create mode 100644 lib/libext2fs/orig/inline.c create mode 100644 lib/libext2fs/orig/inode.c create mode 100644 lib/libext2fs/orig/inode_io.c create mode 100644 lib/libext2fs/orig/io_manager.c create mode 100644 lib/libext2fs/orig/irel.h create mode 100644 lib/libext2fs/orig/irel_ma.c create mode 100644 lib/libext2fs/orig/ismounted.c create mode 100644 lib/libext2fs/orig/jfs_compat.h create mode 100644 lib/libext2fs/orig/jfs_dat.h create mode 100644 lib/libext2fs/orig/jfs_user.h create mode 100644 lib/libext2fs/orig/kernel-jbd.h create mode 100644 lib/libext2fs/orig/kernel-list.h create mode 100644 lib/libext2fs/orig/link.c create mode 100644 lib/libext2fs/orig/llseek.c create mode 100644 lib/libext2fs/orig/lookup.c create mode 100644 lib/libext2fs/orig/mem_allocate.h create mode 100644 lib/libext2fs/orig/mkdir.c create mode 100644 lib/libext2fs/orig/mkjournal.c create mode 100644 lib/libext2fs/orig/namei.c create mode 100644 lib/libext2fs/orig/native.c create mode 100644 lib/libext2fs/orig/newdir.c create mode 100644 lib/libext2fs/orig/openfs.c create mode 100644 lib/libext2fs/orig/partitions.h create mode 100644 lib/libext2fs/orig/progress.c create mode 100644 lib/libext2fs/orig/punch.c create mode 100644 lib/libext2fs/orig/read_bb.c create mode 100644 lib/libext2fs/orig/read_bb_file.c create mode 100644 lib/libext2fs/orig/res_gdt.c create mode 100644 lib/libext2fs/orig/rw_bitmaps.c create mode 100644 lib/libext2fs/orig/sparse.c create mode 100644 lib/libext2fs/orig/swapfs.c create mode 100644 lib/libext2fs/orig/tdb.c create mode 100644 lib/libext2fs/orig/tdb.h create mode 100644 lib/libext2fs/orig/unlink.c create mode 100644 lib/libext2fs/orig/valid_blk.c create mode 100644 lib/libext2fs/orig/version.c create mode 100644 lib/libext2fs/orig/write_bb_file.c create mode 100644 lib/libext2fs/source/Makefile create mode 100644 lib/libext2fs/source/alloc.c create mode 100644 lib/libext2fs/source/alloc_sb.c create mode 100644 lib/libext2fs/source/alloc_stats.c create mode 100644 lib/libext2fs/source/alloc_tables.c create mode 100644 lib/libext2fs/source/badblocks.c create mode 100644 lib/libext2fs/source/bb_compat.c create mode 100644 lib/libext2fs/source/bb_inode.c create mode 100644 lib/libext2fs/source/bit_ops.h create mode 100644 lib/libext2fs/source/bitmaps.c create mode 100644 lib/libext2fs/source/bitops.c create mode 100644 lib/libext2fs/source/bitops.h create mode 100644 lib/libext2fs/source/blkmap64_ba.c create mode 100644 lib/libext2fs/source/blknum.c create mode 100644 lib/libext2fs/source/block.c create mode 100644 lib/libext2fs/source/bmap.c create mode 100644 lib/libext2fs/source/bmap64.h create mode 100644 lib/libext2fs/source/bmove.c create mode 100644 lib/libext2fs/source/brel.h create mode 100644 lib/libext2fs/source/brel_ma.c create mode 100644 lib/libext2fs/source/check_desc.c create mode 100644 lib/libext2fs/source/closefs.c create mode 100644 lib/libext2fs/source/com_err.c create mode 100644 lib/libext2fs/source/com_err.h create mode 100644 lib/libext2fs/source/config.h create mode 100644 lib/libext2fs/source/crc16.c create mode 100644 lib/libext2fs/source/crc16.h create mode 100644 lib/libext2fs/source/csum.c create mode 100644 lib/libext2fs/source/dblist.c create mode 100644 lib/libext2fs/source/dblist_dir.c create mode 100644 lib/libext2fs/source/dir_iterate.c create mode 100644 lib/libext2fs/source/dirblock.c create mode 100644 lib/libext2fs/source/dirhash.c create mode 100644 lib/libext2fs/source/disc_cache.c create mode 100644 lib/libext2fs/source/disc_cache.h create mode 100644 lib/libext2fs/source/dupfs.c create mode 100644 lib/libext2fs/source/e2image.h create mode 100644 lib/libext2fs/source/e2p/e2p.h create mode 100644 lib/libext2fs/source/e2p/feature.c create mode 100644 lib/libext2fs/source/e2p/fgetflags.c create mode 100644 lib/libext2fs/source/e2p/fgetversion.c create mode 100644 lib/libext2fs/source/e2p/fsetflags.c create mode 100644 lib/libext2fs/source/e2p/fsetversion.c create mode 100644 lib/libext2fs/source/e2p/getflags.c create mode 100644 lib/libext2fs/source/e2p/getversion.c create mode 100644 lib/libext2fs/source/e2p/hashstr.c create mode 100644 lib/libext2fs/source/e2p/iod.c create mode 100644 lib/libext2fs/source/e2p/ls.c create mode 100644 lib/libext2fs/source/e2p/mntopts.c create mode 100644 lib/libext2fs/source/e2p/ostype.c create mode 100644 lib/libext2fs/source/e2p/parse_num.c create mode 100644 lib/libext2fs/source/e2p/pe.c create mode 100644 lib/libext2fs/source/e2p/percent.c create mode 100644 lib/libext2fs/source/e2p/pf.c create mode 100644 lib/libext2fs/source/e2p/ps.c create mode 100644 lib/libext2fs/source/e2p/setflags.c create mode 100644 lib/libext2fs/source/e2p/setversion.c create mode 100644 lib/libext2fs/source/e2p/uuid.c create mode 100644 lib/libext2fs/source/expanddir.c create mode 100644 lib/libext2fs/source/ext2.c create mode 100644 lib/libext2fs/source/ext2_err.h create mode 100644 lib/libext2fs/source/ext2_ext_attr.h create mode 100644 lib/libext2fs/source/ext2_frag.c create mode 100644 lib/libext2fs/source/ext2_frag.h create mode 100644 lib/libext2fs/source/ext2_fs.h create mode 100644 lib/libext2fs/source/ext2_internal.c create mode 100644 lib/libext2fs/source/ext2_internal.h create mode 100644 lib/libext2fs/source/ext2_io.h create mode 100644 lib/libext2fs/source/ext2_types.h create mode 100644 lib/libext2fs/source/ext2dir.c create mode 100644 lib/libext2fs/source/ext2dir.h create mode 100644 lib/libext2fs/source/ext2file.c create mode 100644 lib/libext2fs/source/ext2file.h create mode 100644 lib/libext2fs/source/ext2fs.h create mode 100644 lib/libext2fs/source/ext2fsP.h create mode 100644 lib/libext2fs/source/ext3_extents.h create mode 100644 lib/libext2fs/source/ext_attr.c create mode 100644 lib/libext2fs/source/extent.c create mode 100644 lib/libext2fs/source/fiemap.h create mode 100644 lib/libext2fs/source/fileio.c create mode 100644 lib/libext2fs/source/finddev.c create mode 100644 lib/libext2fs/source/flushb.c create mode 100644 lib/libext2fs/source/freefs.c create mode 100644 lib/libext2fs/source/gekko_io.c create mode 100644 lib/libext2fs/source/gekko_io.h create mode 100644 lib/libext2fs/source/gen_bitmap.c create mode 100644 lib/libext2fs/source/gen_bitmap64.c create mode 100644 lib/libext2fs/source/get_pathname.c create mode 100644 lib/libext2fs/source/getsectsize.c create mode 100644 lib/libext2fs/source/getsize.c create mode 100644 lib/libext2fs/source/i_block.c create mode 100644 lib/libext2fs/source/icount.c create mode 100644 lib/libext2fs/source/imager.c create mode 100644 lib/libext2fs/source/ind_block.c create mode 100644 lib/libext2fs/source/initialize.c create mode 100644 lib/libext2fs/source/inline.c create mode 100644 lib/libext2fs/source/inode.c create mode 100644 lib/libext2fs/source/inode_io.c create mode 100644 lib/libext2fs/source/io_manager.c create mode 100644 lib/libext2fs/source/irel.h create mode 100644 lib/libext2fs/source/irel_ma.c create mode 100644 lib/libext2fs/source/ismounted.c create mode 100644 lib/libext2fs/source/jfs_compat.h create mode 100644 lib/libext2fs/source/jfs_dat.h create mode 100644 lib/libext2fs/source/jfs_user.h create mode 100644 lib/libext2fs/source/kernel-jbd.h create mode 100644 lib/libext2fs/source/kernel-list.h create mode 100644 lib/libext2fs/source/link.c create mode 100644 lib/libext2fs/source/llseek.c create mode 100644 lib/libext2fs/source/lookup.c create mode 100644 lib/libext2fs/source/mem_allocate.h create mode 100644 lib/libext2fs/source/mkdir.c create mode 100644 lib/libext2fs/source/mkjournal.c create mode 100644 lib/libext2fs/source/namei.c create mode 100644 lib/libext2fs/source/native.c create mode 100644 lib/libext2fs/source/newdir.c create mode 100644 lib/libext2fs/source/openfs.c create mode 100644 lib/libext2fs/source/partitions.h create mode 100644 lib/libext2fs/source/progress.c create mode 100644 lib/libext2fs/source/punch.c create mode 100644 lib/libext2fs/source/read_bb.c create mode 100644 lib/libext2fs/source/read_bb_file.c create mode 100644 lib/libext2fs/source/res_gdt.c create mode 100644 lib/libext2fs/source/rw_bitmaps.c create mode 100644 lib/libext2fs/source/sparse.c create mode 100644 lib/libext2fs/source/swapfs.c create mode 100644 lib/libext2fs/source/tdb.c create mode 100644 lib/libext2fs/source/tdb.h create mode 100644 lib/libext2fs/source/unlink.c create mode 100644 lib/libext2fs/source/valid_blk.c create mode 100644 lib/libext2fs/source/version.c create mode 100644 lib/libext2fs/source/wii_release/alloc.d create mode 100644 lib/libext2fs/source/wii_release/alloc_sb.d create mode 100644 lib/libext2fs/source/wii_release/alloc_stats.d create mode 100644 lib/libext2fs/source/wii_release/alloc_tables.d create mode 100644 lib/libext2fs/source/wii_release/badblocks.d create mode 100644 lib/libext2fs/source/wii_release/bb_compat.d create mode 100644 lib/libext2fs/source/wii_release/bb_inode.d create mode 100644 lib/libext2fs/source/wii_release/bitmaps.d create mode 100644 lib/libext2fs/source/wii_release/bitops.d create mode 100644 lib/libext2fs/source/wii_release/blkmap64_ba.d create mode 100644 lib/libext2fs/source/wii_release/blknum.d create mode 100644 lib/libext2fs/source/wii_release/block.d create mode 100644 lib/libext2fs/source/wii_release/bmap.d create mode 100644 lib/libext2fs/source/wii_release/bmove.d create mode 100644 lib/libext2fs/source/wii_release/brel_ma.d create mode 100644 lib/libext2fs/source/wii_release/check_desc.d create mode 100644 lib/libext2fs/source/wii_release/closefs.d create mode 100644 lib/libext2fs/source/wii_release/com_err.d create mode 100644 lib/libext2fs/source/wii_release/crc16.d create mode 100644 lib/libext2fs/source/wii_release/csum.d create mode 100644 lib/libext2fs/source/wii_release/dblist.d create mode 100644 lib/libext2fs/source/wii_release/dblist_dir.d create mode 100644 lib/libext2fs/source/wii_release/dir_iterate.d create mode 100644 lib/libext2fs/source/wii_release/dirblock.d create mode 100644 lib/libext2fs/source/wii_release/dirhash.d create mode 100644 lib/libext2fs/source/wii_release/disc_cache.d create mode 100644 lib/libext2fs/source/wii_release/dupfs.d create mode 100644 lib/libext2fs/source/wii_release/expanddir.d create mode 100644 lib/libext2fs/source/wii_release/ext2.d create mode 100644 lib/libext2fs/source/wii_release/ext2_frag.d create mode 100644 lib/libext2fs/source/wii_release/ext2_internal.d create mode 100644 lib/libext2fs/source/wii_release/ext2dir.d create mode 100644 lib/libext2fs/source/wii_release/ext2file.d create mode 100644 lib/libext2fs/source/wii_release/ext_attr.d create mode 100644 lib/libext2fs/source/wii_release/extent.d create mode 100644 lib/libext2fs/source/wii_release/fileio.d create mode 100644 lib/libext2fs/source/wii_release/finddev.d create mode 100644 lib/libext2fs/source/wii_release/flushb.d create mode 100644 lib/libext2fs/source/wii_release/freefs.d create mode 100644 lib/libext2fs/source/wii_release/gekko_io.d create mode 100644 lib/libext2fs/source/wii_release/gen_bitmap.d create mode 100644 lib/libext2fs/source/wii_release/gen_bitmap64.d create mode 100644 lib/libext2fs/source/wii_release/get_pathname.d create mode 100644 lib/libext2fs/source/wii_release/getsectsize.d create mode 100644 lib/libext2fs/source/wii_release/getsize.d create mode 100644 lib/libext2fs/source/wii_release/i_block.d create mode 100644 lib/libext2fs/source/wii_release/icount.d create mode 100644 lib/libext2fs/source/wii_release/imager.d create mode 100644 lib/libext2fs/source/wii_release/ind_block.d create mode 100644 lib/libext2fs/source/wii_release/initialize.d create mode 100644 lib/libext2fs/source/wii_release/inline.d create mode 100644 lib/libext2fs/source/wii_release/inode.d create mode 100644 lib/libext2fs/source/wii_release/inode_io.d create mode 100644 lib/libext2fs/source/wii_release/io_manager.d create mode 100644 lib/libext2fs/source/wii_release/irel_ma.d create mode 100644 lib/libext2fs/source/wii_release/ismounted.d create mode 100644 lib/libext2fs/source/wii_release/link.d create mode 100644 lib/libext2fs/source/wii_release/llseek.d create mode 100644 lib/libext2fs/source/wii_release/lookup.d create mode 100644 lib/libext2fs/source/wii_release/mkdir.d create mode 100644 lib/libext2fs/source/wii_release/mkjournal.d create mode 100644 lib/libext2fs/source/wii_release/namei.d create mode 100644 lib/libext2fs/source/wii_release/native.d create mode 100644 lib/libext2fs/source/wii_release/newdir.d create mode 100644 lib/libext2fs/source/wii_release/openfs.d create mode 100644 lib/libext2fs/source/wii_release/progress.d create mode 100644 lib/libext2fs/source/wii_release/punch.d create mode 100644 lib/libext2fs/source/wii_release/read_bb.d create mode 100644 lib/libext2fs/source/wii_release/read_bb_file.d create mode 100644 lib/libext2fs/source/wii_release/res_gdt.d create mode 100644 lib/libext2fs/source/wii_release/rw_bitmaps.d create mode 100644 lib/libext2fs/source/wii_release/sparse.d create mode 100644 lib/libext2fs/source/wii_release/swapfs.d create mode 100644 lib/libext2fs/source/wii_release/tdb.d create mode 100644 lib/libext2fs/source/wii_release/unlink.d create mode 100644 lib/libext2fs/source/wii_release/valid_blk.d create mode 100644 lib/libext2fs/source/wii_release/version.d create mode 100644 lib/libext2fs/source/wii_release/write_bb_file.d create mode 100644 lib/libext2fs/source/write_bb_file.c create mode 100644 lib/libfat/include/fat.h create mode 100644 lib/libfat/include/libfatversion.h create mode 100644 lib/libfat/src/Makefile create mode 100644 lib/libfat/src/gba/Makefile create mode 100644 lib/libfat/src/gba/include/fat.h create mode 100644 lib/libfat/src/include/fat.h create mode 100644 lib/libfat/src/include/libfatversion.h create mode 100644 lib/libfat/src/libogc/Makefile create mode 100644 lib/libfat/src/libogc/include/fat.h create mode 100644 lib/libfat/src/libogc/wii_release/cache.d create mode 100644 lib/libfat/src/libogc/wii_release/directory.d create mode 100644 lib/libfat/src/libogc/wii_release/disc.d create mode 100644 lib/libfat/src/libogc/wii_release/fat_frag.d create mode 100644 lib/libfat/src/libogc/wii_release/fatdir.d create mode 100644 lib/libfat/src/libogc/wii_release/fatfile.d create mode 100644 lib/libfat/src/libogc/wii_release/file_allocation_table.d create mode 100644 lib/libfat/src/libogc/wii_release/filetime.d create mode 100644 lib/libfat/src/libogc/wii_release/libfat.d create mode 100644 lib/libfat/src/libogc/wii_release/lock.d create mode 100644 lib/libfat/src/libogc/wii_release/partition.d create mode 100644 lib/libfat/src/nds/Makefile create mode 100644 lib/libfat/src/nds/include/fat.h create mode 100644 lib/libfat/src/source/bit_ops.h create mode 100644 lib/libfat/src/source/cache.c create mode 100644 lib/libfat/src/source/cache.h create mode 100644 lib/libfat/src/source/common.h create mode 100644 lib/libfat/src/source/directory.c create mode 100644 lib/libfat/src/source/directory.h create mode 100644 lib/libfat/src/source/disc.c create mode 100644 lib/libfat/src/source/disc.h create mode 100644 lib/libfat/src/source/fat_frag.c create mode 100644 lib/libfat/src/source/fatdir.c create mode 100644 lib/libfat/src/source/fatdir.h create mode 100644 lib/libfat/src/source/fatfile.c create mode 100644 lib/libfat/src/source/fatfile.h create mode 100644 lib/libfat/src/source/file_allocation_table.c create mode 100644 lib/libfat/src/source/file_allocation_table.h create mode 100644 lib/libfat/src/source/filetime.c create mode 100644 lib/libfat/src/source/filetime.h create mode 100644 lib/libfat/src/source/libfat.c create mode 100644 lib/libfat/src/source/lock.c create mode 100644 lib/libfat/src/source/lock.h create mode 100644 lib/libfat/src/source/mem_allocate.h create mode 100644 lib/libfat/src/source/partition.c create mode 100644 lib/libfat/src/source/partition.h create mode 100644 lib/libntfs/include/ntfs.h create mode 100644 lib/libntfs/include/ntfs_frag.h create mode 100644 lib/libntfs/orig/AUTHORS create mode 100644 lib/libntfs/orig/COPYING create mode 100644 lib/libntfs/orig/CREDITS create mode 100644 lib/libntfs/orig/LICENSE create mode 100644 lib/libntfs/orig/Makefile create mode 100644 lib/libntfs/orig/READMII create mode 100644 lib/libntfs/orig/example/Makefile create mode 100644 lib/libntfs/orig/example/data/ehcmodule.elf create mode 100644 lib/libntfs/orig/example/source/ehcmodule_elf.h create mode 100644 lib/libntfs/orig/example/source/main.c create mode 100644 lib/libntfs/orig/example/source/mload.c create mode 100644 lib/libntfs/orig/example/source/mload.h create mode 100644 lib/libntfs/orig/include/ntfs.h create mode 100644 lib/libntfs/orig/source/Makefile create mode 100644 lib/libntfs/orig/source/acls.c create mode 100644 lib/libntfs/orig/source/acls.h create mode 100644 lib/libntfs/orig/source/attrib.c create mode 100644 lib/libntfs/orig/source/attrib.h create mode 100644 lib/libntfs/orig/source/attrlist.c create mode 100644 lib/libntfs/orig/source/attrlist.h create mode 100644 lib/libntfs/orig/source/bit_ops.h create mode 100644 lib/libntfs/orig/source/bitmap.c create mode 100644 lib/libntfs/orig/source/bitmap.h create mode 100644 lib/libntfs/orig/source/bootsect.c create mode 100644 lib/libntfs/orig/source/bootsect.h create mode 100644 lib/libntfs/orig/source/cache.c create mode 100644 lib/libntfs/orig/source/cache.h create mode 100644 lib/libntfs/orig/source/cache2.c create mode 100644 lib/libntfs/orig/source/cache2.h create mode 100644 lib/libntfs/orig/source/collate.c create mode 100644 lib/libntfs/orig/source/collate.h create mode 100644 lib/libntfs/orig/source/compat.c create mode 100644 lib/libntfs/orig/source/compat.h create mode 100644 lib/libntfs/orig/source/compress.c create mode 100644 lib/libntfs/orig/source/compress.h create mode 100644 lib/libntfs/orig/source/config.h create mode 100644 lib/libntfs/orig/source/debug.c create mode 100644 lib/libntfs/orig/source/debug.h create mode 100644 lib/libntfs/orig/source/device.c create mode 100644 lib/libntfs/orig/source/device.h create mode 100644 lib/libntfs/orig/source/device_io.c create mode 100644 lib/libntfs/orig/source/device_io.h create mode 100644 lib/libntfs/orig/source/dir.c create mode 100644 lib/libntfs/orig/source/dir.h create mode 100644 lib/libntfs/orig/source/efs.c create mode 100644 lib/libntfs/orig/source/efs.h create mode 100644 lib/libntfs/orig/source/endians.h create mode 100644 lib/libntfs/orig/source/gekko_io.c create mode 100644 lib/libntfs/orig/source/gekko_io.h create mode 100644 lib/libntfs/orig/source/index.c create mode 100644 lib/libntfs/orig/source/index.h create mode 100644 lib/libntfs/orig/source/inode.c create mode 100644 lib/libntfs/orig/source/inode.h create mode 100644 lib/libntfs/orig/source/layout.h create mode 100644 lib/libntfs/orig/source/lcnalloc.c create mode 100644 lib/libntfs/orig/source/lcnalloc.h create mode 100644 lib/libntfs/orig/source/logfile.c create mode 100644 lib/libntfs/orig/source/logfile.h create mode 100644 lib/libntfs/orig/source/logging.c create mode 100644 lib/libntfs/orig/source/logging.h create mode 100644 lib/libntfs/orig/source/mem_allocate.h create mode 100644 lib/libntfs/orig/source/mft.c create mode 100644 lib/libntfs/orig/source/mft.h create mode 100644 lib/libntfs/orig/source/misc.c create mode 100644 lib/libntfs/orig/source/misc.h create mode 100644 lib/libntfs/orig/source/mst.c create mode 100644 lib/libntfs/orig/source/mst.h create mode 100644 lib/libntfs/orig/source/ntfs.c create mode 100644 lib/libntfs/orig/source/ntfsdir.c create mode 100644 lib/libntfs/orig/source/ntfsdir.h create mode 100644 lib/libntfs/orig/source/ntfsfile.c create mode 100644 lib/libntfs/orig/source/ntfsfile.h create mode 100644 lib/libntfs/orig/source/ntfsinternal.c create mode 100644 lib/libntfs/orig/source/ntfsinternal.h create mode 100644 lib/libntfs/orig/source/ntfstime.h create mode 100644 lib/libntfs/orig/source/object_id.c create mode 100644 lib/libntfs/orig/source/object_id.h create mode 100644 lib/libntfs/orig/source/param.h create mode 100644 lib/libntfs/orig/source/reparse.c create mode 100644 lib/libntfs/orig/source/reparse.h create mode 100644 lib/libntfs/orig/source/runlist.c create mode 100644 lib/libntfs/orig/source/runlist.h create mode 100644 lib/libntfs/orig/source/security.c create mode 100644 lib/libntfs/orig/source/security.h create mode 100644 lib/libntfs/orig/source/support.h create mode 100644 lib/libntfs/orig/source/types.h create mode 100644 lib/libntfs/orig/source/unistr.c create mode 100644 lib/libntfs/orig/source/unistr.h create mode 100644 lib/libntfs/orig/source/volume.c create mode 100644 lib/libntfs/orig/source/volume.h create mode 100644 lib/libntfs/orig/source/xattrs.c create mode 100644 lib/libntfs/orig/source/xattrs.h create mode 100644 lib/libntfs/src/AUTHORS create mode 100644 lib/libntfs/src/CREDITS create mode 100644 lib/libntfs/src/LICENSE create mode 100644 lib/libntfs/src/Makefile create mode 100644 lib/libntfs/src/READMII create mode 100644 lib/libntfs/src/include/ntfs.h create mode 100644 lib/libntfs/src/include/ntfs_frag.h create mode 100644 lib/libntfs/src/source/Makefile create mode 100644 lib/libntfs/src/source/acls.c create mode 100644 lib/libntfs/src/source/acls.h create mode 100644 lib/libntfs/src/source/attrib.c create mode 100644 lib/libntfs/src/source/attrib.h create mode 100644 lib/libntfs/src/source/attrib_frag.c create mode 100644 lib/libntfs/src/source/attrlist.c create mode 100644 lib/libntfs/src/source/attrlist.h create mode 100644 lib/libntfs/src/source/bit_ops.h create mode 100644 lib/libntfs/src/source/bitmap.c create mode 100644 lib/libntfs/src/source/bitmap.h create mode 100644 lib/libntfs/src/source/bootsect.c create mode 100644 lib/libntfs/src/source/bootsect.h create mode 100644 lib/libntfs/src/source/cache.c create mode 100644 lib/libntfs/src/source/cache.h create mode 100644 lib/libntfs/src/source/cache2.c create mode 100644 lib/libntfs/src/source/cache2.h create mode 100644 lib/libntfs/src/source/collate.c create mode 100644 lib/libntfs/src/source/collate.h create mode 100644 lib/libntfs/src/source/compat.c create mode 100644 lib/libntfs/src/source/compat.h create mode 100644 lib/libntfs/src/source/compress.c create mode 100644 lib/libntfs/src/source/compress.h create mode 100644 lib/libntfs/src/source/config.h create mode 100644 lib/libntfs/src/source/debug.c create mode 100644 lib/libntfs/src/source/debug.h create mode 100644 lib/libntfs/src/source/device.c create mode 100644 lib/libntfs/src/source/device.h create mode 100644 lib/libntfs/src/source/device_io.c create mode 100644 lib/libntfs/src/source/device_io.h create mode 100644 lib/libntfs/src/source/dir.c create mode 100644 lib/libntfs/src/source/dir.h create mode 100644 lib/libntfs/src/source/efs.c create mode 100644 lib/libntfs/src/source/efs.h create mode 100644 lib/libntfs/src/source/endians.h create mode 100644 lib/libntfs/src/source/gekko_io.c create mode 100644 lib/libntfs/src/source/gekko_io.h create mode 100644 lib/libntfs/src/source/index.c create mode 100644 lib/libntfs/src/source/index.h create mode 100644 lib/libntfs/src/source/inode.c create mode 100644 lib/libntfs/src/source/inode.h create mode 100644 lib/libntfs/src/source/layout.h create mode 100644 lib/libntfs/src/source/lcnalloc.c create mode 100644 lib/libntfs/src/source/lcnalloc.h create mode 100644 lib/libntfs/src/source/logfile.c create mode 100644 lib/libntfs/src/source/logfile.h create mode 100644 lib/libntfs/src/source/logging.c create mode 100644 lib/libntfs/src/source/logging.h create mode 100644 lib/libntfs/src/source/mem_allocate.h create mode 100644 lib/libntfs/src/source/mft.c create mode 100644 lib/libntfs/src/source/mft.h create mode 100644 lib/libntfs/src/source/misc.c create mode 100644 lib/libntfs/src/source/misc.h create mode 100644 lib/libntfs/src/source/mst.c create mode 100644 lib/libntfs/src/source/mst.h create mode 100644 lib/libntfs/src/source/ntfs.c create mode 100644 lib/libntfs/src/source/ntfsdir.c create mode 100644 lib/libntfs/src/source/ntfsdir.h create mode 100644 lib/libntfs/src/source/ntfsfile.c create mode 100644 lib/libntfs/src/source/ntfsfile.h create mode 100644 lib/libntfs/src/source/ntfsfile_frag.c create mode 100644 lib/libntfs/src/source/ntfsinternal.c create mode 100644 lib/libntfs/src/source/ntfsinternal.h create mode 100644 lib/libntfs/src/source/ntfstime.h create mode 100644 lib/libntfs/src/source/object_id.c create mode 100644 lib/libntfs/src/source/object_id.h create mode 100644 lib/libntfs/src/source/param.h create mode 100644 lib/libntfs/src/source/realpath.c create mode 100644 lib/libntfs/src/source/realpath.h create mode 100644 lib/libntfs/src/source/reparse.c create mode 100644 lib/libntfs/src/source/reparse.h create mode 100644 lib/libntfs/src/source/runlist.c create mode 100644 lib/libntfs/src/source/runlist.h create mode 100644 lib/libntfs/src/source/security.c create mode 100644 lib/libntfs/src/source/security.h create mode 100644 lib/libntfs/src/source/support.h create mode 100644 lib/libntfs/src/source/types.h create mode 100644 lib/libntfs/src/source/unistr.c create mode 100644 lib/libntfs/src/source/unistr.h create mode 100644 lib/libntfs/src/source/volume.c create mode 100644 lib/libntfs/src/source/volume.h create mode 100644 lib/libntfs/src/source/wii_release/acls.d create mode 100644 lib/libntfs/src/source/wii_release/attrib.d create mode 100644 lib/libntfs/src/source/wii_release/attrib_frag.d create mode 100644 lib/libntfs/src/source/wii_release/attrlist.d create mode 100644 lib/libntfs/src/source/wii_release/bitmap.d create mode 100644 lib/libntfs/src/source/wii_release/bootsect.d create mode 100644 lib/libntfs/src/source/wii_release/cache.d create mode 100644 lib/libntfs/src/source/wii_release/cache2.d create mode 100644 lib/libntfs/src/source/wii_release/collate.d create mode 100644 lib/libntfs/src/source/wii_release/compat.d create mode 100644 lib/libntfs/src/source/wii_release/compress.d create mode 100644 lib/libntfs/src/source/wii_release/debug.d create mode 100644 lib/libntfs/src/source/wii_release/device.d create mode 100644 lib/libntfs/src/source/wii_release/device_io.d create mode 100644 lib/libntfs/src/source/wii_release/dir.d create mode 100644 lib/libntfs/src/source/wii_release/efs.d create mode 100644 lib/libntfs/src/source/wii_release/gekko_io.d create mode 100644 lib/libntfs/src/source/wii_release/index.d create mode 100644 lib/libntfs/src/source/wii_release/inode.d create mode 100644 lib/libntfs/src/source/wii_release/lcnalloc.d create mode 100644 lib/libntfs/src/source/wii_release/logfile.d create mode 100644 lib/libntfs/src/source/wii_release/logging.d create mode 100644 lib/libntfs/src/source/wii_release/mft.d create mode 100644 lib/libntfs/src/source/wii_release/misc.d create mode 100644 lib/libntfs/src/source/wii_release/mst.d create mode 100644 lib/libntfs/src/source/wii_release/ntfs.d create mode 100644 lib/libntfs/src/source/wii_release/ntfsdir.d create mode 100644 lib/libntfs/src/source/wii_release/ntfsfile.d create mode 100644 lib/libntfs/src/source/wii_release/ntfsfile_frag.d create mode 100644 lib/libntfs/src/source/wii_release/ntfsinternal.d create mode 100644 lib/libntfs/src/source/wii_release/object_id.d create mode 100644 lib/libntfs/src/source/wii_release/realpath.d create mode 100644 lib/libntfs/src/source/wii_release/reparse.d create mode 100644 lib/libntfs/src/source/wii_release/runlist.d create mode 100644 lib/libntfs/src/source/wii_release/security.d create mode 100644 lib/libntfs/src/source/wii_release/unistr.d create mode 100644 lib/libntfs/src/source/wii_release/volume.d create mode 100644 lib/libntfs/src/source/wii_release/xattrs.d create mode 100644 lib/libntfs/src/source/xattrs.c create mode 100644 lib/libntfs/src/source/xattrs.h create mode 100644 lib/png-1.2.34/include/png.h create mode 100644 lib/png-1.2.34/include/pngconf.h create mode 100644 lib/png/include/png.h create mode 100644 lib/png/include/pngconf.h create mode 100644 lib/png/src/Makefile create mode 100644 lib/png/src/Makefile-orig create mode 100644 lib/png/src/png.c create mode 100644 lib/png/src/png.d create mode 100644 lib/png/src/png.h create mode 100644 lib/png/src/pngconf.h create mode 100644 lib/png/src/pngerror.c create mode 100644 lib/png/src/pngerror.d create mode 100644 lib/png/src/pngget.c create mode 100644 lib/png/src/pngget.d create mode 100644 lib/png/src/pngmem.c create mode 100644 lib/png/src/pngmem.d create mode 100644 lib/png/src/pngpread.c create mode 100644 lib/png/src/pngpread.d create mode 100644 lib/png/src/pngpriv.h create mode 100644 lib/png/src/pngread.c create mode 100644 lib/png/src/pngread.d create mode 100644 lib/png/src/pngrio.c create mode 100644 lib/png/src/pngrio.d create mode 100644 lib/png/src/pngrtran.c create mode 100644 lib/png/src/pngrtran.d create mode 100644 lib/png/src/pngrutil.c create mode 100644 lib/png/src/pngrutil.d create mode 100644 lib/png/src/pngset.c create mode 100644 lib/png/src/pngset.d create mode 100644 lib/png/src/pngtrans.c create mode 100644 lib/png/src/pngtrans.d create mode 100644 lib/png/src/pngwio.c create mode 100644 lib/png/src/pngwio.d create mode 100644 lib/png/src/pngwrite.c create mode 100644 lib/png/src/pngwrite.d create mode 100644 lib/png/src/pngwtran.c create mode 100644 lib/png/src/pngwtran.d create mode 100644 lib/png/src/pngwutil.c create mode 100644 lib/png/src/pngwutil.d create mode 100644 lib/readme.txt create mode 100644 lib/zlib/include/zconf.h create mode 100644 lib/zlib/include/zlib.h create mode 100644 lib/zlib/src/Makefile create mode 100644 lib/zlib/src/Makefile-orig create mode 100644 lib/zlib/src/adler32.c create mode 100644 lib/zlib/src/adler32.d create mode 100644 lib/zlib/src/compress.c create mode 100644 lib/zlib/src/compress.d create mode 100644 lib/zlib/src/crc32.c create mode 100644 lib/zlib/src/crc32.d create mode 100644 lib/zlib/src/crc32.h create mode 100644 lib/zlib/src/deflate.c create mode 100644 lib/zlib/src/deflate.d create mode 100644 lib/zlib/src/deflate.h create mode 100644 lib/zlib/src/gzclose.c create mode 100644 lib/zlib/src/gzclose.d create mode 100644 lib/zlib/src/gzguts.h create mode 100644 lib/zlib/src/gzlib.c create mode 100644 lib/zlib/src/gzlib.d create mode 100644 lib/zlib/src/gzread.c create mode 100644 lib/zlib/src/gzread.d create mode 100644 lib/zlib/src/gzwrite.c create mode 100644 lib/zlib/src/gzwrite.d create mode 100644 lib/zlib/src/infback.c create mode 100644 lib/zlib/src/infback.d create mode 100644 lib/zlib/src/inffast.c create mode 100644 lib/zlib/src/inffast.d create mode 100644 lib/zlib/src/inffast.h create mode 100644 lib/zlib/src/inffixed.h create mode 100644 lib/zlib/src/inflate.c create mode 100644 lib/zlib/src/inflate.d create mode 100644 lib/zlib/src/inflate.h create mode 100644 lib/zlib/src/inftrees.c create mode 100644 lib/zlib/src/inftrees.d create mode 100644 lib/zlib/src/inftrees.h create mode 100644 lib/zlib/src/trees.c create mode 100644 lib/zlib/src/trees.d create mode 100644 lib/zlib/src/trees.h create mode 100644 lib/zlib/src/uncompr.c create mode 100644 lib/zlib/src/uncompr.d create mode 100644 lib/zlib/src/zconf.h create mode 100644 lib/zlib/src/zlib.h create mode 100644 lib/zlib/src/zutil.c create mode 100644 lib/zlib/src/zutil.d create mode 100644 lib/zlib/src/zutil.h create mode 100644 plugins/mighty/Channel Emu Plugin.pnproj create mode 100644 plugins/mighty/Channel Emu Plugin.pnps create mode 100644 plugins/mighty/Makefile create mode 100644 plugins/mighty/source/codes/codehandler.h create mode 100644 plugins/mighty/source/codes/codehandleronly.h create mode 100644 plugins/mighty/source/codes/codehandlerslota.h create mode 100644 plugins/mighty/source/codes/codes.c create mode 100644 plugins/mighty/source/codes/codes.h create mode 100644 plugins/mighty/source/codes/multidol.c create mode 100644 plugins/mighty/source/codes/multidol.h create mode 100644 plugins/mighty/source/codes/patchcode.c create mode 100644 plugins/mighty/source/codes/patchcode.h create mode 100644 plugins/mighty/source/codes/patchhook.S create mode 100644 plugins/mighty/source/config.h create mode 100644 plugins/mighty/source/fat_mine.c create mode 100644 plugins/mighty/source/fat_mine.h create mode 100644 plugins/mighty/source/lz77.c create mode 100644 plugins/mighty/source/lz77.h create mode 100644 plugins/mighty/source/menu.c create mode 100644 plugins/mighty/source/menu.h create mode 100644 plugins/mighty/source/mighty_channels.c create mode 100644 plugins/mighty/source/nand.c create mode 100644 plugins/mighty/source/nand.h create mode 100644 plugins/mighty/source/patch.c create mode 100644 plugins/mighty/source/patch.h create mode 100644 plugins/mighty/source/sonido.c create mode 100644 plugins/mighty/source/sonido.h create mode 100644 plugins/mighty/source/stub.s create mode 100644 plugins/mighty/source/tools.c create mode 100644 plugins/mighty/source/tools.h create mode 100644 plugins/mighty/source/usbstorage.c create mode 100644 plugins/mighty/source/usbstorage.h create mode 100644 plugins/mighty/source/video.c create mode 100644 plugins/mighty/source/video.h create mode 100644 source/GRRLIB.c create mode 100644 source/Makefile create mode 100644 source/NintendontConfig.h create mode 100644 source/RuntimeIOSPatch.c create mode 100644 source/RuntimeIOSPatch.h create mode 100644 source/apploader.c create mode 100644 source/apploader.h create mode 100644 source/banner.c create mode 100644 source/cache.c create mode 100644 source/cache.h create mode 100644 source/cfg.c create mode 100644 source/cfg.h create mode 100644 source/cfgutil.c create mode 100644 source/cfgutil.h create mode 100644 source/channel.c create mode 100644 source/channel.h create mode 100644 source/cheats.c create mode 100644 source/codehandler.h create mode 100644 source/codehandleronly.h create mode 100644 source/confont512.c create mode 100644 source/console.c create mode 100644 source/console.h create mode 100644 source/coverflow.c create mode 100644 source/coverflow.h create mode 100644 source/debug.c create mode 100644 source/debug.h create mode 100644 source/dir.c create mode 100644 source/dir.h create mode 100644 source/disc.c create mode 100644 source/disc.h create mode 100644 source/dns.c create mode 100644 source/dns.h create mode 100644 source/dol.c create mode 100644 source/dol.h create mode 100644 source/dolmenu.c create mode 100644 source/dolmenu.h create mode 100644 source/dvd_broadway.c create mode 100644 source/dvd_broadway.h create mode 100644 source/fat.c create mode 100644 source/fat.h create mode 100644 source/fileOps.c create mode 100644 source/fileOps.h create mode 100644 source/frag.c create mode 100644 source/frag.h create mode 100644 source/fst.c create mode 100644 source/fst.h create mode 100644 source/fwrite_patch.h create mode 100644 source/gc.c create mode 100644 source/gc.h create mode 100644 source/gcmodplay.c create mode 100644 source/geckomenu.h create mode 100644 source/gettext.c create mode 100644 source/gettext.h create mode 100644 source/grid.c create mode 100644 source/grid.h create mode 100644 source/gui.c create mode 100644 source/gui.h create mode 100644 source/guimenu.c create mode 100644 source/guimenu.h create mode 100644 source/http.c create mode 100644 source/http.h create mode 100644 source/libwbfs/libwbfs.c create mode 100644 source/libwbfs/libwbfs.h create mode 100644 source/libwbfs/libwbfs_os.h create mode 100644 source/libwbfs/rijndael.c create mode 100644 source/libwbfs/wiidisc.c create mode 100644 source/libwbfs/wiidisc.h create mode 100644 source/lz77.c create mode 100644 source/lz77.h create mode 100644 source/mem.c create mode 100644 source/mem.h create mode 100644 source/memcheck.c create mode 100644 source/memcheck.h create mode 100644 source/menu.c create mode 100644 source/menu.h create mode 100644 source/mload.c create mode 100644 source/mload.h create mode 100644 source/mload_modules.c create mode 100644 source/mload_modules.h create mode 100644 source/mp3player.c create mode 100644 source/multidol.c create mode 100644 source/multidol.h create mode 100644 source/music.c create mode 100644 source/music.h create mode 100644 source/my_GRRLIB.h create mode 100644 source/nand.c create mode 100644 source/nand.h create mode 100644 source/net.c create mode 100644 source/net.h create mode 100644 source/partition.c create mode 100644 source/partition.h create mode 100644 source/patchcode.c create mode 100644 source/patchcode.h create mode 100644 source/patchhook.S create mode 100644 source/playlog.c create mode 100644 source/playlog.h create mode 100644 source/pngu/orig/pngu.c create mode 100644 source/pngu/orig/pngu.h create mode 100644 source/pngu/pngu.c create mode 100644 source/pngu/pngu.h create mode 100644 source/pngu/pngu_grrlib.c create mode 100644 source/pngu/pngu_impl.h create mode 100644 source/ppc.h create mode 100644 source/restart.c create mode 100644 source/restart.h create mode 100644 source/savegame.c create mode 100644 source/savegame.h create mode 100644 source/sdhc.c create mode 100644 source/sdhc.h create mode 100644 source/sha1.c create mode 100644 source/sha1.h create mode 100644 source/sort.c create mode 100644 source/sort.h create mode 100644 source/splits.c create mode 100644 source/splits.h create mode 100644 source/strutil.c create mode 100644 source/strutil.h create mode 100644 source/stub.S create mode 100644 source/subsystem.c create mode 100644 source/subsystem.h create mode 100644 source/sys.c create mode 100644 source/sys.h create mode 100644 source/unicmap.c create mode 100644 source/unzip/crypt.h create mode 100644 source/unzip/ioapi.c create mode 100644 source/unzip/ioapi.h create mode 100644 source/unzip/miniunz.c create mode 100644 source/unzip/miniunz.h create mode 100644 source/unzip/mztools.c create mode 100644 source/unzip/mztools.h create mode 100644 source/unzip/unzip.c create mode 100644 source/unzip/unzip.h create mode 100644 source/update.c create mode 100644 source/usb-loader.c create mode 100644 source/usbstorage.c create mode 100644 source/usbstorage.h create mode 100644 source/util.c create mode 100644 source/util.h create mode 100644 source/utils.c create mode 100644 source/utils.h create mode 100644 source/version.h create mode 100644 source/video.c create mode 100644 source/video.h create mode 100644 source/wbfs.c create mode 100644 source/wbfs.h create mode 100644 source/wbfs_fat.c create mode 100644 source/wbfs_fat.h create mode 100644 source/wdvd.c create mode 100644 source/wdvd.h create mode 100644 source/wgui.c create mode 100644 source/wgui.h create mode 100644 source/wgui_f.h create mode 100644 source/wiip.c create mode 100644 source/wiip.h create mode 100644 source/wpad.c create mode 100644 source/wpad.h create mode 100644 source/xml.c create mode 100644 source/xml.h create mode 100644 tools/gettext/libexpat.dll create mode 100644 tools/gettext/libgettextlib.dll create mode 100644 tools/gettext/libgettextpo.dll create mode 100644 tools/gettext/libgettextsrc.dll create mode 100644 tools/gettext/libiconv2.dll create mode 100644 tools/gettext/libintl3.dll create mode 100644 tools/gettext/msgmerge.exe create mode 100644 tools/gettext/xgettext.exe create mode 100644 tools/googlecode_upload.py create mode 100644 tools/hex2bin.c create mode 100644 tools/unifont.dat create mode 100644 tools/unifont.hex create mode 100644 updates.txt create mode 100644 usbloader.pnps diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7e14f38 --- /dev/null +++ b/LICENSE.txt @@ -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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin 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. + + , 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. \ No newline at end of file diff --git a/Languages/DE.lang b/Languages/DE.lang new file mode 100644 index 0000000..850f31f --- /dev/null +++ b/Languages/DE.lang @@ -0,0 +1,1892 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2010-07-28 08:58+0100\n" +"Last-Translator: FIX94\n" +"Language-Team: GERMAN \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB von %.1fGB frei" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% von %.2fGB (%c) RZ: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB kopiert in %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d verfügbar" + +#, c-format +msgid "%d more notes" +msgstr "%d mehr Einträge" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Teilen: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, bitte warten..." + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d Sek. Timeout)" + +msgid "(This can take a couple of minutes)" +msgstr "(Dies kann einige Minuten dauern)" + +msgid ".dol too small" +msgstr ".dol zu klein" + +msgid "3D cover" +msgstr "3D Cover" + +msgid "< ASC >" +msgstr "" + +msgid "< DESC >" +msgstr "" + +msgid "< DOWNLOAD >" +msgstr "< HERUNTERLADEN >" + +msgid "About" +msgstr "Ãœber" + +msgid "Action" +msgstr "Action" + +msgid "Additional config:" +msgstr "Zusatzeinstellung:" + +msgid "Admin Lock:" +msgstr "Admin Sperre:" + +msgid "Admin Unlock" +msgstr "Admin entsperren" + +msgid "Adult" +msgstr "Für Erwachsene" + +msgid "Adventure" +msgstr "Abenteuer" + +msgid "All" +msgstr "Alle" + +msgid "All Games" +msgstr "Alle Spiele" + +msgid "All themes up to date." +msgstr "Alle Themes aktuell." + +msgid "Alt dol:" +msgstr "Alt. dol:" + +msgid "Alternative .dol:" +msgstr "Alternative .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix:" + +#, c-format +msgid "App. Path: %s" +msgstr "App. Pfad: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Bist du sicher, dass du\n" +"diese Partition %s willst?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Bist du sicher, dass du diese\n" +"Partition REPARIEREN willst?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Bist du sicher, dass du dieses Spiel\n" +"löschen willst?" + +msgid "Ascending" +msgstr "Aufsteigend" + +msgid "Auto" +msgstr "Automatisch" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Autostart Spiel: %.6s nicht gefunden!" + +msgid "Available Updates" +msgstr "Verfügbare Updates" + +msgid "Back" +msgstr "Zurück" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Basic" +msgstr "Einfach" + +msgid "Block IOS Reload:" +msgstr "Blocke IOS Reload:" + +msgid "Boot Disc" +msgstr "Starte Disk" + +msgid "Boot disc" +msgstr "Starte Disk" + +msgid "Booting Wii game, please wait..." +msgstr "Starte Wii Spiel, bitte warten..." + +#, c-format +msgid "CFG base: %s" +msgstr "CFG Basis: %s" + +msgid "Can't install Wii games!" +msgstr "Wii Spiele können nicht installiert werden!" + +msgid "Cancelled." +msgstr "Abgebrochen." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Kann Verzeichnis nicht erstellen: %s" + +msgid "Cheat Codes:" +msgstr "Cheat Codes:" + +msgid "Cheats: " +msgstr "Cheats: " + +msgid "Check For Updates" +msgstr "Nach Updates suchen" + +msgid "Checking for themes..." +msgstr "Suche nach Themes..." + +msgid "Checking for updates..." +msgstr "Suche nach Updates..." + +msgid "Choose a sorting method" +msgstr "Wähle eine Sortiermethode" + +msgid "Classic" +msgstr "Classic Controller" + +msgid "Classic Controller" +msgstr "Classic Controller" + +msgid "Clear Patches:" +msgstr "Patches aus:" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Fertig. Größe: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Konfigurationsfehler, MAX_DNS_ENTRIES erreicht während die Liste leer ist." + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Verbindungsfehler von net_read() Fehlercode: %i" + +msgid "Console" +msgstr "Konsole" + +msgid "Console Def." +msgstr "Gerätestandard" + +msgid "Controller" +msgstr "Controller" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Konnte DIP Modul nicht initialisieren! (ret= %d)" + +msgid "Country Fix:" +msgstr "Country Fix:" + +msgid "Cover" +msgstr "Cover" + +msgid "Cover Image:" +msgstr "Cover:" + +msgid "Cover Style" +msgstr "Cover Style" + +msgid "Cover~~Back" +msgstr "Hinten" + +msgid "Cover~~Front" +msgstr "Vorne" + +msgid "Create" +msgstr "Erstelle" + +msgid "Creator" +msgstr "Ersteller" + +#, c-format +msgid "Current Version: %s" +msgstr "Aktuelle Version: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Konnte Custom IOS %d nicht finden!\n" +"Bitte installieren." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Konnte Custom IOS %d nicht laden! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d ist ein Stub-IOS!\n" +"Bitte installiere es neu." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s erfolgreich geladen" + +msgid "DISC cover" +msgstr "DISK Cover" + +msgid "DOWN" +msgstr "RUNTER" + +msgid "Dance Pad" +msgstr "Tanzmatte" + +msgid "Database update successful." +msgstr "Datenbankupdate erfolgreich." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Spiel Löschen" + +msgid "Deleting" +msgstr "Lösche" + +msgid "Descending" +msgstr "Absteigend" + +msgid "Developer" +msgstr "Entwickler" + +msgid "Device is not responding!" +msgstr "Laufwerk reagiert nicht!" + +msgid "Device:" +msgstr "Laufwerk:" + +msgid "Disc" +msgstr "Disk" + +msgid "Disc (Ask)" +msgstr "Disk (Fragen)" + +msgid "Done." +msgstr "Fertig." + +msgid "Download .txt" +msgstr ".txt herunterladen" + +msgid "Download All Covers" +msgstr "Alle Cover herunterladen" + +msgid "Download All Missing Covers" +msgstr "Alle Fehlenden Cover herunterladen" + +msgid "Download Missing Covers" +msgstr "Fehlende Cover herunterladen" + +msgid "Download Themes" +msgstr "Themes herunterladen" + +msgid "Download complete." +msgstr "Herunterladen vollständig." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Downloadfehler bei Gamercard #%d." + +msgid "Download titles.txt" +msgstr "titles.txt herunterladen" + +msgid "Downloadable Content" +msgstr "Herunterladbarer Inhalt" + +msgid "Downloading ALL MISSING covers" +msgstr "Lade ALLE FEHLENDEN Cover herunter" + +msgid "Downloading ALL covers" +msgstr "Lade ALLE Cover herunter" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Lade ALLE Cover für %.6s herunter" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Lade FEHLENDE Cover für %.6s herunter" + +msgid "Downloading Theme previews..." +msgstr "Lade Vorschaubilder herunter..." + +msgid "Downloading cheats..." +msgstr "Lade Cheats herunter..." + +msgid "Downloading database." +msgstr "Lade Datenbank herunter." + +msgid "Downloading titles.txt ..." +msgstr "Lade titles.txt herunter..." + +#, c-format +msgid "Downloading: %s" +msgstr "Lade herunter: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Lade herunter: %s als %s" + +msgid "Downloads" +msgstr "Downloads" + +msgid "Drums" +msgstr "Trommeln" + +msgid "Dutch" +msgstr "Niederländisch" + +msgid "ERROR" +msgstr "FEHLER" + +msgid "ERROR creating file" +msgstr "FEHLER Dateierstellung" + +msgid "ERROR game opt" +msgstr "FEHLER Spieleoption" + +msgid "ERROR reading BCA!" +msgstr "FEHLER BCA lesen!" + +#, c-format +msgid "ERROR removing %s" +msgstr "FEHLER Löschen von %s" + +msgid "ERROR writing BCA!" +msgstr "FEHLER BCA schreiben!" + +msgid "ERROR!" +msgstr "FEHLER!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "FEHLER! (ret = %d)" + +msgid "ERROR:" +msgstr "FEHLER:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "FEHLER: %s ist nicht zugänglich" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "FEHLER: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "FEHLER: CIOS222/223 v4 ist nötig für BCA" + +msgid "ERROR: Cache Close" +msgstr "FEHLER: Cache schließen" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "FEHLER: Konnte Spiel nicht öffnen! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "FEHLER: Spiel ist bereits installiert!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "FEHLER: Bekomme Certs %d" + +msgid "ERROR: Invalid Game ID" +msgstr "FEHLER: Ungültige Spiel-ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "FEHLER: Lade EHC Modul! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"FEHLER: NTFS schreiben deaktiviert!\n" +"(setze ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "FEHLER : Keine Partitionen gefunden! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "FEHLER: Keine Wii Disk!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "FEHLER: Offset (0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "FEHLER: Öffne Partition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "FEHLER: Öffne Partition (0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "FEHLER: Setze Frag Liste (0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "FEHLER: Setze WBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "FEHLER: Setze SD Modus" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "FEHLER: USB init! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "FEHLER: WBFS nicht gemountet!" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"FEHLER: cIOS249rev18, cIOS222v4, cIOS223v4,\n" +"cIOS224v5 oder höher vorrausgesetzt um\n" +"Spiele von einer FAT Partition zu starten!\n" +"Update IOS249 oder wähle ein anderes IOS." + +msgid "ERROR: cache: out of memory" +msgstr "FEHLER: Cache: Nicht genug Speicher" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "FEHLER: Erstelle: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "FEHLER: Bekomme Frag Liste: %d" + +msgid "ERROR: memory overlap!" +msgstr "FEHLER: Speicherüberlappung!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERROR: Mehrere WBFS Partitionen werden\n" +"nur von CIOS222/223-mload unterstützt" + +msgid "ERROR: not enough free space!!" +msgstr "FEHLER: Nicht genug freier Speicher!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "FEHLER: NTFS wurde nicht richtig unmounted" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "FEHLER: Öffne %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "FEHLER: Setze Frag Liste: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "FEHLER: Aufsetzen der Fragmente %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "FEHLER: Schreibe %s (%d)." + +msgid "EXTEND" +msgstr "ERWEITERN" + +msgid "Ejecting DVD..." +msgstr "Werfe DVD aus..." + +msgid "English" +msgstr "Englisch" + +msgid "English Title" +msgstr "Englischer Titel" + +msgid "Enter Code: " +msgstr "Code eingeben: " + +msgid "Error GRRLIB init" +msgstr "FEHLER GRRLIB init" + +msgid "Error Initializing Network." +msgstr "FEHLER bei der Netzwerkinitialisierung." + +msgid "Error adding disc!" +msgstr "FEHLER beim Disk Hinzufügen!" + +msgid "Error creating directory..." +msgstr "FEHLER beim Verzeichnis Erstellen..." + +msgid "Error discarding options!" +msgstr "FEHLER beim Optionen Verwerfen!" + +msgid "Error downloading theme preview..." +msgstr "FEHLER beim Vorschaubilder Herunterladen..." + +msgid "Error downloading theme..." +msgstr "FEHLER beim Theme Herunterladen..." + +msgid "Error downloading themes..." +msgstr "FEHLER beim Themes Herunterladen..." + +msgid "Error downloading update..." +msgstr "FEHLER beim Update Herunterladen..." + +msgid "Error downloading updates..." +msgstr "FEHLER beim Updates Herunterladen..." + +msgid "Error downloading." +msgstr "FEHLER beim Herunterladen." + +msgid "Error establishing connection" +msgstr "Fehler beim Verbindungsaufbau" + +msgid "Error extracting theme..." +msgstr "FEHLER beim Theme entpacken..." + +msgid "Error opening database, update did not complete." +msgstr "FEHLER beim Datenbank öffnen, Update nicht vollständig." + +#, c-format +msgid "Error opening: %s" +msgstr "FEHLER beim Öffnen: %s" + +#, c-format +msgid "Error playing %s" +msgstr "FEHLER beim Abspielen von %s" + +msgid "Error reading .dol" +msgstr "FEHLER beim Lesen von .dol" + +msgid "Error reading dol header" +msgstr "FEHLER beim dol Header Lesen" + +#, c-format +msgid "Error saving %s" +msgstr "FEHLER beim Speichern von %s" + +msgid "Error saving options!" +msgstr "FEHLER beim Optionen speichern!" + +msgid "Error saving settings!" +msgstr "FEHLER beim Speichern der Einstellungen!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Fehler beim Speichern der Playlogdatei.\n" +"Starte vom Wii Menü um den Fehler zu beheben." + +msgid "Error: Invalid PNG image!" +msgstr "FEHLER: Ungültiges PNG Bild!" + +msgid "Error: no URL." +msgstr "FEHLER: Keine URL." + +msgid "Error: no data." +msgstr "FEHLER: Keine Daten." + +msgid "Exit" +msgstr "Verlassen" + +#, c-format +msgid "Extracting: %s" +msgstr "Entpacke: %s" + +msgid "FAIL" +msgstr "FEHLGESCHLAGEN" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC ERROR %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "KRITISCH: alloc grid (%d)" + +#, c-format +msgid "FILL %d" +msgstr "FÃœLLE %d" + +msgid "FLAT cover" +msgstr "2D Cover" + +msgid "FULL cover" +msgstr "KOMPLETTES Cover" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Aus" + +msgid "Fav: On" +msgstr "Fav: An" + +msgid "Favorite" +msgstr "Favorit" + +msgid "Favorite Games" +msgstr "Favoriten" + +msgid "Favorite:" +msgstr "Favorit:" + +msgid "Favorites:" +msgstr "Favoriten:" + +msgid "Fighting" +msgstr "Kampfspiele" + +#, c-format +msgid "File not found! %s" +msgstr "Datei nicht gefunden! %s" + +msgid "Filter" +msgstr "Filter" + +msgid "Filter Games" +msgstr "Filtere Spiele" + +msgid "Filter by Controller" +msgstr "Filter nach Controller" + +msgid "Filter by Genre" +msgstr "Filter nach Genre" + +msgid "Filter by Online Features" +msgstr "Filter nach Online-Merkmalen" + +msgid "Filter:" +msgstr "Filter:" + +msgid "Fixing EXTEND partition..." +msgstr "Repariere ERWEITERTE Partition..." + +msgid "Force NTSC" +msgstr "Erzwinge NTSC" + +msgid "Force PAL50" +msgstr "Erzwinge PAL50" + +msgid "Force PAL60" +msgstr "Erzwinge PAL60" + +msgid "Formatting" +msgstr "Formatiere" + +#, c-format +msgid "Found %s" +msgstr "%s gefunden" + +msgid "French" +msgstr "Französisch" + +msgid "Full" +msgstr "Voll" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Spielestandard" + +msgid "Game Options" +msgstr "Spieleoptionen" + +#, c-format +msgid "Game Options: %s" +msgstr "Spieleoptionen: %s" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d berichtet: %.*s" + +msgid "Genre" +msgstr "Genre" + +msgid "German" +msgstr "Deutsch" + +msgid "Global Options" +msgstr "Globale Einstellungen" + +msgid "Guitar" +msgstr "Gitarre" + +msgid "HQ cover" +msgstr "HQ Cover" + +msgid "HTTP Response was without a file" +msgstr "HTTP Antwort war ohne eine Datei" + +msgid "Hide" +msgstr "Verstecken" + +msgid "Hide Game:" +msgstr "Verstecke Spiel:" + +msgid "Hold button B to cancel." +msgstr "Halte B um abzubrechen." + +msgid "Homebrew Channel" +msgstr "Homebrew Kanal" + +msgid "Hook Type:" +msgstr "Hook Typ:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID Unterschied: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Reload: Geblockt" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS Öffnen (%s) fehlgeschlagen Code %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Falls CFG nächstes Mal nicht richtig\n" +"startet, kopiere die boot.dol.bak\n" +"über die boot.dol und starte erneuert." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Inkonsistenz in LZ77 Kodierung %x" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info Datei: %s" + +msgid "Initializing Network..." +msgstr "Initialisiere Netzwerk..." + +msgid "Install" +msgstr "Installiere" + +msgid "Install Date" +msgstr "Installierungsdatum" + +msgid "Install Game" +msgstr "Installiere Spiel" + +msgid "Install game" +msgstr "Installiere Spiel" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Installation FEHLER! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Installiere Spiel, bitte warten..." + +msgid "Invalid .dol" +msgstr "Ungültige .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Ungültige Partition '%s'" + +msgid "Italian" +msgstr "Italienisch" + +msgid "Japanese" +msgstr "Japanisch" + +msgid "Japanese Title" +msgstr "Japanischer Titel" + +msgid "Keyboard" +msgstr "Tastatur" + +msgid "Korean" +msgstr "Koreanisch" + +msgid "LEFT" +msgstr "LINKS" + +msgid "LOCKED!" +msgstr "GESPERRT!" + +msgid "Language:" +msgstr "Sprache:" + +msgid "Last Play Date" +msgstr "Zuletzt gespielt" + +msgid "Launch Methods" +msgstr "Startmethoden" + +msgid "Load OK!" +msgstr "Laden OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Loader Version: %s" + +msgid "Loading ..." +msgstr "Lade ..." + +msgid "Main" +msgstr "Hauptmenü" + +msgid "Main Menu" +msgstr "Hauptmenü" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Stelle sicher, dass USB Port 0 verwendet wird!\n" +"(USB Port nahe am Gehäuserand)" + +msgid "Manage" +msgstr "Verwalten" + +msgid "Manage Cheats" +msgstr "Verwalte Cheats" + +msgid "Microphone" +msgstr "Mikrofon" + +msgid "Motion+" +msgstr "Motion Plus" + +msgid "Mounting device, please wait..." +msgstr "Mounte Laufwerk, bitte warten..." + +msgid "Music" +msgstr "Musik" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Musikdatei aus Verzeichnis: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Musikdatei Größe: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Musikdatei: %s" + +msgid "Music: Disabled" +msgstr "Musik: Aus" + +msgid "Music: Enabled" +msgstr "Musik: Ein" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Musik: Suche nach %s Dateien in: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Musik: Nächste abzuspielende Datei: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Musik: Anzahl von %s gefundenen Dateien: %i" + +msgid "Music: musicArray contents: " +msgstr "Musik: Musiksammlung beinhaltet:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"BEACHTE: CIOS249 vor rev10:\n" +"Spiele laden von SDHC nicht unterstützt!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"BEACHTE: CIOS249 vor rev14:\n" +"Möglicher ERROR #001 nicht korrigiert!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"BEACHTE: CIOS249 vor rev9:\n" +"Spiele starten von USB nicht unterstützt!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"BEACHTE: CIOS249 rev13 wird verwendet:\n" +"Um das Spiel zu beenden, müssen sie die Wii\n" +"zurücksetzen oder ausschalten, sonst hängt\n" +"sie sich beim Starten eines weiteren Spiels auf!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"BEACHTE: CIOS 249 rev 14 wird verwendet:\n" +"Es hat bekannte Probleme mit DVD9\n" +"(Dual Layer) Spielen. Zum Abspielen und\n" +"zur Installation dieser Spiele wird\n" +"ein höheres CIOS dringend empfohlen!" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"BEACHTE: Neustart erforderlich\n" +"um das Update abzuschließen." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"BEACHTE: Partition P#%d ist ERWEITERT aber\n" +"beinhaltet ein WBFS Dateisystem. Dieses ist\n" +"ein ungültiges Setup. Drücke %s um den\n" +"Partitionstyp von ERWEITERT auf DATA zu ändern." + +msgid "NTFS compression not supported!" +msgstr "NTFS Kompression nicht unterstützt!" + +msgid "NTFS encryption not supported!" +msgstr "NTFS Verschlüsselung nicht unterstützt!" + +msgid "Network connection established." +msgstr "Netzwerkverbindung besteht." + +msgid "Network error. Can't update gamercards." +msgstr "Netzwerkfehler. Gamercard Update nicht möglich." + +msgid "New Themes" +msgstr "Neue Themes" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS Konnektivität" + +msgid "No" +msgstr "Nein" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Keine Domäne in der URL '%s'" + +msgid "No games found!" +msgstr "Keine Spiele gefunden!" + +msgid "No partition selected!" +msgstr "Keine Partition ausgewählt!" + +msgid "No themes found." +msgstr "Keine Themes gefunden." + +msgid "No updates found." +msgstr "Keine Updates gefunden." + +msgid "None found on disc" +msgstr "Keine auf Disk gefunden" + +msgid "Not Found!" +msgstr "Nicht gefunden!" + +msgid "Number of Online Players" +msgstr "Anzahl der Online-Spieler" + +msgid "Number of Players" +msgstr "Anzahl der Spieler" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (Cheats):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina Cheat Manager" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Code Fehler" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Codes gefunden." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Keine Codes gefunden" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Kein Speicher mehr frei" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Suche nach Codes..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Zu viele Codes" + +msgid "Off" +msgstr "Aus" + +msgid "On" +msgstr "An" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Online-Inhalte" + +msgid "Online Play" +msgstr "Online" + +msgid "Online Players" +msgstr "Online-Spieler" + +msgid "Online Score List" +msgstr "Online-Punkteliste" + +msgid "Online Updates" +msgstr "Online-Updates" + +msgid "Open Sort" +msgstr "Öffne Sortierung" + +msgid "Open Style" +msgstr "Öffne Style" + +msgid "Opening DVD disc..." +msgstr "Lade DVD Disk..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Lade Partition: %s" + +msgid "Options" +msgstr "Optionen" + +msgid "Options discarded for this game." +msgstr "Optionen des Spiels verworfen." + +msgid "Options saved for this game." +msgstr "Optionen des Spiels gespeichert." + +msgid "Out of memory" +msgstr "Nicht genug Speicher" + +msgid "Page:" +msgstr "Seite:" + +msgid "Partition:" +msgstr "Partition:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partition: %s nicht gefunden!" + +msgid "Party" +msgstr "Party" + +msgid "Paused Start" +msgstr "Pausierter Start" + +msgid "Platformer" +msgstr "Jump & Run" + +msgid "Play Count" +msgstr "Spielzähler" + +msgid "Players" +msgstr "Spieler" + +msgid "Please insert a game disc..." +msgstr "Wii Spiele-Disk einlegen..." + +#, c-format +msgid "Press %s button for options." +msgstr "Drücke %s: Optionen" + +#, c-format +msgid "Press %s button to %s." +msgstr "Drücke %s: %s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Drücke %s: Codes aktivieren" + +#, c-format +msgid "Press %s button to cancel." +msgstr "Drücke %s: Abbrechen" + +#, c-format +msgid "Press %s button to change device." +msgstr "Drücke %s: Laufwerk wechseln" + +#, c-format +msgid "Press %s button to continue." +msgstr "Drücke %s: Fortfahren" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Drücke %s: Dateisystem löschen" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Drücke %s: BCA schreiben" + +#, c-format +msgid "Press %s button to exit." +msgstr "Drücke %s: Verlassen" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Drücke %s: ERWEITERT/WBFS reparieren" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Drücke %s: WBFS formatieren" + +#, c-format +msgid "Press %s button to go back." +msgstr "Drücke %s: Zurück" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Drücke %s: Partition auswählen" + +#, c-format +msgid "Press %s button to select." +msgstr "Drücke %s: Auswählen" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Drücke %s: Codes überspringen" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Drücke %s: FAT synchonisieren" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Drücke %s: Vollbild-Vorschau" + +#, c-format +msgid "Press %s for game options" +msgstr "Drücke %s: Spieloptionen" + +#, c-format +msgid "Press %s for global options" +msgstr "Drücke %s: Globale Einstellungen" + +#, c-format +msgid "Press %s to discard options" +msgstr "Drücke %s: Optionen verwerfen" + +#, c-format +msgid "Press %s to download and update" +msgstr "Drücke %s: Herunterladen und Updaten" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Drücke %s: Theme herunterladen" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Drücke: %s = herunterladen, %s = zurück" + +#, c-format +msgid "Press %s to return" +msgstr "Drücke %s: Zurück" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Drücke %s: Einstellungen speichern" + +#, c-format +msgid "Press %s to save options" +msgstr "Drücke %s: Optionen speichern" + +#, c-format +msgid "Press %s to save selection" +msgstr "Drücke %s: Auswahl speichern" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Drücke %s: Filtertypen wählen" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Drücke %s: Sortiermethode wählen" + +#, c-format +msgid "Press %s to start game" +msgstr "Drücke %s: Spielstart" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Drücke %s: Update ohne meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Drücke LINKS/RECHTS: Laufwerk wählen" + +msgid "Press 2 to reload IOS" +msgstr "Drücke 2: IOS neuladen" + +msgid "Press A to select device" +msgstr "Drücke A: Gerät auswählen" + +msgid "Press A to continue without config.txt" +msgstr "Drücke A: Weiter ohne config.txt" + +msgid "Press 1 to skip WBFS mounting" +msgstr "Drücke 1: Weiter ohne WBFS" + +msgid "Press B to exit to HBC" +msgstr "Drücke B: Beende zum HBC" + +msgid "Press HOME to reset" +msgstr "Drücke HOME: Reset" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Drücke LINKS/RECHTS: IOS auswählen" + +msgid "Press any button to continue..." +msgstr "Beliebigen Knopf drücken zum Fortfahren..." + +msgid "Press any button to exit..." +msgstr "Beliebigen Knopf drücken zum Verlassen..." + +msgid "Press any button to restart..." +msgstr "Beliebigen Knopf drücken für Neustart..." + +msgid "Press any button..." +msgstr "Beliebigen Knopf drücken..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Drücke %s: Cover herunterladen" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Drücke %s: DVD auswerfen" + +msgid "Profile:" +msgstr "Profil:" + +#, c-format +msgid "Profile: %s" +msgstr "Profil: %s" + +msgid "Program Updates" +msgstr "Programm Updates" + +msgid "Publisher" +msgstr "Herausgeber" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Beenden" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "RECHTS" + +msgid "RPG" +msgstr "Rollenspiele" + +msgid "Racing" +msgstr "Rennen" + +#, c-format +msgid "Rated %s" +msgstr "FSK/PEGI %s" + +msgid "Rating" +msgstr "Bewertung" + +msgid "Read" +msgstr "Lese" + +msgid "Reading BCA..." +msgstr "Lese BCA..." + +msgid "Reboot" +msgstr "Neustarten" + +msgid "Release Date" +msgstr "Erscheinungsdatum" + +msgid "Release Notes: (short)" +msgstr "Inhaltsangabe: (kurz)" + +msgid "Removing game, please wait..." +msgstr "Lösche Spiel, bitte warten..." + +msgid "Restarting Wii..." +msgstr "Starte Wii neu..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Zurückgegeben! (ret = %d)" + +msgid "Rhythm" +msgstr "Rhythmus" + +msgid "Rows:" +msgstr "Reihen:" + +msgid "Running benchmark, please wait" +msgstr "Benchmark läuft, bitte warten" + +msgid "S. Chinese" +msgstr "Chinesisch (S.)" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC Karte" + +msgid "SUCCESS!" +msgstr "ERFOLG!" + +msgid "Save .gct" +msgstr "Speichere .gct" + +msgid "Save Debug" +msgstr "Speichere Debug" + +msgid "Save Settings" +msgstr "Speichern" + +msgid "Save debug.log" +msgstr "Speichere debug.log" + +msgid "Saving Settings... " +msgstr "Speichere Einstellungen..." + +msgid "Saving cheats..." +msgstr "Speichere Cheats..." + +msgid "Saving gamelist.txt ... " +msgstr "Speichere gamelist.txt..." + +msgid "Saving settings..." +msgstr "Speichere Einstellungen..." + +msgid "Saving:" +msgstr "Speichere:" + +#, c-format +msgid "Saving: %s" +msgstr "Speichere: %s" + +msgid "Scroll:" +msgstr "Scrollen:" + +msgid "Select Alternative .dol:" +msgstr "Wähle alternative .dol:" + +msgid "Select WBFS device:" +msgstr "Wähle WBFS Laufwerk:" + +msgid "Select a different partition" +msgstr "Wähle eine andere Partition" + +msgid "Select a partition" +msgstr "Wähle eine Partition" + +msgid "Select all" +msgstr "Wähle alle" + +msgid "Select the game you want to boot" +msgstr "Wähle das zu startende Spiel aus" + +msgid "Selected Game" +msgstr "Gewähltes Spiel" + +msgid "Settings" +msgstr "Einstellungen" + +msgid "Shooter" +msgstr "Shooter" + +msgid "Show All" +msgstr "Zeige alle" + +msgid "Show cIOS info" +msgstr "Zeige CIOS Informationen" + +msgid "Shutdown" +msgstr "Herunterfahren" + +msgid "Side:" +msgstr "Seite:" + +msgid "Simulation" +msgstr "Simulation" + +msgid "Size(GB) Type Mount Used" +msgstr "Größe(GB) Typ Mount Benutzt" + +#, c-format +msgid "Size: %d bytes" +msgstr "Größe: %d Bytes" + +msgid "Sort" +msgstr "Sortiere" + +msgid "Sort Games" +msgstr "Sortiere Spiele" + +msgid "Sort Order:" +msgstr "Sortieren Folge:" + +msgid "Sort Type:" +msgstr "Sortieren Typ:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Sortiere: %s-%s" + +msgid "Spanish" +msgstr "Spanisch" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Start" + +msgid "Start Game" +msgstr "Starte Spiel" + +msgid "Start this game?" +msgstr "Dieses Spiel starten?" + +msgid "Stopping DVD..." +msgstr "Stoppe DVD..." + +msgid "Strategy" +msgstr "Strategie" + +msgid "Style" +msgstr "Style" + +msgid "Style:" +msgstr "Style:" + +msgid "Sync FAT free space info?" +msgstr "FAT freien Speicher synchronisieren?" + +msgid "Synchronizing, please wait." +msgstr "Synchronisiere, bitte warten." + +msgid "System" +msgstr "System" + +msgid "System Def." +msgstr "Systemstandard" + +msgid "T. Chinese" +msgstr "Chinesisch (T.)" + +msgid "Theme" +msgstr "Theme" + +msgid "Theme Info" +msgstr "Theme-Info" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Theme vielleicht zu groß.\n" +"Cfg neustarten empfohlen.\n" + +msgid "Theme:" +msgstr "Theme:" + +#, c-format +msgid "Theme: %s" +msgstr "Theme: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Themes angeboten von wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Herunterladbare Themes" + +msgid "Themes with updates" +msgstr "Themes mit Updates" + +msgid "Title" +msgstr "Titel" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Zu viele Cheats! (%d)" + +msgid "Too many code lines!" +msgstr "Zu viele Codezeilen!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Zu viele Fragmente! %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Versuche (url#%d)..." + +msgid "UNUSED" +msgstr "UNBENUTZT" + +msgid "UP" +msgstr "OBEN" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' beginnt nicht mit 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' hat keinen Pfadteil" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko erkannt. Debugging aktiviert." + +msgid "USB Mass Storage Device" +msgstr "USB Massenspeicher" + +msgid "Unknown" +msgstr "Unbekannt" + +msgid "Unknown syntax!" +msgstr "Unbekannte Syntax!" + +msgid "Unplayed" +msgstr "Nichtgespielt" + +msgid "Unplayed Games" +msgstr "Nichtgespielte Spiele" + +msgid "Update WiiTDB Game Database" +msgstr "WiiTDB Datenbank aktualisieren" + +msgid "Update themes" +msgstr "Themes aktualisieren" + +msgid "Update titles.txt" +msgstr "Spieletitel aktualisieren" + +msgid "Updates" +msgstr "Updates" + +msgid "Updating database." +msgstr "Aktualisiere Datenbank." + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Genutzt: %.1fGB Frei: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Nutze gespeicherte alternative .dol:" + +msgid "Version" +msgstr "Version" + +msgid "Video Patch:" +msgstr "Video Patch:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Ansicht" + +msgid "Vitality Sensor" +msgstr "Vitalitätssensor" + +msgid "WARNING:" +msgstr "WARNUNG:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB frei von %.1fGB" + +msgid "Wheel" +msgstr "Lenkrad" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiTDB Game Database" +msgstr "WiiTDB Game Database" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Write Playlog:" +msgstr "Schreibe Playlog:" + +#, c-format +msgid "Writing to %s" +msgstr "Schreibe auf %s" + +#, c-format +msgid "Writing: %s" +msgstr "Schreibe: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Falsche Größe: %d (%d)" + +msgid "Yes" +msgstr "Ja" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Du kannst auch versuchen das\n" +"Laufwerk ab- und anzuklemmen,\n" +"oder warte noch etwas" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[VERÄNDERT]" + +msgid "[ FOUND ]" +msgstr "[GEFUNDEN]" + +msgid "[ SAVED ]" +msgstr "[GESPEICHERT]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[Kein HQ]" + +msgid "[USED]" +msgstr "[BENUTZT]" + +msgid "[default]" +msgstr "[Standard]" + +msgid "[saved]" +msgstr "[gespeichert]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "Ungültige wbfs Datei: %s" + +msgid "calculating space, please wait..." +msgstr "Berechne freien Speicher, bitte warten..." + +msgid "delete" +msgstr "Lösche" + +#, c-format +msgid "dest: %p - %p" +msgstr "Ziel: %p - %p" + +msgid "discard" +msgstr "verwerfen" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "Domäne %s konnte nicht aufgelöst werden" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "Fehler beim Lesen: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "Für %d Spieler" + +msgid "for 1 player" +msgstr "Für 1 Spieler" + +msgid "format" +msgstr "Formatiere" + +msgid "free" +msgstr "frei" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "Lineare Lesegeschwindigkeit: %.2f MB/s" + +msgid "linear..." +msgstr "Linear..." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "Musik Datei zu groß (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 oder music.mod nicht gefunden!" + +msgid "no file" +msgstr "Keine Datei" + +msgid "none" +msgstr "Nichts" + +msgid "or format a WBFS partition." +msgstr "oder formatiere eine WBFS Partition." + +msgid "page" +msgstr "Seite" + +msgid "parse error" +msgstr "Analysierungsfehler" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "Zufällige Lesegeschwindigkeit: %.2f MB/s" + +msgid "random..." +msgstr "Zufall..." + +msgid "reset" +msgstr "Reset" + +msgid "revert" +msgstr "Rückgängig" + +msgid "save" +msgstr "Speichern" + +#, c-format +msgid "split error: %s" +msgstr "Teilen Fehler: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "Kann Wii Disk nicht laden" + +#, c-format +msgid "used: %p - %p" +msgstr "Benutzt: %p - %p" + +#~ msgid "Front" +#~ msgstr "Vorne" + +#~ msgid "Loading previous game list..." +#~ msgstr "Lade vorherige Spieleliste..." + +#~ msgid "Remove Game" +#~ msgstr "Lösche Spiel" diff --git a/Languages/DK.lang b/Languages/DK.lang new file mode 100644 index 0000000..b207100 --- /dev/null +++ b/Languages/DK.lang @@ -0,0 +1,2365 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-27 14:40+GMT1\n" +"Last-Translator: Fox888 <>\n" +"Language-Team: <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: Danish\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB af %.1fGB fri" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% af %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB kopieret I %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d tilgængelig" + +#, c-format +msgid "%d more notes" +msgstr "%d flere noter" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Split: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, Vent venligst..." + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d sekunders timeout)" + +msgid "(This can take a couple of minutes)" +msgstr "(Dette kan tage flere minutter)" + +msgid ".dol too small" +msgstr ".dol for lille" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "1st-Person shooter" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "3D cover" + +msgid "3rd-person Shooter" +msgstr "3rd-Person shooter" + +msgid "< ASC >" +msgstr "< ASC >" + +msgid "< DESC >" +msgstr "< DESC >" + +msgid "< DOWNLOAD >" +msgstr "< DOWNLOAD >" + +msgid "About" +msgstr "Om" + +msgid "Action" +msgstr "Action" + +msgid "Additional config:" +msgstr "Ekstra config:" + +msgid "Admin Lock:" +msgstr "Admin LÃ¥s:" + +msgid "Admin Unlock" +msgstr "Admin oplÃ¥st" + +msgid "Adult" +msgstr "Voksen" + +msgid "Adventure" +msgstr "Eventyr" + +msgid "All" +msgstr "Alle" + +msgid "All Channels" +msgstr "Alle kanaler" + +msgid "All Games" +msgstr "Alle spil" + +msgid "All themes up to date." +msgstr "Alle temaer er op til dato." + +msgid "Alt Button Cfg:" +msgstr "Alt knap Cfg:" + +msgid "Alt dol:" +msgstr "Alt dol:" + +msgid "Alternative .dol:" +msgstr "Alternativ .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix:" + +#, c-format +msgid "App. Path: %s" +msgstr "App. Sti: %s" + +msgid "Arcade" +msgstr "Arkade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Er du Sikker pÃ¥ at du vil %s\n" +"denne partition" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Er du sikker pÃ¥ at du vil FIX\n" +"denne partition?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Er du sikker pÃ¥ at du vil fjerne dette\n" +"spil?" + +msgid "Ascending" +msgstr "Stigende" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto-start spil: %.6s ikke fundet!" + +msgid "Available Updates" +msgstr "Tilgængelige opdateringer" + +msgid "Back" +msgstr "Tilbage" + +msgid "Balance Board" +msgstr "Balance bræt" + +msgid "Baseball" +msgstr "Baseball" + +msgid "Basic" +msgstr "Basic" + +msgid "Basketball" +msgstr "Basketball" + +msgid "Bike Racing" +msgstr "Cykelløb" + +msgid "Billiards" +msgstr "Billard" + +msgid "Block IOS Reload:" +msgstr "Block IOS Reload:" + +msgid "Board Game" +msgstr "Bræt spil" + +msgid "Boot Disc" +msgstr "Indlæs disc" + +msgid "Boot disc" +msgstr "Indlæs disc" + +msgid "Booting game, please wait..." +msgstr "Indlæser spil, vent venligst..." + +msgid "Bowling" +msgstr "Bowling" + +msgid "Boxing" +msgstr "Boksning" + +msgid "Business Sim" +msgstr "Forretnings sim" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG base: %s" + +msgid "Camera" +msgstr "Kamera" + +msgid "Can not dump GC savegames.\n" +msgstr "Kan ikke dumpe GC savegames.\n" + +msgid "Can't install Wii games!" +msgstr "Kan ikke installer Wii spil!" + +msgid "Cancelled." +msgstr "Annulleret." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Kan ikke lave mappe: %s" + +msgid "Cards" +msgstr "Kort" + +msgid "Channel" +msgstr "Kanal" + +msgid "Cheat Codes:" +msgstr "Cheat Koder:" + +msgid "Cheats: " +msgstr "Cheats: " + +msgid "Check For Updates" +msgstr "Tjek for opdateringer" + +msgid "Checking for themes..." +msgstr "Søger efter temaer..." + +msgid "Checking for updates..." +msgstr "Søger efter opdateringer..." + +msgid "Chess" +msgstr "Skak" + +msgid "Choose a sorting method" +msgstr "Vælg en sorterings metode" + +msgid "Classic" +msgstr "Klassisk" + +msgid "Classic Controller" +msgstr "Classic controller" + +msgid "Clear" +msgstr "Ryd" + +msgid "Clear Patches:" +msgstr "Clear patches:" + +msgid "Coaching" +msgstr "" + +msgid "Compare Type" +msgstr "Sammenligne type" + +msgid "Compilation" +msgstr "Kompilering" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Komplet. Str: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Konfiguration fejl, MAX_DNS_ENTRIES opnÃ¥et mens listen er tom" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Forbindelses fejl fra net_read() fejl kode: %i" + +msgid "Console" +msgstr "Konsol" + +msgid "Construction Sim" +msgstr "Konstruktion sim" + +msgid "Contains" +msgstr "Indeholder" + +msgid "Controller" +msgstr "Kontroller" + +msgid "Cooking" +msgstr "Madlavning" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Kunne ikke initialisere DIP modul! (ret = %d)" + +msgid "Country Fix:" +msgstr "Country Fix:" + +msgid "Cover" +msgstr "Cover" + +msgid "Cover Image:" +msgstr "Cover billede:" + +msgid "Cover Style" +msgstr "Cover stil" + +msgid "Covers Available" +msgstr "Tilgængelig covers" + +msgid "Cover~~Back" +msgstr "Cover~~Bagside" + +msgid "Cover~~Front" +msgstr "Cover~~Forside" + +msgid "Create" +msgstr "Lav" + +msgid "Creator" +msgstr "Skaber" + +msgid "Cricket" +msgstr "Cricket" + +#, c-format +msgid "Current Version: %s" +msgstr "Nuværende version: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Custom IOS %d kunne ikke findes!\n" +"Vær venlig og installer den" + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Custom IOS %d kunne ikke indlæses! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d Er En Stub!\n" +"Vær venlig og genindstaller den" + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s indlæst" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "DISK cover" + +msgid "DOWN" +msgstr "NED" + +msgid "Dance" +msgstr "Danse" + +msgid "Dance Pad" +msgstr "Danse mÃ¥tte" + +msgid "Darts" +msgstr "Dart" + +msgid "Database update successful." +msgstr "Database opdatering lykkedes." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Slet spil" + +msgid "Deleting" +msgstr "Sletter" + +msgid "Descending" +msgstr "Faldende" + +msgid "Developer" +msgstr "Udvikler" + +msgid "Device is not responding!" +msgstr "Enhed reagere ikke!" + +msgid "Device:" +msgstr "Enhed:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolution acceptere kun rene dumps!\n" + +msgid "Devolution:" +msgstr "Devolution:" + +msgid "Disc" +msgstr "Disk" + +msgid "Disc (Ask)" +msgstr "Disk (Spørg)" + +msgid "Done." +msgstr "Færdig." + +msgid "Download .txt" +msgstr "Download .txt" + +msgid "Download All Covers" +msgstr "Download alle covers" + +msgid "Download All Missing Covers" +msgstr "Downloade alle manglende covers" + +msgid "Download Missing Covers" +msgstr "Download manglende covers" + +msgid "Download Plugins" +msgstr "Download plugins" + +msgid "Download Themes" +msgstr "Download temaer" + +msgid "Download complete." +msgstr "Download komplet." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Gamercard download fejl #%d." + +msgid "Download game information" +msgstr "Download spil information" + +msgid "Downloadable Content" +msgstr "Indhold som kan downloades" + +msgid "Downloading ALL MISSING covers" +msgstr "Downloader ALLE MANGLENDE covers" + +msgid "Downloading ALL covers" +msgstr "Downloader ALLE covers" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Downloader Alle covers for %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Downloader MANGLENDE covers for %.6s" + +msgid "Downloading Theme previews..." +msgstr "Downloader tema forhÃ¥ndsvisning..." + +msgid "Downloading cheats..." +msgstr "Downloader cheats..." + +msgid "Downloading database." +msgstr "Downloader database." + +msgid "Downloading devolution." +msgstr "Downloader devolution." + +msgid "Downloading mighty plugin." +msgstr "Downloader mighty plugin." + +msgid "Downloading neek2o plugin." +msgstr "Downloader neek2o plugin." + +msgid "Downloading titles.txt ..." +msgstr "Downloader titles.txt ..." + +msgid "Downloading translation file." +msgstr "Downloader oversættelse fil." + +msgid "Downloading unifont." +msgstr "Downloader unifont." + +#, c-format +msgid "Downloading: %s" +msgstr "Downloader: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Downloader: %s som %s" + +msgid "Downloads" +msgstr "Downloads" + +msgid "Drawing" +msgstr "Tegne" + +msgid "Drums" +msgstr "Trommer" + +msgid "Dump savegame" +msgstr "Dump savegame" + +msgid "Duplicate ID3" +msgstr "Duplikere ID3" + +msgid "Dutch" +msgstr "Hollandsk" + +msgid "ERROR" +msgstr "FEJL" + +msgid "ERROR creating file" +msgstr "FEJL kunne ikke lave fil" + +msgid "ERROR game opt" +msgstr "FEJL spil opt" + +msgid "ERROR reading BCA!" +msgstr "FEJL kunne ikke læse BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "FEJL kunne ikke fjerne %s" + +msgid "ERROR writing BCA!" +msgstr "FEJL kunne ikke skrive BCA!" + +msgid "ERROR!" +msgstr "FEJL!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "FEJL! (ret = %d)" + +msgid "ERROR:" +msgstr "FEJL:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "FEJL: %s er ikke tilgængelig" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "FEJL: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "FEJL: CIOS222/223 v4 pÃ¥krævet for BCA" + +msgid "ERROR: Cache Close" +msgstr "FEJL: Cache Luk" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "FEJL: Kunne ikke Ã¥bne spil! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "FEJL: Spil allerede installeret" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "FEJL: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "FEJL: Ugyldig spil ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "FEJL: Indlæsning af EHC module! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"FEJL: NTFS skrivning er deaktiveret!\n" +"(set ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "FEJL: Ingen partitioner fundet! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "FEJL: Ikke en Wii DVD!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "FEJL: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "FEJL: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "FEJL: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "FEJL: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "FEJL: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "FEJL: Setting SD mode" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "FEJL: USB init! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "FEJL: WBFS er ikke indlæst!" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"FEJL: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 eller højere pÃ¥krævet\n" +"for at starte spil fra en FAT partiton!\n" +"Opgrader IOS249 eller vælg en anden IOS." + +msgid "ERROR: cache: out of memory" +msgstr "FEJL: cache: ikke mere hukommelse" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "FEJL: kunne ikke skabe: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "FEJL: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "FEJL: Hukommelse overlap!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"FEJL: multiple wbfs partitions\n" +"kun understøttet med IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "FEJL: Ikke nok fri plads!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "FEJL: ntfs var ikke lukket ordentlig ned" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "FEJL: Ã…bning af %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "FEJL: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "FEJL: Opsætning af fragmenter %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "FEJL: drevet eller partition %s/ er ikke forbundet!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "FEJL: Skrivning %s (%d)." + +msgid "EXTEND" +msgstr "UDVIDE" + +msgid "Educational" +msgstr "Belærende" + +msgid "Ejecting DVD..." +msgstr "Udskyder DVD..." + +msgid "English" +msgstr "Engelsk" + +msgid "English Title" +msgstr "Engelske Titler" + +msgid "Enter Code: " +msgstr "Skriv kode: " + +msgid "Error GRRLIB init" +msgstr "Fejl GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Fejlede initialisering af netværk. " + +msgid "Error adding disc!" +msgstr "Fejlede at tilføje disc!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Fejlede med allokering af buffer: %d" + +msgid "Error creating directory..." +msgstr "Fejlede at skabe mappe..." + +msgid "Error discarding options!" +msgstr "Fejlede bortskaffelse af indstillinger!" + +msgid "Error downloading theme preview..." +msgstr "Fejlede at downloade tema forhÃ¥ndsvisning..." + +msgid "Error downloading theme..." +msgstr "Fejlede at downloade tema..." + +msgid "Error downloading themes..." +msgstr "Fejlede at downloade temaer..." + +msgid "Error downloading update..." +msgstr "Fejlede at downloade opdatering..." + +msgid "Error downloading updates..." +msgstr "Fejlede at downloade opdateringer..." + +msgid "Error downloading." +msgstr "Fejlede at downloade." + +msgid "Error establishing connection" +msgstr "Kunne ikke oprette forbindelse" + +msgid "Error extracting theme..." +msgstr "Fejlede at udpakke tema..." + +msgid "Error opening database, update did not complete." +msgstr "Kunne ikke Ã¥bne database, opdatering ikke komplet." + +#, c-format +msgid "Error opening: %s" +msgstr "Kunne ikke Ã¥bne: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Kunne ikke afspille %s" + +msgid "Error reading .dol" +msgstr "Kunne ikke læse .dol" + +msgid "Error reading dol header" +msgstr "Kunne ikke læse dol header" + +#, c-format +msgid "Error saving %s" +msgstr "Kunne ikke gemme %s" + +msgid "Error saving options!" +msgstr "Kunne ikke gemme indstillinger!" + +msgid "Error saving settings!" +msgstr "Kunne ikke gemme indstillinger!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Fejlede at gemme playlog fil.\n" +"Start fra Wii Menu for at repare." + +msgid "Error: Invalid PNG image!" +msgstr "Fejl: Ugyldig PNG billede!" + +msgid "Error: no URL." +msgstr "Fejl: Ingen URL." + +msgid "Error: no data." +msgstr "Fejl: Ingen data." + +msgid "Exercise" +msgstr "Motion" + +msgid "Exit" +msgstr "Afslut" + +#, c-format +msgid "Extracting: %s" +msgstr "Udpakker: %s" + +msgid "FAIL" +msgstr "Mislykkes" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC FEJL %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "ALVORLIG FEJL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "FYLD %d" + +msgid "FLAT cover" +msgstr "FLAD cover" + +msgid "FULL cover" +msgstr "FULD cover" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Fra" + +msgid "Fav: On" +msgstr "Fav: Til" + +msgid "Favorite" +msgstr "Farvorit" + +msgid "Favorite Games" +msgstr "Favorit spil" + +msgid "Favorite:" +msgstr "Favoritter:" + +msgid "Favorites:" +msgstr "Farvoritter:" + +msgid "Fighting" +msgstr "Kamp" + +#, c-format +msgid "File not found! %s" +msgstr "Fil ikke fundet! %s" + +msgid "Filter" +msgstr "Filter" + +msgid "Filter Games" +msgstr "Filter spil" + +msgid "Filter by Controller" +msgstr "Filter ifølge controller" + +msgid "Filter by Game Type" +msgstr "Filter ifølge spil type" + +msgid "Filter by Genre" +msgstr "Filter ifølge genre" + +msgid "Filter by Online Features" +msgstr "Filter ifølge Online egenskaber" + +msgid "Filter:" +msgstr "Filter:" + +msgid "Fishing" +msgstr "Fiskeri" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "Reparer UDVIDET partition..." + +msgid "Flight Sim" +msgstr "Flyv sim" + +msgid "Football" +msgstr "Amerikansk fodbold" + +msgid "Force Devolution:" +msgstr "tving devolution:" + +msgid "Force NTSC" +msgstr "Tvinge NTSC" + +msgid "Force NTSC 480p" +msgstr "Tving NTSC 480p" + +msgid "Force PAL" +msgstr "Tving PAL" + +msgid "Force PAL 480p" +msgstr "Tving PAL 480p" + +msgid "Force PAL50" +msgstr "Tvinge PAL50" + +msgid "Force PAL60" +msgstr "Tvinge PAL60" + +msgid "Formatting" +msgstr "Formaterer" + +#, c-format +msgid "Found %s" +msgstr "Fundet %s" + +msgid "French" +msgstr "Fransk" + +msgid "Full" +msgstr "Fuld" + +msgid "Futuristic Racing" +msgstr "Fremtids racing" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Spille standard" + +msgid "Game ID" +msgstr "Spil ID" + +msgid "Game Options" +msgstr "Spil indstilling" + +#, c-format +msgid "Game Options: %s" +msgstr "Spil indstilling: %s" + +msgid "Game Type" +msgstr "Spil type" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d reportere:" + +msgid "Genre" +msgstr "Genre" + +msgid "German" +msgstr "Tysk" + +msgid "Global Options" +msgstr "Global indstilling" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Guitar" + +msgid "HQ cover" +msgstr "HQ cover" + +msgid "HTTP Response was without a file" +msgstr "HTTP Svar var uden en fil" + +msgid "Health" +msgstr "Helse" + +msgid "Hidden Object" +msgstr "Skjulte objekter" + +msgid "Hide" +msgstr "Skjul" + +msgid "Hide Game:" +msgstr "Skjul spil:" + +msgid "Hockey" +msgstr "Hockey" + +msgid "Hold button B to cancel." +msgstr "Hold B knappen for at annullere." + +msgid "Home Menu" +msgstr "Hjem menu" + +msgid "Homebrew Channel" +msgstr "Homebrew kanal" + +msgid "Hook Type:" +msgstr "Hook type:" + +msgid "Hunting" +msgstr "Jagt" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID misforhold: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Reload: Blocked" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) Fejlede med kode %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Hvis Cfg ikke starter ordentligt\n" +"næste gang, kopier boot.dol.bak over\n" +"boot.dol og prøv igen." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Inkonsistens i LZ77 encoding %x" + +msgid "Infinity Base" +msgstr "Infinity base" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info fil: %s" + +msgid "Initializing Network..." +msgstr "Initialisere netværk..." + +msgid "Install" +msgstr "Installere" + +msgid "Install Date" +msgstr "Installere dato" + +msgid "Install Game" +msgstr "Installere spil" + +msgid "Install game" +msgstr "Installere spil" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Indstallerings FEJL! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Installere spil, vent venligst..." + +msgid "Invalid .dol" +msgstr "Ugyldig .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Ugyldig partition: '%s'" + +msgid "Italian" +msgstr "Italiensk" + +msgid "Japanese" +msgstr "Japansk" + +msgid "Japanese Title" +msgstr "Japanske Titler" + +msgid "Jump" +msgstr "Hop" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Kart Racing" + +msgid "Keyboard" +msgstr "Tastatur" + +msgid "Korean" +msgstr "Koreansk" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "VENSTRE" + +msgid "LOCKED!" +msgstr "LÃ…ST!" + +msgid "Language:" +msgstr "Sprog:" + +msgid "Last Play Date" +msgstr "Sidste spille dato" + +msgid "Launch Methods" +msgstr "Indlæsnings metode" + +msgid "Life Simulation" +msgstr "Livs simulering" + +msgid "Load OK!" +msgstr "Indlæsning OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Loader version: %s" + +msgid "Loading ..." +msgstr "Indlæser..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Indlæser..%s\n" + +msgid "Main" +msgstr "Hoved" + +msgid "Main Menu" +msgstr "Hoved Menu" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Vær sikke pÃ¥ USB port 0 er i brug!\n" +"(Den som er nærmest bunden)" + +msgid "Manage" +msgstr "Administrer" + +msgid "Manage Cheats" +msgstr "Administrere Cheats" + +msgid "Management Sim" +msgstr "Administrere sim" + +msgid "Martial Arts" +msgstr "Krigs kunst" + +msgid "Microphone" +msgstr "Mikrofon" + +msgid "Mighty Plugin" +msgstr "Mighty plugin" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Motorcykelløb" + +msgid "Mounting device, please wait..." +msgstr "Mounting enhed, vent venligst..." + +msgid "Music" +msgstr "Musik" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Musik fil fra mappe: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Musik fil størrelse: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Musik fil: %s" + +msgid "Music: Disabled" +msgstr "Musik: Fra" + +msgid "Music: Enabled" +msgstr "Musik: Til" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Musik: Kigger efter %s filer i: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Musik: Næste fil index at spille: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Musik: Antal af %s filer fundet: %i" + +msgid "Music: musicArray contents: " +msgstr "Musik: Musikopstillings indhold: " + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"NAND emu sti:\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "NAND emu:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTE: CIOS249 før rev10:\n" +"Indlæsning fra SDHC ikke understøttet!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTE: CIOS249 før rev14:\n" +"Mulig fejl #001 er ikke hÃ¥ndteret!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTE: CIOS249 Før rev9:\n" +"Indlæsning af spil fra USB ikke understøttet!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTE: Du bruger CIOS249 rev13:\n" +"For at forlade spillet mÃ¥ du resete eller\n" +"sluk for Wiien før du starter\n" +"et andet spil ellers vil det fryse!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTE: du bruger CIOS249 rev14:\n" +"Det har kendte problemer med dual layer\n" +"spil. det er højt anbefalet at\n" +"du bruger en anden IOS eller revision\n" +"for installation/playback for dette spil." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"Genstart af loader er nødvendig\n" +"for at opdatering virker." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"Genstart af loader er mÃ¥ske nødvendig\n" +"for at indstillingerne virker." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTE: Partition P#%d er type EXTEND men\n" +"indeholder et WBFS filsystem. Det er et\n" +"ugyldigt setup. Tryk %s for at ændre\n" +"partition type fra EXTEND til data." + +msgid "NTFS compression not supported!" +msgstr "NTFS kompression er ikke understøttet!" + +msgid "NTFS encryption not supported!" +msgstr "NTFS kryptering er ikke understøttet!" + +msgid "NTSC-J patch:" +msgstr "NTSC-J patch:" + +msgid "Neek2o Plugin" +msgstr "Neek2o plugin" + +msgid "Network connection established." +msgstr "Netværks forbindelse oprettet" + +msgid "Network error. Can't update gamercards." +msgstr "Netværks fejl. Kan ikke opdater gamercards. " + +msgid "New Themes" +msgstr "Nye temaer" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS Connectivity" + +msgid "No" +msgstr "Nej" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Ingen domæne del i URL '%s'" + +msgid "No games found!" +msgstr "Ingen spil fundet!" + +msgid "No partition selected!" +msgstr "Ingen partition valgt!" + +msgid "No themes found." +msgstr "Ingen tema fundet." + +msgid "No updates found." +msgstr "Ingen opdateringer fundet." + +msgid "NoDisc:" +msgstr "NoDisc:" + +msgid "None found on disc" +msgstr "Fandt ikke noget pÃ¥ disc" + +msgid "Not Found boot.dol!" +msgstr "Fandt ikke boot.dol!" + +msgid "Not Found!" +msgstr "Ikke fundet!" + +msgid "Number of Online Players" +msgstr "Antal online spillere" + +msgid "Number of Players" +msgstr "Antal af spillere" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (cheats):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina cheat manager" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Kode fejl" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Koder fundet." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Ingen koder fundet" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Ude af hukommelse fejl" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Søger koder..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: For mange koder" + +msgid "Off" +msgstr "Fra" + +msgid "Off-Road Racing" +msgstr "Off-Road racing" + +msgid "On" +msgstr "Til" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Online indhold" + +msgid "Online Play" +msgstr "Online spil" + +msgid "Online Players" +msgstr "Online spillere" + +msgid "Online Score List" +msgstr "Online score list" + +msgid "Online Updates" +msgstr "Online opdateringer" + +msgid "Open Sort" +msgstr "Ã…ben sortering " + +msgid "Open Style" +msgstr "Ã…ben Stil" + +msgid "Opening DVD disc..." +msgstr "Ã…bner DVD disc..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Ã…bner partition: %s" + +msgid "Options" +msgstr "Muligheder" + +msgid "Options discarded for this game." +msgstr "Indstillinger afvist for dette spil." + +msgid "Options saved for this game." +msgstr "Indstillinger gemt for dette spil." + +msgid "Out of memory" +msgstr "Ude af hukommelse" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK" + +msgid "Page:" +msgstr "Side:" + +msgid "Partial" +msgstr "Delvis" + +msgid "Partition:" +msgstr "Partition:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partition: %s ikke fundet!" + +msgid "Party" +msgstr "Party" + +msgid "Paused Start" +msgstr "Pause start" + +msgid "Petanque" +msgstr "Petanque" + +msgid "Pinball" +msgstr "Flipperspil" + +msgid "Platformer" +msgstr "Platform" + +msgid "Play Count" +msgstr "Spil tæller" + +msgid "Players" +msgstr "Spillere" + +msgid "Please insert a game disc..." +msgstr "Indsæt venligst en spil disc.." + +msgid "Plugin:" +msgstr "Plugin" + +msgid "Poker" +msgstr "Poker" + +msgid "Portal of Power" +msgstr "Portal af kraft" + +#, c-format +msgid "Press %s button for options." +msgstr "Tryk %s knap for spil indstilling." + +#, c-format +msgid "Press %s button to %s." +msgstr "Tryk %s knap for at %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Tryk %s knap for at godkende koder." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Tryk %s knap for at annuller." + +#, c-format +msgid "Press %s button to change device." +msgstr "Tryk %s knap for at ændre enheder." + +#, c-format +msgid "Press %s button to continue." +msgstr "Tryk %s knap for at fortsætte." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Tryk %s knap for at slette FS." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Tryk %s knap for at dumpe BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Tryk %s knap for at forlade." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Tryk %s knap for at fixe EXTEND/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Tryk %s knap for at formater WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Tryk %s knap for at gÃ¥ tilbage." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Tryk %s knap for at vælge partition." + +#, c-format +msgid "Press %s button to select." +msgstr "Tryk %s knap for at vælge." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Tryk %s knap for at springe over koder." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Pres %s knap for at synkronisere FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Tryk %s for fuld skærm forhÃ¥ndsvisning" + +#, c-format +msgid "Press %s for game options" +msgstr "Tryk %s for spil indstilling" + +#, c-format +msgid "Press %s for global options" +msgstr "Tryk %s For global indstilling" + +#, c-format +msgid "Press %s to discard options" +msgstr "Tryk %s for at afvise indstilling" + +#, c-format +msgid "Press %s to download and update" +msgstr "Tryk %s for at downloade og opdater" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Tryk %s for at downloade dette tema" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Tryk %s for at downloade, %s for at vende tilbage" + +#, c-format +msgid "Press %s to return" +msgstr "Tryk %s for at vende tilbage" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Tryk %s for at gemme global indstilling" + +#, c-format +msgid "Press %s to save options" +msgstr "Tryk %s for at gemme indstilling" + +#, c-format +msgid "Press %s to save selection" +msgstr "Tryk %s for at gemme valgte" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Tryk %s for at vælge filter type" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Tryk %s for at vælge sorterings metode" + +#, c-format +msgid "Press %s to start game" +msgstr "Tryk %s for at starte spil" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Tryk %s for at opdater uden meta.xml" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "Tryk %s/%s for at vælge enhed." + +msgid "Press 1 to skip WBFS mounting" +msgstr "Tryk 1 for at springe indlæsning af WBFS over" + +msgid "Press 2 to reload IOS" +msgstr "Tryk 2 for at reload IOS" + +msgid "Press A to continue without config.txt" +msgstr "Tryk A for at fortsætte uden config.txt" + +msgid "Press A to select device" +msgstr "Tryk A for at vælge enhed" + +msgid "Press B to exit to HBC" +msgstr "Tryk B for at forlade til HBC" + +msgid "Press HOME to reset" +msgstr "Tryk HOME for at nulstille" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Tryk VENSTRE/HØJRE for at vælge IOS" + +msgid "Press any button to continue..." +msgstr "Tryk pÃ¥ en knap for at fortsætte..." + +msgid "Press any button to exit..." +msgstr "Tryk pÃ¥ en knap for at lukke..." + +msgid "Press any button to restart..." +msgstr "Tryk pÃ¥ en knap for at genstarte..." + +msgid "Press any button.\n" +msgstr "Tryk pÃ¥ en knap.\n" + +msgid "Press any button..." +msgstr "Tryk pÃ¥ en knap..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Tryk knap %s for at downloade covers." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Tryk knap %s for at udskyde DVD." + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Profil:" + +#, c-format +msgid "Profile: %s" +msgstr "Profil: %s" + +msgid "Program Updates" +msgstr "Program opdatering" + +msgid "Publisher" +msgstr "Udgiver" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Afslut" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "HØJRE" + +msgid "RPG" +msgstr "RGP" + +msgid "Racing" +msgstr "Racing" + +msgid "Rail Shooter" +msgstr "Rail Shooter" + +#, c-format +msgid "Rated %s" +msgstr "Vurderet %s" + +msgid "Rating" +msgstr "Bedømmelse" + +msgid "Read" +msgstr "Læse" + +msgid "Reading BCA..." +msgstr "Læser BCA..." + +msgid "Reboot" +msgstr "Genstart" + +msgid "Region" +msgstr "Region" + +msgid "Release Date" +msgstr "Frigivelses dato " + +msgid "Release Notes: (short)" +msgstr "Frigivelses noter: (Kort)" + +msgid "Removing game, please wait..." +msgstr "Fjerner spil, vent venligst..." + +msgid "Restarting Wii..." +msgstr "Genstarter Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Vendt tilbage! (ret = %d)" + +msgid "Rhythm" +msgstr "Rytme" + +msgid "Rows:" +msgstr "Rækker:" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Kører benchmark, vent venligst" + +msgid "S. Chinese" +msgstr "S. Kinesisk" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC Kort" + +msgid "SUCCESS!" +msgstr "LYKKEDES!" + +msgid "Save .gct" +msgstr "Gem .gct" + +msgid "Save Debug" +msgstr "Gem debug" + +msgid "Save Settings" +msgstr "Gem indstillinger" + +msgid "Save debug.log" +msgstr "Gem debug.log" + +msgid "Savegame not found.\n" +msgstr "Fandt ikke savegame.\n" + +msgid "Savegame:" +msgstr "Savegame:" + +msgid "Saving Settings... " +msgstr "Gemmer indstillinger..." + +msgid "Saving cheats..." +msgstr "Gemmer cheats..." + +msgid "Saving gamelist.txt ... " +msgstr "Gemmer gamelist.txt ..." + +msgid "Saving settings..." +msgstr "Gemmer indstillinger..." + +msgid "Saving:" +msgstr "Gemmer:" + +#, c-format +msgid "Saving: %s" +msgstr "Gem: %s" + +msgid "Screenshot:" +msgstr "Skærmprint:" + +msgid "Scroll:" +msgstr "Rul:" + +msgid "Search" +msgstr "Søg" + +msgid "Search for:" +msgstr "Søg efter:" + +msgid "Select Alternative .dol:" +msgstr "Vælg alternetive .dol:" + +msgid "Select WBFS device:" +msgstr "Vælg WBFS enhed:" + +msgid "Select a different partition" +msgstr "Vælg en anden partition" + +msgid "Select a partition" +msgstr "Vælg en partition" + +msgid "Select all" +msgstr "Vælg alle" + +msgid "Select device:" +msgstr "Vælg enhed:" + +msgid "Select the game you want to boot" +msgstr "Vælg det spil du vil indlæse" + +msgid "Selected Game" +msgstr "Valgt spil" + +msgid "Settings" +msgstr "Indstillinger" + +msgid "Shooter" +msgstr "Shooter" + +msgid "Show All" +msgstr "Vis alle" + +msgid "Show cIOS info" +msgstr "Vis cIOS info" + +msgid "Shutdown" +msgstr "Sluk for Wiien" + +msgid "Side:" +msgstr "Side:" + +msgid "Sim Racing" +msgstr "Sim Racing" + +msgid "Simulation" +msgstr "Simulering" + +msgid "Size(GB) Type Mount Used" +msgstr "Størrelse(GB) Type Mount Brugt" + +#, c-format +msgid "Size: %d bytes" +msgstr "Str: %d bytes" + +msgid "Skateboard" +msgstr "Skateboard" + +msgid "Skateboarding" +msgstr "Skateboarding" + +msgid "Skiing" +msgstr "Skisport" + +msgid "Snowboarding" +msgstr "Snowboarding" + +msgid "Soccer" +msgstr "Fodbold" + +msgid "Sort" +msgstr "Sorter" + +msgid "Sort Games" +msgstr "Sorter spil" + +msgid "Sort Order:" +msgstr "Sorterings orden:" + +msgid "Sort Type:" +msgstr "Sorterings type:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Sorter: %s-%s" + +msgid "Spanish" +msgstr "Spansk" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Start" + +msgid "Start Game" +msgstr "Start spil" + +msgid "Start this game?" +msgstr "Start dette spil?" + +msgid "Stealth Action" +msgstr "Stealth action" + +msgid "Stopping DVD..." +msgstr "Stopper DVD..." + +msgid "Strategy" +msgstr "Strategi" + +msgid "Style" +msgstr "Stil" + +msgid "Style:" +msgstr "Stil:" + +msgid "Surfing" +msgstr "Surfing" + +msgid "Survival Horror" +msgstr "Overlevelse horror" + +msgid "Sync FAT free space info?" +msgstr "Synkroniser FAT fri plads info?" + +msgid "Synchronizing, please wait." +msgstr "Synkronisere, vent venligst." + +msgid "Synopsis" +msgstr "Synopsis" + +msgid "Synopsis Length" +msgstr "Længde pÃ¥ synopsis" + +msgid "System" +msgstr "System" + +msgid "System Def." +msgstr "System Std." + +msgid "T. Chinese" +msgstr "T. Kinesisk" + +msgid "Table Tennis" +msgstr "Bordtennis" + +msgid "Tennis" +msgstr "Tennis" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Tema Info" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Tema er mÃ¥ske for stor.\n" +"Genstart af Cfg anbefalet.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temaer leveret af wii.spiff360.com" + +msgid "Themes to download" +msgstr "Temaer til download" + +msgid "Themes with updates" +msgstr "Temaer med opdateringer" + +msgid "Title" +msgstr "Titel" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "For mange cheats! (%d)" + +msgid "Too many code lines!" +msgstr "For mange code linjer!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "For mange fragmenter! %d" + +msgid "Total Body Tracking" +msgstr "Total Body Tracking" + +msgid "Train Simulation" +msgstr "Tog simulator" + +msgid "Trivia" +msgstr "Trivia" + +msgid "Truck Racing" +msgstr "Lastbilløb" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Prøver (url#%d) ..." + +msgid "Turntable" +msgstr "Drejeskive" + +msgid "UNUSED" +msgstr "UBRUGT" + +msgid "UP" +msgstr "OP" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' starter ikke med 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' har ingen sti dele" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko fundet. Debugging er til." + +msgid "USB Mass Storage Device" +msgstr "USB Lager enhed" + +msgid "Unknown" +msgstr "Ukendt" + +msgid "Unknown syntax!" +msgstr "Ukendt syntax!" + +msgid "Unplayed" +msgstr "Uspillet" + +msgid "Unplayed Games" +msgstr "Uspillet spil" + +msgid "Update themes" +msgstr "Opdater temaer" + +msgid "Updates" +msgstr "Opdateringer" + +msgid "Updating database." +msgstr "Opdaterer database." + +msgid "Updating devolution" +msgstr "Opdaterer devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Brugt: %.1fGB Fri: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Bruger gemt alternative .dol:" + +msgid "VC-Arcade" +msgstr "VC-Arkade" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-N64" + +msgid "VC-NES" +msgstr "VC-NES" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC-SNES" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "Version" + +msgid "Video Patch:" +msgstr "Video patch:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "udseende" + +msgid "Virtual Pet" +msgstr "Virtual kæledyr" + +msgid "Vitality Sensor" +msgstr "Vitalitet sensor" + +msgid "Volleyball" +msgstr "Volleyball" + +msgid "WARNING:" +msgstr "ADVARSEL:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB fri af %.1fGB" + +msgid "Watercraft Racing" +msgstr "Watercraft Racing" + +msgid "Wheel" +msgstr "Rat" + +msgid "Wide Screen:" +msgstr "Bredformat:" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Wii kanal" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Brydning" + +msgid "Write Playlog:" +msgstr "Skriv playlog:" + +#, c-format +msgid "Writing to %s" +msgstr "Skriver til %s" + +#, c-format +msgid "Writing: %s" +msgstr "Skriver: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Forkert Str: %d (%d)" + +msgid "Yes" +msgstr "Ja" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Du kan ogsÃ¥ prøve at trække ud\n" +"og tilslutte enheden igen,\n" +"eller vente lidt længere" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ ÆNDRET ]" + +msgid "[ FOUND ]" +msgstr "[ FUNDET ]" + +msgid "[ SAVED ]" +msgstr "[ GEMT ]" + +msgid "[HOME]" +msgstr "[HJEM]" + +msgid "[No HQ]" +msgstr "[Ingen HQ]" + +msgid "[USED]" +msgstr "[BRUGT]" + +msgid "[default]" +msgstr "[standard]" + +msgid "[saved]" +msgstr "[Gemt]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "dÃ¥rlig wbfs fil: %s" + +msgid "calculating space, please wait..." +msgstr "Udregner plads, vent venligst..." + +msgid "delete" +msgstr "Slet" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "Kassere" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "Domain %s kunne ikke løses" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "Fejlede at læse: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "For %d spillere" + +msgid "for 1 player" +msgstr "For 1 spiller" + +msgid "format" +msgstr "Format" + +msgid "free" +msgstr "Fri" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "Lineær læse hastighed: %.2f mb/s" + +msgid "linear..." +msgstr "Lineær..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin størrelse: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "Musik fil er for stor (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 eller music.mod ikke fundet!" + +msgid "no file" +msgstr "Ingen fil" + +msgid "none" +msgstr "Ingen" + +msgid "or format a WBFS partition." +msgstr "Eller formater en WBFS partition." + +msgid "page" +msgstr "Side" + +msgid "parse error" +msgstr "Parse fejl" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "Tilfældig læse hastighed: %.2f mb/s" + +msgid "random..." +msgstr "Tilfældig..." + +msgid "reset" +msgstr "Nulstil" + +msgid "revert" +msgstr "revert" + +msgid "save" +msgstr "Gem" + +#, c-format +msgid "split error: %s" +msgstr "split fejl: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw Spil tavle" + +msgid "unable to open wii disc" +msgstr "Kunne ikke Ã¥bne Wii disc" + +#, c-format +msgid "used: %p - %p" +msgstr "Brugt: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Indlæser Wii spil, vent venligst..." + +#~ msgid "Console Def." +#~ msgstr "Konsol Def." + +#~ msgid "Download titles.txt" +#~ msgstr "Download titles.txt" + +#~ msgid "Front" +#~ msgstr "Front" + +#~ msgid "Loading previous game list..." +#~ msgstr "Indlæser tidliger spil liste..." + +#~ msgid "Remove Game" +#~ msgstr "Fjern spil" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "Opdater WiiTDB spil database" + +#~ msgid "Update titles.txt" +#~ msgstr "Opdater titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "WiiTDB spil database" diff --git a/Languages/ES.lang b/Languages/ES.lang new file mode 100644 index 0000000..8fb304d --- /dev/null +++ b/Languages/ES.lang @@ -0,0 +1,2360 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader ES Translation\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-24 14:18-0500\n" +"Last-Translator: TyRaNtM \n" +"Language-Team: SPANISH \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB libre de %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% de %.2fGB (%c) TEA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB copiado en %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d disponible" + +#, c-format +msgid "%d more notes" +msgstr "%d más notas" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Dividir: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, por favor espera..." + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d segundos de tiempo de espera)" + +msgid "(This can take a couple of minutes)" +msgstr "(Esto puede tomar un par de minutos)" + +msgid ".dol too small" +msgstr ".dol es muy pequeño" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "Disparos en Primera Persona" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "portadas 3D" + +msgid "3rd-person Shooter" +msgstr "Disparos en Tercera Persona" + +msgid "< ASC >" +msgstr "< ASC >" + +msgid "< DESC >" +msgstr "< DESC >" + +msgid "< DOWNLOAD >" +msgstr "< DESCARGAR >" + +msgid "About" +msgstr "Créditos" + +msgid "Action" +msgstr "Acción" + +msgid "Additional config:" +msgstr "Configuración Adicional:" + +msgid "Admin Lock:" +msgstr "Bloqueo Admin:" + +msgid "Admin Unlock" +msgstr "Desbloqueo Admin" + +msgid "Adult" +msgstr "Adulto" + +msgid "Adventure" +msgstr "Aventura" + +msgid "All" +msgstr "Todos" + +msgid "All Channels" +msgstr "Todos los Canales" + +msgid "All Games" +msgstr "Todos los Juegos" + +msgid "All themes up to date." +msgstr "Todos los temas al día." + +msgid "Alt Button Cfg:" +msgstr "Botón Alt. Cfg:" + +msgid "Alt dol:" +msgstr ".dol Alternativo: " + +msgid "Alternative .dol:" +msgstr "dol Alternativo:" + +msgid "Anti 002 Fix:" +msgstr "Arreglo Anti 002:" + +#, c-format +msgid "App. Path: %s" +msgstr "Ruta Aplicación: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"¿Estás seguro que quieres %s\n" +"esta partición?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +" Estás seguro que quieres ARREGLAR\n" +"esta partición?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"¿Estás seguro que deseas eliminar este\n" +"juego?" + +msgid "Ascending" +msgstr "Ascendente" + +msgid "Auto" +msgstr "Automático" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto-inicio del juego: %.6s no encontrado!" + +msgid "Available Updates" +msgstr "Actualizaciones Disponibles" + +msgid "Back" +msgstr "Atrás" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Béisbol" + +msgid "Basic" +msgstr "Básico" + +msgid "Basketball" +msgstr "Baloncesto" + +msgid "Bike Racing" +msgstr "Carrera de Motos" + +msgid "Billiards" +msgstr "Billar" + +msgid "Block IOS Reload:" +msgstr "Bloq. Recarga IOS:" + +msgid "Board Game" +msgstr "Juego de Mesa" + +msgid "Boot Disc" +msgstr "Cargar Disco" + +msgid "Boot disc" +msgstr "Cargar disco" + +msgid "Booting game, please wait..." +msgstr "Iniciando juego, por favor espera.." + +msgid "Bowling" +msgstr "Bolos" + +msgid "Boxing" +msgstr "Boxeo" + +msgid "Business Sim" +msgstr "Simulador de Negocios" + +#, c-format +msgid "CFG base: %s" +msgstr "Base de CFG: %s" + +msgid "Camera" +msgstr "Cámara" + +msgid "Can not dump GC savegames.\n" +msgstr "No se pueden extraer los saves de GC.\n" + +msgid "Can't install Wii games!" +msgstr "No se pueden instalar juegos de Wii!" + +msgid "Cancelled." +msgstr "Cancelado." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "No se puede crear directorio: %s" + +msgid "Cards" +msgstr "Tarjetas" + +msgid "Channel" +msgstr "Canal" + +msgid "Cheat Codes:" +msgstr "Trucos: " + +msgid "Cheats: " +msgstr "Trucos: " + +msgid "Check For Updates" +msgstr "Buscar Actualizaciones" + +msgid "Checking for themes..." +msgstr "Comprobando los temas.." + +msgid "Checking for updates..." +msgstr "Comprobando actualizaciones..." + +msgid "Chess" +msgstr "Ajedrez" + +msgid "Choose a sorting method" +msgstr "Elige un método de clasificación" + +msgid "Classic" +msgstr "Clásico" + +msgid "Classic Controller" +msgstr "Control Clásico" + +msgid "Clear" +msgstr "Limpio" + +msgid "Clear Patches:" +msgstr "Borrar Parches:" + +msgid "Coaching" +msgstr "Entrenamiento" + +msgid "Compare Type" +msgstr "Comparar Tipo" + +msgid "Compilation" +msgstr "Compilación" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Terminado. Tamaño: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Error de configuración, MAX_DNS_ENTRIES alcanzado mientras que la lista está vacía" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Error de conexión de net_read() Error: %i" + +msgid "Console" +msgstr "Consola" + +msgid "Construction Sim" +msgstr "Simulador de Construcción" + +msgid "Contains" +msgstr "Contiene" + +msgid "Controller" +msgstr "Control" + +msgid "Cooking" +msgstr "Cocina" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "No se pudo inicializar el módulo DIP! (ret = %d)" + +msgid "Country Fix:" +msgstr "Arreglo de País: " + +msgid "Cover" +msgstr "Portada" + +msgid "Cover Image:" +msgstr "Imagen de Portada:" + +msgid "Cover Style" +msgstr "Estílo de Portada" + +msgid "Covers Available" +msgstr "Portadas Disponibles" + +msgid "Cover~~Back" +msgstr "Portada~~Atrás" + +msgid "Cover~~Front" +msgstr "Portada~~Frente" + +msgid "Create" +msgstr "Crear" + +msgid "Creator" +msgstr "Creador" + +msgid "Cricket" +msgstr "Cricket" + +#, c-format +msgid "Current Version: %s" +msgstr "Versión Actual: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"El Custom IOS %d no se pudo encontrar!\n" +"Por favor instalalo." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "El Custom IOS %d no se pudo cargar! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"El CIOS %d está vacío (stub)!\n" +"Por favor reinstalalo." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s Cargado OK" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "portada de DISCO" + +msgid "DOWN" +msgstr "ABAJO" + +msgid "Dance" +msgstr "Baile" + +msgid "Dance Pad" +msgstr "Alfombra de Baile" + +msgid "Darts" +msgstr "Dardos" + +msgid "Database update successful." +msgstr "Base de Datos exitosamente actualizada." + +msgid "Debug" +msgstr "Depuración" + +msgid "Delete Game" +msgstr "Borrar Juego" + +msgid "Deleting" +msgstr "Eliminando" + +msgid "Descending" +msgstr "Descendente" + +msgid "Developer" +msgstr "Desarrollador" + +msgid "Device is not responding!" +msgstr "El dispositivo no está respondiendo!" + +msgid "Device:" +msgstr "Dispositivo:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolution solo acepta juegos limpios!\n" + +msgid "Devolution:" +msgstr "Devolution" + +msgid "Disc" +msgstr "Disco" + +msgid "Disc (Ask)" +msgstr "Disco (Preguntar)" + +msgid "Done." +msgstr "Hecho." + +msgid "Download .txt" +msgstr "Descargar .txt" + +msgid "Download All Covers" +msgstr "Descargar Todas las Portadas" + +msgid "Download All Missing Covers" +msgstr "Descargar las Portadas Faltantes" + +msgid "Download Missing Covers" +msgstr "Descargar Portadas Faltantes" + +msgid "Download Plugins" +msgstr "Bajar Complementos" + +msgid "Download Themes" +msgstr "Descargar Temas" + +msgid "Download complete." +msgstr "Descarga terminada." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Error de descarga en la gamercard #%d." + +msgid "Download game information" +msgstr "Bajar información del juego" + +msgid "Downloadable Content" +msgstr "Contenido Descargable" + +msgid "Downloading ALL MISSING covers" +msgstr "Descargar las Portadas FALTANTES" + +msgid "Downloading ALL covers" +msgstr "Descargar TODAS las Portadas" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Descargar TODAS las Portadas para %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Descargar las Portadas FALTANTES para %.6s" + +msgid "Downloading Theme previews..." +msgstr "Bajando vistas previas de Temas..." + +msgid "Downloading cheats..." +msgstr "Descargando trucos..." + +msgid "Downloading database." +msgstr "Descargando Base de Datos." + +msgid "Downloading devolution." +msgstr "Bajando devolution." + +msgid "Downloading mighty plugin." +msgstr "Bajando complemento mighty." + +msgid "Downloading neek2o plugin." +msgstr "Bajando complemento neek2o" + +msgid "Downloading titles.txt ..." +msgstr "Descargando titles.txt ..." + +msgid "Downloading translation file." +msgstr "Bajando archivo de traducción." + +msgid "Downloading unifont." +msgstr "Bajando unifont." + +#, c-format +msgid "Downloading: %s" +msgstr "Descargando: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Descargando: %s de %s" + +msgid "Downloads" +msgstr "Descargas" + +msgid "Drawing" +msgstr "Dibujo" + +msgid "Drums" +msgstr "Tambores" + +msgid "Dump savegame" +msgstr "Extraer archivo de guardado" + +msgid "Duplicate ID3" +msgstr "Duplicar ID3" + +msgid "Dutch" +msgstr "Holandés" + +msgid "ERROR" +msgstr "ERROR" + +msgid "ERROR creating file" +msgstr "ERROR creando archivo" + +msgid "ERROR game opt" +msgstr "ERROR del juego elegido" + +msgid "ERROR reading BCA!" +msgstr "ERROR leyendo BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "ERROR removiendo %s" + +msgid "ERROR writing BCA!" +msgstr "ERROR escribiendo BCA!" + +msgid "ERROR!" +msgstr "ERROR!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ERROR! (ret = %d)" + +msgid "ERROR:" +msgstr "ERROR:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ERROR: %s no es accesible" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ERROR: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ERROR: CIOS222/223 v4 requerida para BCA" + +msgid "ERROR: Cache Close" +msgstr "ERROR: Cache Cerrada" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ERROR: No se puede abrir el juego! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ERROR: El juego ya está instalado!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ERROR: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ERROR: ID del Juego Inválida" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ERROR: Cargando módulo EHC! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERROR: Escritura NTFS desactivada!\n" +"(establece ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ERROR: No hay particiones encontradas! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ERROR: No es un disco de Wii!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ERROR: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ERROR: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ERROR: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ERROR: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ERROR: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ERROR: Configurando modo SD" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ERROR: Inicializando USB! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "ERROR: WBFS no está montado!" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"cIOS223v4, cIOS224v5 o superior requerido\n" +"para iniciar juegos de una partición FAT!\n" +"Actualiza IOS249 o elige un IOS diferente." + +msgid "ERROR: cache: out of memory" +msgstr "ERROR: cache: sin memoria" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ERROR: creando: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ERROR: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "ERROR: memoria superpuesta!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERROR: multiples particiones wbfs\n" +"suportadas solamente con IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ERROR: no hay suficiente espacio libre!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ERROR: ntfs no ha sido limpiamente desmontado" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ERROR: abriendo %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ERROR: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ERROR: Configurando fragmentos %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "ERROR: el disco o la partición %s/ no está conectada!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ERROR: escribiendo %s (%d)." + +msgid "EXTEND" +msgstr "EXTENDIDO" + +msgid "Educational" +msgstr "Educacional" + +msgid "Ejecting DVD..." +msgstr "Expulsando DVD..." + +msgid "English" +msgstr "Inglés" + +msgid "English Title" +msgstr "Título en Inglés" + +msgid "Enter Code: " +msgstr "Ingresar Código: " + +msgid "Error GRRLIB init" +msgstr "Error Inicializando GRRLIB" + +msgid "Error Initializing Network." +msgstr "Error Inicializando la Red." + +msgid "Error adding disc!" +msgstr "Error agregando disco!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Error asignando buffer: %d" + +msgid "Error creating directory..." +msgstr "Error creando directorio..." + +msgid "Error discarding options!" +msgstr "Error descartando opciones!" + +msgid "Error downloading theme preview..." +msgstr "Error descargando vistas previas de temas..." + +msgid "Error downloading theme..." +msgstr "Error descargando tema..." + +msgid "Error downloading themes..." +msgstr "Error descargando temas" + +msgid "Error downloading update..." +msgstr "Error descargando actualización..." + +msgid "Error downloading updates..." +msgstr "Error descargando actualizaciones..." + +msgid "Error downloading." +msgstr "Error descargando." + +msgid "Error establishing connection" +msgstr "Error estableciendo conexión" + +msgid "Error extracting theme..." +msgstr "Error al extraer el tema..." + +msgid "Error opening database, update did not complete." +msgstr "Error abriendo base de datos, actualización no ha sido completada." + +#, c-format +msgid "Error opening: %s" +msgstr "Error abriendo: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Error jugando %s" + +msgid "Error reading .dol" +msgstr "Error leyendo .dol" + +msgid "Error reading dol header" +msgstr "Error leyendo la cabecera del dol" + +#, c-format +msgid "Error saving %s" +msgstr "Error guardando %s" + +msgid "Error saving options!" +msgstr "Error guardando opciones!" + +msgid "Error saving settings!" +msgstr "Error guardando configuraciones!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Error almacenando registro del juego.\n" +"Inicia desde el menú de Wii para arreglarlo." + +msgid "Error: Invalid PNG image!" +msgstr "Error: Imagen PNG inválida!" + +msgid "Error: no URL." +msgstr "Error: no hay URL." + +msgid "Error: no data." +msgstr "Error: no hay datos." + +msgid "Exercise" +msgstr "Ejercicio" + +msgid "Exit" +msgstr "Salir" + +#, c-format +msgid "Extracting: %s" +msgstr "Extrayendo: %s" + +msgid "FAIL" +msgstr "FALLÓ" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC ERROR %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "LLENAR %d" + +msgid "FLAT cover" +msgstr "portada PLANA" + +msgid "FULL cover" +msgstr "portada COMPLETA" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: No" + +msgid "Fav: On" +msgstr "Fav: Si" + +msgid "Favorite" +msgstr "Favorito" + +msgid "Favorite Games" +msgstr "Juegos Favoritos" + +msgid "Favorite:" +msgstr "Favorito:" + +msgid "Favorites:" +msgstr "Favoritos:" + +msgid "Fighting" +msgstr "Pelea" + +#, c-format +msgid "File not found! %s" +msgstr "Archivo no encontrado! %s" + +msgid "Filter" +msgstr "Filtro" + +msgid "Filter Games" +msgstr "Filtrar Juegos" + +msgid "Filter by Controller" +msgstr "Filtrar por Control" + +msgid "Filter by Game Type" +msgstr "Filtrar por Tipo de Juego" + +msgid "Filter by Genre" +msgstr "Filtrar por Género" + +msgid "Filter by Online Features" +msgstr "Filtrar por Características Online" + +msgid "Filter:" +msgstr "Filtro:" + +msgid "Fishing" +msgstr "Pesca" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "Arreglando particiones EXTENDIDAS..." + +msgid "Flight Sim" +msgstr "Simulador de Vuelo" + +msgid "Football" +msgstr "Fútbol" + +msgid "Force Devolution:" +msgstr "Forzar Devolution:" + +msgid "Force NTSC" +msgstr "Forzar NTSC" + +msgid "Force NTSC 480p" +msgstr "Forzar NTSC 480p" + +msgid "Force PAL" +msgstr "Forzar PAL" + +msgid "Force PAL 480p" +msgstr "Forzar PAL 480p" + +msgid "Force PAL50" +msgstr "Forzar PAL50" + +msgid "Force PAL60" +msgstr "Forzar PAL60" + +msgid "Formatting" +msgstr "Formateando" + +#, c-format +msgid "Found %s" +msgstr "Encontrados %s" + +msgid "French" +msgstr "Francés" + +msgid "Full" +msgstr "Completo" + +msgid "Futuristic Racing" +msgstr "Carreras Futurísticas" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Juego por Defecto" + +msgid "Game ID" +msgstr "ID de Juego" + +msgid "Game Options" +msgstr "Opciones de Juego" + +#, c-format +msgid "Game Options: %s" +msgstr "Opciones de Juego: %s" + +msgid "Game Type" +msgstr "Tipo de Juego" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "GameCube" + +msgid "Gamer Card:" +msgstr "Tarjeta de Jugador:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d reportada: %.*s" + +msgid "Genre" +msgstr "Género" + +msgid "German" +msgstr "Alemán" + +msgid "Global Options" +msgstr "Opciones Globales" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Guitarra" + +msgid "HQ cover" +msgstr "Portada HQ" + +msgid "HTTP Response was without a file" +msgstr "Respuesta HTTP fué sin un archivo" + +msgid "Health" +msgstr "Salud" + +msgid "Hidden Object" +msgstr "Objeto Oculto" + +msgid "Hide" +msgstr "Ocultar" + +msgid "Hide Game:" +msgstr "Ocultar Juego:" + +msgid "Hockey" +msgstr "Hockey" + +msgid "Hold button B to cancel." +msgstr "Mantener el botón B para cancelar." + +msgid "Home Menu" +msgstr "Menú de Inicio" + +msgid "Homebrew Channel" +msgstr "Canal Homebrew" + +msgid "Hook Type:" +msgstr "Tipo de Hook:" + +msgid "Hunting" +msgstr "Caza" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID desajustada: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "Recargar IOS: Bloqueada" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) falló con código %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Si Cfg no se inicia correctamente la\n" +"próxima vez, copia boot.dol.bak\n" +"sobre boot.dol e intenta de nuevo." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Incoherencia en la codificación LZ77 %x" + +msgid "Infinity Base" +msgstr "Base Infinita" + +msgid "Info" +msgstr "Información" + +#, c-format +msgid "Info file: %s" +msgstr "Información del archivo: %s" + +msgid "Initializing Network..." +msgstr "Inicializando la Red..." + +msgid "Install" +msgstr "Instalar" + +msgid "Install Date" +msgstr "Fecha Instalación" + +msgid "Install Game" +msgstr "Instalar Juego" + +msgid "Install game" +msgstr "Instalar juego" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "ERROR de instalación! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Instalando juego, por favor espera..." + +msgid "Invalid .dol" +msgstr ".dol inválido" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Partición inválida: '%s'" + +msgid "Italian" +msgstr "Italiano" + +msgid "Japanese" +msgstr "Japonés" + +msgid "Japanese Title" +msgstr "Título en Japonés" + +msgid "Jump" +msgstr "Salto" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Carreras de Karts" + +msgid "Keyboard" +msgstr "Teclado" + +msgid "Korean" +msgstr "Coreano" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "IZQUIERDA" + +msgid "LOCKED!" +msgstr "CERRADO!" + +msgid "Language:" +msgstr "Idioma:" + +msgid "Last Play Date" +msgstr "Fecha Último Juego" + +msgid "Launch Methods" +msgstr "Métodos de Carga" + +msgid "Life Simulation" +msgstr "Simulador de Vida" + +msgid "Load OK!" +msgstr "Cargado OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Versión del Cargador: %s" + +msgid "Loading ..." +msgstr "Cargando ..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Cargando..%s\n" + +msgid "Main" +msgstr "Principal" + +msgid "Main Menu" +msgstr "Menú Principal" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Asegurate que el puerto 0 del USB sea el usado!\n" +"(El más cercano a la orilla)" + +msgid "Manage" +msgstr "Gestionar" + +msgid "Manage Cheats" +msgstr "Gestionar Trucos" + +msgid "Management Sim" +msgstr "Simulador de Administración" + +msgid "Martial Arts" +msgstr "Artes Marciales" + +msgid "Microphone" +msgstr "Micrófono" + +msgid "Mighty Plugin" +msgstr "Complemento Mighty" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Carreras de Motocicletas" + +msgid "Mounting device, please wait..." +msgstr "Montando dispositivo, espera..." + +msgid "Music" +msgstr "Música" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Archivo de música del directorio: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Tamaño del archivo de música: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Archivo de música: %s" + +msgid "Music: Disabled" +msgstr "Música: Desactivado" + +msgid "Music: Enabled" +msgstr "Músic: Activado" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Música: Buscando %s archivos en: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Música: Siguiente archivo índice para escuchar: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Música: Número de %s archivos encontrados: %i" + +msgid "Music: musicArray contents: " +msgstr "Música: contenidos de musicArray: " + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" + +msgid "NAND Emu:" +msgstr "Emulación de NAND:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTE: CIOS249 antes de rev10:\n" +"Cargar juegos desde una SDHC no está soportado!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTE: CIOS249 antes de rev14:\n" +"Posible error #001 no es manipulable!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTE: CIOS249 antes de rev9:\n" +"Cargar juegos desde un USB no está soportado!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTE: Estás usando CIOS249 rev13:\n" +"Para salir del juego debes resetear o\n" +"apagar la Wii antes de que inicies\n" +"otro juego o se congelerá aquí!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTE: Estás usando CIOS249 rev14:\n" +"Este tiene problemas con los juegos que usan\n" +"discos doble capa. Es recomendable que\n" +"tengas que usar una revisión de IOS distinta\n" +"para la instalación/carga de este juego." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTA: Se requiere reiniciar para que\n" +"la actualización tome efecto.\n" + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTE: partición P#%d es tipo EXTENDIDA pero\n" +"contiene un sistema de archivos WBFS. Esto es una\n" +"configuración no válida. Presiona %s para cambiar el\n" +"tipo de partición EXTENDIDA a la de datos." + +msgid "NTFS compression not supported!" +msgstr "Compresión NTFS no soportada!" + +msgid "NTFS encryption not supported!" +msgstr "Cifrado NTFS no soportado!" + +msgid "NTSC-J patch:" +msgstr "Parche NTSC-J:" + +msgid "Neek2o Plugin" +msgstr "Complemento Neek2o" + +msgid "Network connection established." +msgstr "Conexión a la red establecida." + +msgid "Network error. Can't update gamercards." +msgstr "Error de la Red. No se pueden actualizar las gamercards." + +msgid "New Themes" +msgstr "Nuevos Temas" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Conectividad con Nintendo DS" + +msgid "No" +msgstr "No" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Ninguna parte de dominio en la URL '%s'" + +msgid "No games found!" +msgstr "No hay juegos!" + +msgid "No partition selected!" +msgstr "No hay particiones seleccionadas!" + +msgid "No themes found." +msgstr "No hay temas encontrados." + +msgid "No updates found." +msgstr "No hay actualizaciones encontradas." + +msgid "NoDisc:" +msgstr "NoDisc:" + +msgid "None found on disc" +msgstr "Nada encontrado en el disco" + +msgid "Not Found boot.dol!" +msgstr "No se encontró boot.dol!" + +msgid "Not Found!" +msgstr "No Encontrado!" + +msgid "Number of Online Players" +msgstr "N. de Jugadores Online" + +msgid "Number of Players" +msgstr "Número de Jugadores" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (trucos):" + +msgid "Ocarina Cheat Manager" +msgstr "Gestionador de Trucos Ocarina" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Error de Código" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Códigos encontrados." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: No hay códigos encontrados" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Error por Falta de Memoria" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Buscando códigos..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Hay muchos códigos" + +msgid "Off" +msgstr "Desactivado" + +msgid "Off-Road Racing" +msgstr "Carreras Todo Terreno" + +msgid "On" +msgstr "Activado" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Contenido Online" + +msgid "Online Play" +msgstr "Juego Online" + +msgid "Online Players" +msgstr "Jugadores Online" + +msgid "Online Score List" +msgstr "Lista de Puntajes Online" + +msgid "Online Updates" +msgstr "Actulizaciones Online" + +msgid "Open Sort" +msgstr "Abrir Clase" + +msgid "Open Style" +msgstr "Abrir Estílo" + +msgid "Opening DVD disc..." +msgstr "Abriendo disco DVD..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Abriendo partición: %s" + +msgid "Options" +msgstr "Opciones" + +msgid "Options discarded for this game." +msgstr "Opciones descartadas para este juego." + +msgid "Options saved for this game." +msgstr "Opciones guardadas para este juego." + +msgid "Out of memory" +msgstr "Sin memoria" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "J1 %3u%% | J2 %3u%% | J3 %3u%% | J4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK:" + +msgid "Page:" +msgstr "Página:" + +msgid "Partial" +msgstr "" + +msgid "Partition:" +msgstr "Partición:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partición: %s no encontrada!" + +msgid "Party" +msgstr "Fiesta" + +msgid "Paused Start" +msgstr "Inicio Pausado" + +msgid "Petanque" +msgstr "Petanca" + +msgid "Pinball" +msgstr "Flipper" + +msgid "Platformer" +msgstr "Plataforma" + +msgid "Play Count" +msgstr "Conteo de Juego" + +msgid "Players" +msgstr "Jugadores" + +msgid "Please insert a game disc..." +msgstr "Por favor inserte un disco de juego..." + +msgid "Plugin:" +msgstr "Complemento:" + +msgid "Poker" +msgstr "Póquer" + +msgid "Portal of Power" +msgstr "Portal de Poder" + +#, c-format +msgid "Press %s button for options." +msgstr "Presiona el botón %s para las opciones." + +#, c-format +msgid "Press %s button to %s." +msgstr "Presiona el botón %s para %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Presiona el botón %s para aplicar códigos." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Presiona el botón %s para cancelar." + +#, c-format +msgid "Press %s button to change device." +msgstr "Presiona el botón %s para cambiar dispositivo." + +#, c-format +msgid "Press %s button to continue." +msgstr "Presiona el botón %s para continuar." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Presiona el botón %s para borrar FS." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Presiona el botón %s para extraer BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Presiona el botón %s para salir." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Presiona el botón %s para arreglar partición EXTENDIDA/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Presiona el botón %s para formatear a WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Presiona el botón %s para ir atrás." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Presiona el botón %s para seleccionar una partición." + +#, c-format +msgid "Press %s button to select." +msgstr "Presiona el botón %s para elegir." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Presiona el botón %s para saltar códigos." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Presiona el botón %s para sincronizar FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Presiona %s para ver en pantalla completa" + +#, c-format +msgid "Press %s for game options" +msgstr "Presiona %s para las opciones de juego" + +#, c-format +msgid "Press %s for global options" +msgstr "Presiona %s para las opciones globales" + +#, c-format +msgid "Press %s to discard options" +msgstr "Presiona %s para descartar opciones" + +#, c-format +msgid "Press %s to download and update" +msgstr "Presiona %s para descargar y actualizar" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Presiona %s para descargar este tema" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Presiona %s para descargar, %s para regresar" + +#, c-format +msgid "Press %s to return" +msgstr "Presiona el botón %s para regresar" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Presiona %s para guardar cfg. globales" + +#, c-format +msgid "Press %s to save options" +msgstr "Presiona %s para guardar opciones" + +#, c-format +msgid "Press %s to save selection" +msgstr "Presiona %s para guardar selección" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Presiona %s para seleccionar filtro" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Presiona %s para elegir clasificación" + +#, c-format +msgid "Press %s to start game" +msgstr "Presiona %s para iniciar el juego" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Presiona %s para actualizar sin meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Presiona IZQUIERDA/DERECHA para seleccionar dispositivo." + +msgid "Press 1 to skip WBFS mounting" +msgstr "Presiona 1 para saltar el montaje de WBFS" + +msgid "Press 2 to reload IOS" +msgstr "Presiona 2 para recargar IOS" + +msgid "Press A to continue without config.txt" +msgstr "Presiona A para continuar sin config.txt" + +msgid "Press A to select device" +msgstr "Presiona A para elegir dispositivo" + +msgid "Press B to exit to HBC" +msgstr "Presiona B para salir al HBC" + +msgid "Press HOME to reset" +msgstr "Presiona HOME para reiniciar" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Presiona IZQUIERDA/DERECHA para seleccionar IOS" + +msgid "Press any button to continue..." +msgstr "Presiona cualquier botón para continuar..." + +msgid "Press any button to exit..." +msgstr "Presiona cualquier botón para salir..." + +msgid "Press any button to restart..." +msgstr "Presiona cualquier botón para reiniciar..." + +msgid "Press any button.\n" +msgstr "Presiona cualquier botón.\n" + +msgid "Press any button..." +msgstr "Presiona cualquier botón..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Presiona el botón %s para descargar carátulas" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Presiona el botón %s para expulsar el DVD." + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Perfil:" + +#, c-format +msgid "Profile: %s" +msgstr "Perfil: %s" + +msgid "Program Updates" +msgstr "Programar Actualizaciones" + +msgid "Publisher" +msgstr "Editor" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Salir" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "DERECHA" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Carreras" + +msgid "Rail Shooter" +msgstr "Disparos en Riel" + +#, c-format +msgid "Rated %s" +msgstr "Evaluado %s" + +msgid "Rating" +msgstr "Clasificación" + +msgid "Read" +msgstr "Leer" + +msgid "Reading BCA..." +msgstr "Leyendo BCA..." + +msgid "Reboot" +msgstr "Reiniciar" + +msgid "Region" +msgstr "Región" + +msgid "Release Date" +msgstr "Fecha de la Versión" + +msgid "Release Notes: (short)" +msgstr "Notas de la Versión: (corto)" + +msgid "Removing game, please wait..." +msgstr "Eliminando juego, por favor espera..." + +msgid "Restarting Wii..." +msgstr "Reiniciar Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Regresado! (ret = %d)" + +msgid "Rhythm" +msgstr "Ritmo" + +msgid "Rows:" +msgstr "Filas:" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Corriendo punto de referencia, por favor espere" + +msgid "S. Chinese" +msgstr "Chino S." + +msgid "SD/SDHC Card" +msgstr "Tarjeta SD/SDHC" + +msgid "SUCCESS!" +msgstr "ÉXITO!" + +msgid "Save .gct" +msgstr "Guardar .gct" + +msgid "Save Debug" +msgstr "Guardar Depuración" + +msgid "Save Settings" +msgstr "Guardar Configuraciones" + +msgid "Save debug.log" +msgstr "Guardar debug.log" + +msgid "Savegame not found.\n" +msgstr "Archivo de guardado no encontrado.\n" + +msgid "Savegame:" +msgstr "Archivo de guardado:" + +msgid "Saving Settings... " +msgstr "Guardando Configuraciones... " + +msgid "Saving cheats..." +msgstr "Guardando trucos..." + +msgid "Saving gamelist.txt ... " +msgstr "Guardando gamelist.txt ... " + +msgid "Saving settings..." +msgstr "Guardando configuraciones..." + +msgid "Saving:" +msgstr "Guardando:" + +#, c-format +msgid "Saving: %s" +msgstr "Guardando: %s" + +msgid "Screenshot:" +msgstr "Captura de Pantalla:" + +msgid "Scroll:" +msgstr "Desplazamiento:" + +msgid "Search" +msgstr "Buscar" + +msgid "Search for:" +msgstr "Buscar por:" + +msgid "Select Alternative .dol:" +msgstr "Seleccionando .dol Alternativo:" + +msgid "Select WBFS device:" +msgstr "Seleccionando dispositivo WBFS:" + +msgid "Select a different partition" +msgstr "Selecciona una partición diferente" + +msgid "Select a partition" +msgstr "Selecciona una partición" + +msgid "Select all" +msgstr "Selecciona todo" + +msgid "Select device:" +msgstr "Elegir dispositivo:" + +msgid "Select the game you want to boot" +msgstr "Selecciona el juego que deseas iniciar" + +msgid "Selected Game" +msgstr "Juego Seleccionado" + +msgid "Settings" +msgstr "Configuración" + +msgid "Shooter" +msgstr "Disparos" + +msgid "Show All" +msgstr "Mostrar Todo" + +msgid "Show cIOS info" +msgstr "Mostrar la información del cIOS" + +msgid "Shutdown" +msgstr "Apagado" + +msgid "Side:" +msgstr "Lado:" + +msgid "Sim Racing" +msgstr "Simulador de Carreras" + +msgid "Simulation" +msgstr "Simulación" + +msgid "Size(GB) Type Mount Used" +msgstr "Tam.(GB) Tipo Mont. Usado" + +#, c-format +msgid "Size: %d bytes" +msgstr "Tamaño: %d bytes" + +msgid "Skateboard" +msgstr "Skateboard" + +msgid "Skateboarding" +msgstr "Skateboarding" + +msgid "Skiing" +msgstr "Esquí" + +msgid "Snowboarding" +msgstr "Snowboarding" + +msgid "Soccer" +msgstr "Fútbol" + +msgid "Sort" +msgstr "Ordenar" + +msgid "Sort Games" +msgstr "Ordenar Juegos" + +msgid "Sort Order:" +msgstr "Orden de Clasificación:" + +msgid "Sort Type:" +msgstr "Tipo de Orden:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Orden: %s-%s" + +msgid "Spanish" +msgstr "Español" + +msgid "Sports" +msgstr "Deportes" + +msgid "Start" +msgstr "Iniciar" + +msgid "Start Game" +msgstr "Iniciar Juego" + +msgid "Start this game?" +msgstr "¿Iniciar este juego?" + +msgid "Stealth Action" +msgstr "Acción y Sigilo" + +msgid "Stopping DVD..." +msgstr "Deteniendo DVD..." + +msgid "Strategy" +msgstr "Estrategia" + +msgid "Style" +msgstr "Estílo" + +msgid "Style:" +msgstr "Estílo:" + +msgid "Surfing" +msgstr "Sorfeo" + +msgid "Survival Horror" +msgstr "Horror y Supervivencia" + +msgid "Sync FAT free space info?" +msgstr "Sincronizar la información de espacio libre FAT?" + +msgid "Synchronizing, please wait." +msgstr "Sincronizando, por favor espera." + +msgid "Synopsis" +msgstr "Sinopsis" + +msgid "Synopsis Length" +msgstr "Longitud de la Sinopsis" + +msgid "System" +msgstr "Sistema" + +msgid "System Def." +msgstr "Def. Sistema" + +msgid "T. Chinese" +msgstr "Chino T." + +msgid "Table Tennis" +msgstr "Tenis de Mesa" + +msgid "Tennis" +msgstr "Tenis" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Información del Tema" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"El Tema puede ser muy grande.\n" +"Se sugiere reiniciar CFG.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temas proporcionados por wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Temas para descargar" + +msgid "Themes with updates" +msgstr "Temas con actualizaciones" + +msgid "Title" +msgstr "Título" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Hay muchos trucos! (%d)" + +msgid "Too many code lines!" +msgstr "Hay muchas líneas de código!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Hay muchos fragmentos! %d" + +msgid "Total Body Tracking" +msgstr "Rastreo Corporal Total" + +msgid "Train Simulation" +msgstr "Simulador de Trenes" + +msgid "Trivia" +msgstr "Trivia" + +msgid "Truck Racing" +msgstr "Carrera de Camiones" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Intentando (url#%d) ..." + +msgid "Turntable" +msgstr "Plato Giratorio" + +msgid "UNUSED" +msgstr "NO USADO" + +msgid "UP" +msgstr "ARRIBA" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' no puede iniciar con 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' no tiene una parte de RUTA" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko encontrado. La depuración está activada." + +msgid "USB Mass Storage Device" +msgstr "Dispositivo de Almacenamiento USB" + +msgid "Unknown" +msgstr "Desconocido" + +msgid "Unknown syntax!" +msgstr "Sintaxis desconocida!" + +msgid "Unplayed" +msgstr "No jugado" + +msgid "Unplayed Games" +msgstr "Juegos Sin Jugar" + +msgid "Update themes" +msgstr "Actualizar temas" + +msgid "Updates" +msgstr "Actualizaciones" + +msgid "Updating database." +msgstr "Actualizando Base de Datos." + +msgid "Updating devolution" +msgstr "Actualizando devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Usado: %.1fGB Libre: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Usando .dol Alternativo Guardado:" + +msgid "VC-Arcade" +msgstr "VC de Arcade" + +msgid "VC-Commodore 64" +msgstr "VC de Commodore 64" + +msgid "VC-N64" +msgstr "VC de N64" + +msgid "VC-NES" +msgstr "VC de NES" + +msgid "VC-Neo Geo" +msgstr "VC de Neo Geo" + +msgid "VC-SMS" +msgstr "VC de SMS" + +msgid "VC-SNES" +msgstr "VC de SNES" + +msgid "VC-Sega Genesis" +msgstr "VC de Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC de TurboGrafx-16" + +msgid "Version" +msgstr "Versión" + +msgid "Video Patch:" +msgstr "Parche al Video:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Ver" + +msgid "Virtual Pet" +msgstr "Mascota Virtual" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "Volleyball" +msgstr "Voleibol" + +msgid "WARNING:" +msgstr "ALERTA:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB libre de %.1fGB" + +msgid "Watercraft Racing" +msgstr "Carrera de Embarcaciones" + +msgid "Wheel" +msgstr "Volante Wii" + +msgid "Wide Screen:" +msgstr "Pantalla Ancha:" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Canal de Wii" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Lucha Libre" + +msgid "Write Playlog:" +msgstr "Escribir Registro:" + +#, c-format +msgid "Writing to %s" +msgstr "Escribiendo a %s" + +#, c-format +msgid "Writing: %s" +msgstr "Escribiendo: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Tamaño Erroneo: %d (%d)" + +msgid "Yes" +msgstr "Si" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Puedes tratar de desconectar y\n" +"conectar de nuevo el dispositivo,\n" +"o solamente esperar un poco más" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ CAMBIADO ]" + +msgid "[ FOUND ]" +msgstr "[ ENCONTRADO ]" + +msgid "[ SAVED ]" +msgstr "[ GUARDADO ]" + +msgid "[HOME]" +msgstr "[INICIO]" + +msgid "[No HQ]" +msgstr "[No HQ]" + +msgid "[USED]" +msgstr "[USADO]" + +msgid "[default]" +msgstr "[defecto]" + +msgid "[saved]" +msgstr "[guardado]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "archivo wbfs malo: %s" + +msgid "calculating space, please wait..." +msgstr "calculando espacio, por favor esperar..." + +msgid "delete" +msgstr "borrar" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "descartar" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "dominio %s no ha sido resuelto" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "error leyendo: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "para %d jugadores" + +msgid "for 1 player" +msgstr "para 1 jugador" + +msgid "format" +msgstr "formato" + +msgid "free" +msgstr "libre" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "velocidad lineal de lectura: %.2f mb/s" + +msgid "linear..." +msgstr "lineal..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "tamaño de loader.bin: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "archivo de música es muy grande (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 o music.mod no han sido encontrados!" + +msgid "no file" +msgstr "no hay archivo." + +msgid "none" +msgstr "ninguno" + +msgid "or format a WBFS partition." +msgstr "o formatear una partición WBFS." + +msgid "page" +msgstr "página" + +msgid "parse error" +msgstr "error de análisis" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "velocidad de lectura aleatoria: %.2f mb/s" + +msgid "random..." +msgstr "al azar..." + +msgid "reset" +msgstr "reiniciar" + +msgid "revert" +msgstr "revertir" + +msgid "save" +msgstr "guardar" + +#, c-format +msgid "split error: %s" +msgstr "error de división: %s" + +msgid "uDraw GameTablet" +msgstr "Tableta de uDraw" + +msgid "unable to open wii disc" +msgstr "no se puede abrir el disco de wii" + +#, c-format +msgid "used: %p - %p" +msgstr "usado: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Cargando juego de Wii, espera..." + +#~ msgid "Console Def." +#~ msgstr "Def. Consola" + +#~ msgid "Download titles.txt" +#~ msgstr "Descargas titles.txt" + +#~ msgid "Front" +#~ msgstr "Frente" + +#~ msgid "Loading previous game list..." +#~ msgstr "Cargando lista de juegos anterior.." + +#~ msgid "Remove Game" +#~ msgstr "Remover Juego" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "Actualizar WiiTDB" + +#~ msgid "Update titles.txt" +#~ msgstr "Actualizar titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "Base de Datos WiiTDB" diff --git a/Languages/FI.lang b/Languages/FI.lang new file mode 100644 index 0000000..600e411 --- /dev/null +++ b/Languages/FI.lang @@ -0,0 +1,2349 @@ +# FINNISH LANGUAGE. +# Copyright (C) 2013 +# This file is distributed under the same license as the PACKAGE package. +# Marko Hämäläinen, make8686@gmail.com, 2012. +# Corrected typos and some words to better describe. 2014 January. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-21 23:19+GMT+2\n" +"Language-Team: Marko Hämäläinen \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Translator: Marko Hämäläinen \n" +"Language: FINNISH\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%Vapaana .1fGB/%.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%%/%.2fGB (%c) Aika: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB Kopioitu ajassa %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d vapaana" + +#, c-format +msgid "%d more notes" +msgstr "%d lisätietoja" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Jako: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, odota..." + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(Odotetaan %d sekuntia)" + +msgid "(This can take a couple of minutes)" +msgstr "(Tässä voi mennä vähän aikaa)" + +msgid ".dol too small" +msgstr "-dol-tiedosto liian pieni" + +msgid "1.2+" +msgstr "" + +msgid "1st-Person Shooter" +msgstr "" + +msgid "2.0+" +msgstr "" + +msgid "2.1+" +msgstr "" + +msgid "2.2+" +msgstr "" + +msgid "3D cover" +msgstr "3D kansi" + +msgid "3rd-person Shooter" +msgstr "" + +msgid "< ASC >" +msgstr "< SUUR >" + +msgid "< DESC >" +msgstr "< PIEN >" + +msgid "< DOWNLOAD >" +msgstr "< LATAA >" + +msgid "About" +msgstr "Tietoa ohjelmasta" + +msgid "Action" +msgstr "Toiminta" + +msgid "Additional config:" +msgstr "Lisää asetuksia:" + +msgid "Admin Lock:" +msgstr "Lapsilukko:" + +msgid "Admin Unlock" +msgstr "Lapsilukko pois" + +msgid "Adult" +msgstr "K18+" + +msgid "Adventure" +msgstr "Seikkailu" + +msgid "All" +msgstr "Kaikki" + +msgid "All Channels" +msgstr "Kaikki kanavat" + +msgid "All Games" +msgstr "Kaikki pelit" + +msgid "All themes up to date." +msgstr "Kaikki teemat ajan tasalla" + +msgid "Alt Button Cfg:" +msgstr "Vaihtoehtoiset napit" + +msgid "Alt dol:" +msgstr "Muu .dol" + +msgid "Alternative .dol:" +msgstr "Vaihtoehtoinen .dol" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix" + +#, c-format +msgid "App. Path: %s" +msgstr "Ohj. polku: %s" + +msgid "Arcade" +msgstr "" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Oletko varma että haluat %s\n" +"tämän osion?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Oletko varma että haluat korjata\n" +"tämän osion?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Oletko varma että haluat poistaa\n" +"tämän pelin?" + +msgid "Ascending" +msgstr "Nouseva" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto-start-peli: %.6s ei löydy!" + +msgid "Available Updates" +msgstr "Saatavilla olevat päivitykset" + +msgid "Back" +msgstr "Takaisin" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Pesäpallo" + +msgid "Basic" +msgstr "Perus" + +msgid "Basketball" +msgstr "Koripallo" + +msgid "Bike Racing" +msgstr "Pyöriä" + +msgid "Billiards" +msgstr "Biljardi" + +msgid "Block IOS Reload:" +msgstr "Block IOS Reload:" + +msgid "Board Game" +msgstr "Lautapeli" + +msgid "Boot Disc" +msgstr "Lataa levy" + +msgid "Boot disc" +msgstr "Lataa levy" + +msgid "Booting game, please wait..." +msgstr "Käynnistetään peliä, odota..." + +msgid "Bowling" +msgstr "Keilaus" + +msgid "Boxing" +msgstr "Nyrkkeily" + +msgid "Business Sim" +msgstr "" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG base: %s" + +msgid "Camera" +msgstr "Kamera" + +msgid "Can not dump GC savegames.\n" +msgstr "Ei voi kopioida GC tallennuksia. \n" + +msgid "Can't install Wii games!" +msgstr "Ei voi asentaa Wii-pelejä!" + +msgid "Cancelled." +msgstr "Peruutettu" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Ei voida luoda kansiota: %s" + +msgid "Cards" +msgstr "Korttipeli" + +msgid "Channel" +msgstr "Kanava" + +msgid "Cheat Codes:" +msgstr "Huijauskoodit:" + +msgid "Cheats: " +msgstr "Koodit:" + +msgid "Check For Updates" +msgstr "Tarkista päivitykset" + +msgid "Checking for themes..." +msgstr "Etsitään skinejä" + +msgid "Checking for updates..." +msgstr "Tarkistetaan päivityksiä" + +msgid "Chess" +msgstr "Shakki" + +msgid "Choose a sorting method" +msgstr "Järjestäminen" + +msgid "Classic" +msgstr "Classic" + +msgid "Classic Controller" +msgstr "Classic-ohjain" + +msgid "Clear" +msgstr "Tyhj." + +msgid "Clear Patches:" +msgstr "Clear Patches:" + +msgid "Coaching" +msgstr "" + +msgid "Compare Type" +msgstr "Vertaa tyyppiä" + +msgid "Compilation" +msgstr "Kokoelma" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Valmis. Koko: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Virhe säädöissä, MAX_DNS_ENTRIES vaikka lista on tyhjänä" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Yhteysvirhe, net_read() Virhekoodi: %i" + +msgid "Console" +msgstr "Yksinkertainen näkymä" + +msgid "Construction Sim" +msgstr "" + +msgid "Contains" +msgstr "Sisältää" + +msgid "Controller" +msgstr "Ohjain" + +msgid "Cooking" +msgstr "Ruoanlaitto" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "DIP-modulia ei tunnistettu! (ret = %d)" + +msgid "Country Fix:" +msgstr "Country Fix" + +msgid "Cover" +msgstr "Kansi" + +msgid "Cover Image:" +msgstr "Kansikuva" + +msgid "Cover Style" +msgstr "Kansityyli" + +msgid "Covers Available" +msgstr "" + +msgid "Cover~~Back" +msgstr "Kansi~~Takakansi" + +msgid "Cover~~Front" +msgstr "Kansi~~Etukansi" + +msgid "Create" +msgstr "Valmis" + +msgid "Creator" +msgstr "Tekijä" + +msgid "Cricket" +msgstr "" + +#, c-format +msgid "Current Version: %s" +msgstr "Nykyinen versio: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Custom IOS %d Ei löydy!\n" +"Kannattaa asentaa." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Custom IOS %d Ei voitu ladata! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d on tynkä!\n" +"Asenna se uudelleen." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s Ladattu OK" + +msgid "DEVO" +msgstr "" + +msgid "DISC cover" +msgstr "Levyn kansi" + +msgid "DOWN" +msgstr "Alas" + +msgid "Dance" +msgstr "Tanssi" + +msgid "Dance Pad" +msgstr "Tanssimatto" + +msgid "Darts" +msgstr "Tikanheitto" + +msgid "Database update successful." +msgstr "Tietokanta päivitetty onnistuneesti" + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Poista peli" + +msgid "Deleting" +msgstr "Poistetaan" + +msgid "Descending" +msgstr "Laskeva" + +msgid "Developer" +msgstr "Tekijä" + +msgid "Device is not responding!" +msgstr "Laite ei vastaa!" + +msgid "Device:" +msgstr "Muisti:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "" + +msgid "Devolution:" +msgstr "" + +msgid "Disc" +msgstr "Levy" + +msgid "Disc (Ask)" +msgstr "Levy (Kysy)" + +msgid "Done." +msgstr "Valmis." + +msgid "Download .txt" +msgstr "Lataa .txt" + +msgid "Download All Covers" +msgstr "Lataa kaikki kannet" + +msgid "Download All Missing Covers" +msgstr "Lataa kaikki puuttuvat kannet" + +msgid "Download Missing Covers" +msgstr "Lataa puuttuvat kannet" + +msgid "Download Plugins" +msgstr "Lataa plugineja" + +msgid "Download Themes" +msgstr "Lataa teemoja" + +msgid "Download complete." +msgstr "Lataus valmis." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Latausvirhe gamercardissa #%d." + +msgid "Download game information" +msgstr "Lataa pelitietoja" + +msgid "Downloadable Content" +msgstr "Ladattavaa sisältöä" + +msgid "Downloading ALL MISSING covers" +msgstr "Ladataan KAIKKI puuttuvat kannet" + +msgid "Downloading ALL covers" +msgstr "Ladataan KAIKKI kannet" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Ladataan KAIKKI kannet %.6slle" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Ladataan PUUTTUVAT kannet %.6slle" + +msgid "Downloading Theme previews..." +msgstr "Ladataan teemojen esikatseluita..." + +msgid "Downloading cheats..." +msgstr "Ladataan huijauskoodeja..." + +msgid "Downloading database." +msgstr "Ladataan tietokantaa." + +msgid "Downloading devolution." +msgstr "" + +msgid "Downloading mighty plugin." +msgstr "" + +msgid "Downloading neek2o plugin." +msgstr "" + +msgid "Downloading titles.txt ..." +msgstr "Ladataan titles.txt ..." + +msgid "Downloading translation file." +msgstr "Ladataan käännöstiedostoa" + +msgid "Downloading unifont." +msgstr "" + +#, c-format +msgid "Downloading: %s" +msgstr "Ladataan: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Ladataan: %s as %s" + +msgid "Downloads" +msgstr "Lataukset" + +msgid "Drawing" +msgstr "Piirtäminen" + +msgid "Drums" +msgstr "Rummut" + +msgid "Dump savegame" +msgstr "Kopioi pelitallennus" + +msgid "Duplicate ID3" +msgstr "" + +msgid "Dutch" +msgstr "Hollanti" + +msgid "ERROR" +msgstr "VIRHE" + +msgid "ERROR creating file" +msgstr "VIRHE tehtäessä tiedostoa" + +msgid "ERROR game opt" +msgstr "VIRHE game opt" + +msgid "ERROR reading BCA!" +msgstr "VIRHE luettaessa BCA" + +#, c-format +msgid "ERROR removing %s" +msgstr "VIRHE poistaessa %s" + +msgid "ERROR writing BCA!" +msgstr "VIRHE kirjoittaessa BCA!" + +msgid "ERROR!" +msgstr "VIRHE!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "VIRHE! (ret = %d)" + +msgid "ERROR:" +msgstr "VIRHE:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "VIRHE: %s ei ole saatavilla" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "VIRHE: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "VIRHE: BCA vaatii CIOS222/223 v4" + +msgid "ERROR: Cache Close" +msgstr "VIRHE: Cache Close" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "VIRHE: Ei voitu ladata peliä! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "VIRHE: Peli on jo asennettu!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "VIRHE: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "VIRHE: Epäkelpo pelin ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "VIRHE: EHC moduulin latauksessa! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"VIRHE: NTFS kirjoitus poissa käytöstä!\n" +"(set ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "VIRHE: Osioita ei löydy! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "VIRHE: Ei ole Wiin levy!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "VIRHE: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "VIRHE: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "VIRHE: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "VIRHE: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "VIRHE: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "VIRHE: SD-tilaan vaihtaessa" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "VIRHE: USB ei tunnistettu! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "VIRHE: WBFS ei päästä käsiksi!" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"VIRHE: Tarvitaan suurempi kuin cIOS249rev18,\n" +"cIOS222v4, cIOS223v4, cIOS224v5\n" +"Käynnistäessä pelejä FAT-osioilta!\n" +"Päivitä IOS249 tai valitse joku muu IOS." + +msgid "ERROR: cache: out of memory" +msgstr "VIRHE: cache: muisti loppu" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "VIRHE: tehtäessä: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "VIRHE: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "VIRHE: muisti ylittyi!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"VIRHE: liian monta wbfs osiota\n" +"tuettu vain IOS222/223-mload kanssa" + +msgid "ERROR: not enough free space!!" +msgstr "VIRHE: Liian vähän vapaata tilaa!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "VIRHE: ntfs ei poistettu oikein." + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "VIRHE: avattaessa %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "VIRHE: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "VIRHE: asentaessa osia %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "VIRHE: levy tai osio %s/ ei ole yhdistettynä!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "VIRHE: kirjoittaessa %s (%d)." + +msgid "EXTEND" +msgstr "" + +msgid "Educational" +msgstr "Opetuksellinen" + +msgid "Ejecting DVD..." +msgstr "Poistetaan levyä..." + +msgid "English" +msgstr "Englanti" + +msgid "English Title" +msgstr "Englanninkielinen nimi" + +msgid "Enter Code: " +msgstr "Salasana: " + +msgid "Error GRRLIB init" +msgstr "Virhe GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Virhe tunnistaessa verkkoa" + +msgid "Error adding disc!" +msgstr "Virhe levyn lisäämisessä!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "" + +msgid "Error creating directory..." +msgstr "Virhe kansiota luotaessa..." + +msgid "Error discarding options!" +msgstr "Virhe poistaessa asetuksia!" + +msgid "Error downloading theme preview..." +msgstr "Virhe ladattaessa teemojen esikatselua..." + +msgid "Error downloading theme..." +msgstr "Virhe ladattaessa teemaa" + +msgid "Error downloading themes..." +msgstr "Virhe ladattaessa skinejä..." + +msgid "Error downloading update..." +msgstr "Virhe ladattaessa päivitystä..." + +msgid "Error downloading updates..." +msgstr "Virhe ladattaessa päivityksiä..." + +msgid "Error downloading." +msgstr "Virhe latauksessa." + +msgid "Error establishing connection" +msgstr "Virhe yhteyden muodostamisessa" + +msgid "Error extracting theme..." +msgstr "Virhe teeman purkamisessa..." + +msgid "Error opening database, update did not complete." +msgstr "Virhe avattaessa tietokantaa, päivitystä ei suoritettu." + +#, c-format +msgid "Error opening: %s" +msgstr "Virhe avattessa: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Virhe toistaessa %s" + +msgid "Error reading .dol" +msgstr "Virhe luettaessa .dol-tiedostoa" + +msgid "Error reading dol header" +msgstr "Virhe luettaessa dol headeria" + +#, c-format +msgid "Error saving %s" +msgstr "Virhe tallentaessa: %s" + +msgid "Error saving options!" +msgstr "Virhe tallentaessa valintoja!" + +msgid "Error saving settings!" +msgstr "Virhe tallentaessa asetuksia!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Virhe tallentaessa playlog tiedostoa.\n" +"Käynnistä Wii menusta korjaaksesi." + +msgid "Error: Invalid PNG image!" +msgstr "Virhe: Epäkelpo PNG kuva!" + +msgid "Error: no URL." +msgstr "Virhe: ei ole URL" + +msgid "Error: no data." +msgstr "Virhe: ei dataa." + +msgid "Exercise" +msgstr "" + +msgid "Exit" +msgstr "Wii menu" + +#, c-format +msgid "Extracting: %s" +msgstr "Puretaan: %s" + +msgid "FAIL" +msgstr "EPÄONNISTUI" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC ERROR %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "TÄYTÄ %d" + +msgid "FLAT cover" +msgstr "PELKKÄ kansi" + +msgid "FULL cover" +msgstr "KOKO kansi" + +msgid "Fav" +msgstr "Suosikki" + +msgid "Fav: Off" +msgstr "Suosikki: On" + +msgid "Fav: On" +msgstr "Suosikki: Off" + +msgid "Favorite" +msgstr "Suosikki" + +msgid "Favorite Games" +msgstr "Suosikkipelit" + +msgid "Favorite:" +msgstr "Suosikki:" + +msgid "Favorites:" +msgstr "Suosikit:" + +msgid "Fighting" +msgstr "Tappelu" + +#, c-format +msgid "File not found! %s" +msgstr "Tiedostoa ei löydy! %s" + +msgid "Filter" +msgstr "Suodata" + +msgid "Filter Games" +msgstr "Suodata pelit" + +msgid "Filter by Controller" +msgstr "Suodata ohjaimen mukaan" + +msgid "Filter by Game Type" +msgstr "" + +msgid "Filter by Genre" +msgstr "Suodata tyypin mukan" + +msgid "Filter by Online Features" +msgstr "Suodata nettipelin mukaan" + +msgid "Filter:" +msgstr "Suodata:" + +msgid "Fishing" +msgstr "Kalastus" + +msgid "Fitness" +msgstr "Liikunta" + +msgid "Fixing EXTEND partition..." +msgstr "Korjataan EXTEND osiota..." + +msgid "Flight Sim" +msgstr "" + +msgid "Football" +msgstr "Jalkapallo" + +msgid "Force Devolution:" +msgstr "Pakota Devolution" + +msgid "Force NTSC" +msgstr "Pakota NTSC" + +msgid "Force NTSC 480p" +msgstr "Pakota NTSC 480p" + +msgid "Force PAL" +msgstr "Pakota PAL" + +msgid "Force PAL 480p" +msgstr "Pakota PAL 480p" + +msgid "Force PAL50" +msgstr "Pakota PAL50" + +msgid "Force PAL60" +msgstr "Pakota PAL60" + +msgid "Formatting" +msgstr "Alustetaan" + +#, c-format +msgid "Found %s" +msgstr "Löytyi %s" + +msgid "French" +msgstr "Ranska" + +msgid "Full" +msgstr "Täynnä" + +msgid "Futuristic Racing" +msgstr "Futuristinen ajopeli" + +msgid "GB" +msgstr "Gt" + +msgid "Game Default" +msgstr "Oletus" + +msgid "Game ID" +msgstr "Peli ID" + +msgid "Game Options" +msgstr "Pelin asetukset" + +#, c-format +msgid "Game Options: %s" +msgstr "Pelin asetukset: %s" + +msgid "Game Type" +msgstr "Pelityyppi" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d raportoitu: %.*s" + +msgid "Genre" +msgstr "Genre" + +msgid "German" +msgstr "Saksa" + +msgid "Global Options" +msgstr "Yleiset asetukset" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Kitara" + +msgid "HQ cover" +msgstr "HQ kansi" + +msgid "HTTP Response was without a file" +msgstr "HTTP vastasi ilman tiedostoa" + +msgid "Health" +msgstr "Terveys" + +msgid "Hidden Object" +msgstr "" + +msgid "Hide" +msgstr "Piilota" + +msgid "Hide Game:" +msgstr "Piilota peli" + +msgid "Hockey" +msgstr "Jääkiekko" + +msgid "Hold button B to cancel." +msgstr "Paina B peruuttaaksesi" + +msgid "Home Menu" +msgstr "Päävalikko" + +msgid "Homebrew Channel" +msgstr "Homebrew Channel" + +msgid "Hook Type:" +msgstr "Hook Type:" + +msgid "Hunting" +msgstr "Metsästys" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID ei käy: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Lataus: Estetty" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) epäonnistui koodilla %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Jollei Cfg käynnisty oikein\n" +"ensi kerralla, kpioi boot.dol.bak\n" +"boot.dol tilalle ja yritä uudelleen." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Epäjohdonmukaisuus LZ77 koodauksessa %x" + +msgid "Infinity Base" +msgstr "Infinity-alusta" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info tiedosto: %s" + +msgid "Initializing Network..." +msgstr "Tunnistetaan verkkoa..." + +msgid "Install" +msgstr "Asenna peli" + +msgid "Install Date" +msgstr "Asennusjärjestys" + +msgid "Install Game" +msgstr "Asenna peli" + +msgid "Install game" +msgstr "Asenna peli" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Virhe asennuksessa! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Asennetaan peliä, odota..." + +msgid "Invalid .dol" +msgstr "Epäkelpo .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Epäkelpo osio: '%s'" + +msgid "Italian" +msgstr "Italia" + +msgid "Japanese" +msgstr "Japani" + +msgid "Japanese Title" +msgstr "Japanilainen nimi" + +msgid "Jump" +msgstr "Tasohyppy" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Kart-ajopeli" + +msgid "Keyboard" +msgstr "Näppäimistö" + +msgid "Korean" +msgstr "Korea" + +msgid "LED:" +msgstr "" + +msgid "LEFT" +msgstr "VASEN" + +msgid "LOCKED!" +msgstr "LUKITTU!" + +msgid "Language:" +msgstr "Kieli:" + +msgid "Last Play Date" +msgstr "Viimeksi pelattu" + +msgid "Launch Methods" +msgstr "Käynnistystavat" + +msgid "Life Simulation" +msgstr "Elämä-simulaattori" + +msgid "Load OK!" +msgstr "Lataus OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Ohjelman versio: %s" + +msgid "Loading ..." +msgstr "Lataa ..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Lataa..%s\n" + +msgid "Main" +msgstr "Päävalikko" + +msgid "Main Menu" +msgstr "Päävalikko" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Käytä USB 0 porttia!\n" +"(Eli se joka on lähempänä reunaa)" + +msgid "Manage" +msgstr "Asetuksia" + +msgid "Manage Cheats" +msgstr "Huijauskoodit" + +msgid "Management Sim" +msgstr "" + +msgid "Martial Arts" +msgstr "Taistelulajit" + +msgid "Microphone" +msgstr "Mikrofoni" + +msgid "Mighty Plugin" +msgstr "" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Moottoripyöriä" + +msgid "Mounting device, please wait..." +msgstr "Otetaan laite käyttöön, odota..." + +msgid "Music" +msgstr "Musiikki" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Audiotiedosto kansiosta: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Audiotiedoston koko: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Audiotiedosto: %s" + +msgid "Music: Disabled" +msgstr "Musiikki: Pois" + +msgid "Music: Enabled" +msgstr "Musiikki: Päällä" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Audio: Etsitään %s tiedostoja: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Audio: Seuraava toistettava tiedosto: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Audio: %s tiedostoja löytyi %i kpl" + +msgid "Music: musicArray contents: " +msgstr "Audio: musicArray sisällöt: " + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "NAND Emu polku:\n" + +msgid "NAND Emu:" +msgstr "NAND Emu:" + +msgid "NMM:" +msgstr "" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "Pelaaminen SDHC:lta ei ole tuettu!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"HUOM: CIOS249 ennen rev14:\n" +"Mahdollista virhettä #001 ei ole käsitelty!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "Pelaaminen USB:lta ei ole tuettu!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"HUOM: Käytössä on: CIOS249 rev13:\n" +"Kun lopetat pelaamisen, resetoi tai\n" +"sammuta Wii ennenkuin aloitat\n" +"uuden pelin, tai Wii jää jumiin.!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"HUOM: Käytössä on: CIOS249 rev14:\n" +"Dual Layer-pelit ei toimi kunnolla.\n" +"Kannattaa käyttää muuta IOS:ta\n" +"tai versiota\n" +"kun asennat tai pelaat." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"HUOM: Ohjelma pitää käynnistää uudelleen\n" +"jotta asetukset tulevat voimaan." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"HUOM: Osio P#%d on EXTEND, mutta\n" +"sisältää WBFS järjestelmän. Tämä ei toimi\n" +"Paina %s vaihtaaksesi\n" +"osion EXTENDistä dataan." + +msgid "NTFS compression not supported!" +msgstr "NTFS pakkaus ei tuettu!" + +msgid "NTFS encryption not supported!" +msgstr "NTFS koodaus ei tuettu!" + +msgid "NTSC-J patch:" +msgstr "" + +msgid "Neek2o Plugin" +msgstr "" + +msgid "Network connection established." +msgstr "Verkkoyhteys muodostettu." + +msgid "Network error. Can't update gamercards." +msgstr "Verkkovirhe. Gamercardeja ei voi päivittää." + +msgid "New Themes" +msgstr "Uudet teemat" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS mukana" + +msgid "No" +msgstr "Ei" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "URL:sta puuttuu domain '%s'" + +msgid "No games found!" +msgstr "Ei löytynyt pelejä!" + +msgid "No partition selected!" +msgstr "Osiota ei valittuna!" + +msgid "No themes found." +msgstr "Skinejä ei löytynyt." + +msgid "No updates found." +msgstr "Päivityksiä ei löytynyt." + +msgid "NoDisc:" +msgstr "EiLevyä:" + +msgid "None found on disc" +msgstr "Levyltä ei löytynyt mitään." + +msgid "Not Found boot.dol!" +msgstr "boot.dol ei löytynyt!" + +msgid "Not Found!" +msgstr "Ei löydy!" + +msgid "Number of Online Players" +msgstr "Verkkopelaajien määrä" + +msgid "Number of Players" +msgstr "Pelaajia" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (huijaukset):" + +msgid "Ocarina Cheat Manager" +msgstr "Huijaukset (Ocarina)" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Virheellinen koodi" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Koodeja löytyi" + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Ei löytynyt koodeja" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Muisti täynnä" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Etsitään koodeja..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Liikaa koodeja" + +msgid "Off" +msgstr "Pois" + +msgid "Off-Road Racing" +msgstr "Rallipeli" + +msgid "On" +msgstr "Päällä" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Kilpailuja" + +msgid "Online Play" +msgstr "Nettimoninpeli" + +msgid "Online Players" +msgstr "Online-pelaajia" + +msgid "Online Score List" +msgstr "Pistelasku" + +msgid "Online Updates" +msgstr "Päivittäminen" + +msgid "Open Sort" +msgstr "Järjestysvalikko" + +msgid "Open Style" +msgstr "Teemavalikko" + +msgid "Opening DVD disc..." +msgstr "Avataan levyä..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Avataan osiota: %s" + +msgid "Options" +msgstr "Valinnat" + +msgid "Options discarded for this game." +msgstr "Asetukset ovat pois päältä tässä pelissä." + +msgid "Options saved for this game." +msgstr "Tämän pelin asetukset." + +msgid "Out of memory" +msgstr "Muisti loppu." + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "" + +msgid "PAD HOOK" +msgstr "" + +msgid "PAD HOOK:" +msgstr "" + +msgid "Page:" +msgstr "Sivu:" + +msgid "Partial" +msgstr "" + +msgid "Partition:" +msgstr "Osio:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Osio: %s ei löydy!" + +msgid "Party" +msgstr "Moninpeli" + +msgid "Paused Start" +msgstr "Paused Start" + +msgid "Petanque" +msgstr "" + +msgid "Pinball" +msgstr "" + +msgid "Platformer" +msgstr "Tasohyppely" + +msgid "Play Count" +msgstr "Pelattuja" + +msgid "Players" +msgstr "Pelaajia" + +msgid "Please insert a game disc..." +msgstr "Aseta levy..." + +msgid "Plugin:" +msgstr "" + +msgid "Poker" +msgstr "Pokeri" + +msgid "Portal of Power" +msgstr "" + +#, c-format +msgid "Press %s button for options." +msgstr "%s: Asetukset" + +#, c-format +msgid "Press %s button to %s." +msgstr "Paina %s jotta %s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Koodit käyttöön, %s." + +#, c-format +msgid "Press %s button to cancel." +msgstr "%s, Peruuta valinta." + +#, c-format +msgid "Press %s button to change device." +msgstr "Vaihda laite, %s." + +#, c-format +msgid "Press %s button to continue." +msgstr "%s: Jatka" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "%s: Poista FS" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "%s: Hylkää BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Poistu, %s." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "%s: Korjaa EXTEND/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "%s alusta WBFS-osio" + +#, c-format +msgid "Press %s button to go back." +msgstr "%s: Takaisin" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Valitse osio, %s." + +#, c-format +msgid "Press %s button to select." +msgstr "%s: Valitse" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "%s, hyppää koodien yli." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "%s: Synkkaa FAT-osio" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "%s: Kokoruudun esikatselu" + +#, c-format +msgid "Press %s for game options" +msgstr "Pelin asetukset: %s" + +#, c-format +msgid "Press %s for global options" +msgstr "Yleiset asetukset, %s" + +#, c-format +msgid "Press %s to discard options" +msgstr "Hylkää muutokset, %s" + +#, c-format +msgid "Press %s to download and update" +msgstr "Lataa ja päivitä, %s" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Lataa tämä teema, %s" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Lataa, %s. Paluu, %s" + +#, c-format +msgid "Press %s to return" +msgstr "%s: Palaa" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Tallenna yleiset asetukset, %s" + +#, c-format +msgid "Press %s to save options" +msgstr "Tallenna asetukset, %s" + +#, c-format +msgid "Press %s to save selection" +msgstr "Tallenna valinnat, %s" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Valitse suodatin, %s" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Järjestelytapa, %s" + +#, c-format +msgid "Press %s to start game" +msgstr "Käynnistä peli, %s" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Päiivitä ilman meta.xml-tiedostoa, %s" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "%s/%s: Valitse laite" + +msgid "Press 1 to skip WBFS mounting" +msgstr "Paina 1 ohittaaksesi WBFS valmistelun" + +msgid "Press 2 to reload IOS" +msgstr "2: Lataa IOS" + +msgid "Press A to continue without config.txt" +msgstr "Paina A jatkaaksesi ilman config.txt-tiedostoa" + +msgid "Press A to select device" +msgstr "A: Valitse laite" + +msgid "Press B to exit to HBC" +msgstr "B: poistu HBC" + +msgid "Press HOME to reset" +msgstr "HOME: Reset" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "VASEN/OIKEA: Valitse IOS" + +msgid "Press any button to continue..." +msgstr "Paina mitä tahansa nappia..." + +msgid "Press any button to exit..." +msgstr "Paina mitä tahansa nappia poistuaksesi..." + +msgid "Press any button to restart..." +msgstr "Paina mitä tahansa nappia käynnistääksesi ohjelman uudelleen..." + +msgid "Press any button.\n" +msgstr "Paina mitä tahansa nappia.\n" + +msgid "Press any button..." +msgstr "Paina mitä tahansa nappia..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Lataa kannet, %s" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "%s: Sylkäise levy ulos." + +msgid "Priiloader" +msgstr "" + +msgid "Profile:" +msgstr "Profiili:" + +#, c-format +msgid "Profile: %s" +msgstr "Prifiili: %s" + +msgid "Program Updates" +msgstr "Ohjelman päivitykset" + +msgid "Publisher" +msgstr "Julkaisija" + +msgid "Puzzle" +msgstr "Pulmanratkonta" + +msgid "Quit" +msgstr "Lopeta?" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "OIKEA" + +msgid "RPG" +msgstr "Roolipeli" + +msgid "Racing" +msgstr "Ajopeli" + +msgid "Rail Shooter" +msgstr "" + +#, c-format +msgid "Rated %s" +msgstr "%s luokiteltu" + +msgid "Rating" +msgstr "Luokitus" + +msgid "Read" +msgstr "Lue" + +msgid "Reading BCA..." +msgstr "Lukee BCA:ta..." + +msgid "Reboot" +msgstr "Käynnistä uudelleen" + +msgid "Region" +msgstr "" + +msgid "Release Date" +msgstr "Julkaisupäivä" + +msgid "Release Notes: (short)" +msgstr "Tietoa julkaisusta: (lyhyt)" + +msgid "Removing game, please wait..." +msgstr "Poistetaan peliä, odota..." + +msgid "Restarting Wii..." +msgstr "Käynnistetään Wiitä uudelleen..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Palattiin! (ret = %d)" + +msgid "Rhythm" +msgstr "Tanssi" + +msgid "Rows:" +msgstr "Rivejä:" + +msgid "Rugby" +msgstr "" + +msgid "Running benchmark, please wait" +msgstr "Running benchmark, odota" + +msgid "S. Chinese" +msgstr "S. Chinese" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC" + +msgid "SUCCESS!" +msgstr "ONNISTUI!" + +msgid "Save .gct" +msgstr "Tallenna .gct" + +msgid "Save Debug" +msgstr "Tallenna" + +msgid "Save Settings" +msgstr "Tallenna asetukset" + +msgid "Save debug.log" +msgstr "Tallenna debug.log" + +msgid "Savegame not found.\n" +msgstr "Pelitallennusta ei löytynyt. \n" + +msgid "Savegame:" +msgstr "Pelitallennus:" + +msgid "Saving Settings... " +msgstr "Tallennetaan asetuksia..." + +msgid "Saving cheats..." +msgstr "Tallennetaan koodeja..." + +msgid "Saving gamelist.txt ... " +msgstr "Tallennetaan gamelist.txt..." + +msgid "Saving settings..." +msgstr "Tallennetaan asetuksia..." + +msgid "Saving:" +msgstr "Tallennetaan:" + +#, c-format +msgid "Saving: %s" +msgstr "Tallennetaan: %s" + +msgid "Screenshot:" +msgstr "Kuvakaappaus:" + +msgid "Scroll:" +msgstr "Rullaus:" + +msgid "Search" +msgstr "Etsi" + +msgid "Search for:" +msgstr "Etsi...:" + +msgid "Select Alternative .dol:" +msgstr "Vaihtoehtoinen .dol" + +msgid "Select WBFS device:" +msgstr "WBFS laite" + +msgid "Select a different partition" +msgstr "Valitse toinen osio" + +msgid "Select a partition" +msgstr "Valitse osio" + +msgid "Select all" +msgstr "Valitse kaikki" + +msgid "Select device:" +msgstr "Valitse laite:" + +msgid "Select the game you want to boot" +msgstr "Valitse käynnistettävä peli" + +msgid "Selected Game" +msgstr "Peli valittu" + +msgid "Settings" +msgstr "Asetukset" + +msgid "Shooter" +msgstr "Ammuskelu" + +msgid "Show All" +msgstr "Näytä kaikki" + +msgid "Show cIOS info" +msgstr "Näytä CIOS info" + +msgid "Shutdown" +msgstr "Sammuta" + +msgid "Side:" +msgstr "Kansi:" + +msgid "Sim Racing" +msgstr "" + +msgid "Simulation" +msgstr "Simulaattori" + +msgid "Size(GB) Type Mount Used" +msgstr "Koko(GB) Tyyppi Laite Tila" + +#, c-format +msgid "Size: %d bytes" +msgstr "Koko: %d tavua" + +msgid "Skateboard" +msgstr "Rullalauta" + +msgid "Skateboarding" +msgstr "Rullalautailu" + +msgid "Skiing" +msgstr "Hiihto" + +msgid "Snowboarding" +msgstr "Lumilautailu" + +msgid "Soccer" +msgstr "Jalkapallo" + +msgid "Sort" +msgstr "Järjestä" + +msgid "Sort Games" +msgstr "Järjestä pelit" + +msgid "Sort Order:" +msgstr "Järjestys:" + +msgid "Sort Type:" +msgstr "Järjestä" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Järjestä: %s-%s" + +msgid "Spanish" +msgstr "Espanja" + +msgid "Sports" +msgstr "Urheilu" + +msgid "Start" +msgstr "Käynnistä" + +msgid "Start Game" +msgstr "Käynnistä peli" + +msgid "Start this game?" +msgstr "Käynnistä peli?" + +msgid "Stealth Action" +msgstr "" + +msgid "Stopping DVD..." +msgstr "Pysäytetään levyä..." + +msgid "Strategy" +msgstr "Strategia" + +msgid "Style" +msgstr "Tyyli" + +msgid "Style:" +msgstr "Tyyli:" + +msgid "Surfing" +msgstr "Surffaus" + +msgid "Survival Horror" +msgstr "Kauhu" + +msgid "Sync FAT free space info?" +msgstr "Sync FAT free space info?" + +msgid "Synchronizing, please wait." +msgstr "Synkronisoidaan, odota." + +msgid "Synopsis" +msgstr "" + +msgid "Synopsis Length" +msgstr "" + +msgid "System" +msgstr "Laitteisto" + +msgid "System Def." +msgstr "System Def." + +msgid "T. Chinese" +msgstr "T. Chinese" + +msgid "Table Tennis" +msgstr "Pöytätennis" + +msgid "Tennis" +msgstr "" + +msgid "Theme" +msgstr "teema" + +msgid "Theme Info" +msgstr "Tietoa teemasta" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"teema saattaa olla liian iso.\n" +"Suositellaan uudelleenkäynnistystä.\n" + +msgid "Theme:" +msgstr "teema:" + +#, c-format +msgid "Theme: %s" +msgstr "teema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "teemat tarjonnut: wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Ladattavat teemat" + +msgid "Themes with updates" +msgstr "Päivitettävät teemat" + +msgid "Title" +msgstr "Nimi" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Liikaa koodeja! (%d)" + +msgid "Too many code lines!" +msgstr "Liian monta riviä koodeja!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Liian pirstoutunut! %d" + +msgid "Total Body Tracking" +msgstr "" + +msgid "Train Simulation" +msgstr "Junasimulaattori" + +msgid "Trivia" +msgstr "Tietokilpailu" + +msgid "Truck Racing" +msgstr "Kuorma-auto-kisailu" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Yritetään (url#%d) ..." + +msgid "Turntable" +msgstr "" + +msgid "UNUSED" +msgstr "KÄYTTÄMÄTÖN" + +msgid "UP" +msgstr "YLÖS" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' ei ala 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' ei sisällä PATH-osaa" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko löytyi. Debuggaus on päällä." + +msgid "USB Mass Storage Device" +msgstr "USB massamuisti" + +msgid "Unknown" +msgstr "Tuntematon" + +msgid "Unknown syntax!" +msgstr "Tuntematon syntaksi!" + +msgid "Unplayed" +msgstr "Ei-pelatut" + +msgid "Unplayed Games" +msgstr "Ei-pelatut" + +msgid "Update themes" +msgstr "Päiivitä teemat" + +msgid "Updates" +msgstr "Päivitykset" + +msgid "Updating database." +msgstr "Päivitetään tietokantaa." + +msgid "Updating devolution" +msgstr "Päivitetään Devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Käytetty: %.1fGB Vapaata %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Käytetään tallennettua vaihtoehtoista .dol:" + +msgid "VC-Arcade" +msgstr "" + +msgid "VC-Commodore 64" +msgstr "" + +msgid "VC-N64" +msgstr "" + +msgid "VC-NES" +msgstr "" + +msgid "VC-Neo Geo" +msgstr "" + +msgid "VC-SMS" +msgstr "" + +msgid "VC-SNES" +msgstr "" + +msgid "VC-Sega Genesis" +msgstr "" + +msgid "VC-TurboGrafx-16" +msgstr "" + +msgid "Version" +msgstr "Versio" + +msgid "Video Patch:" +msgstr "Video Patch:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Järjestys" + +msgid "Virtual Pet" +msgstr "Virtuaalilemmikki" + +msgid "Vitality Sensor" +msgstr "Elinvoima sensori(?)" + +msgid "Volleyball" +msgstr "" + +msgid "WARNING:" +msgstr "VAROITUS:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: Vapaana %.1fGB/%.1fGB" + +msgid "Watercraft Racing" +msgstr "Vene-kilpa-ajo" + +msgid "Wheel" +msgstr "Ratti" + +msgid "Wide Screen:" +msgstr "Laajakuva" + +msgid "Wii" +msgstr "" + +msgid "Wii Channel" +msgstr "Wii-kanava" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Paini" + +msgid "Write Playlog:" +msgstr "Write Playlog:" + +#, c-format +msgid "Writing to %s" +msgstr "Kirjoitetaan: %s" + +#, c-format +msgid "Writing: %s" +msgstr "Kirjoitetaan: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Väärä koko: %d (%d)" + +msgid "Yes" +msgstr "Kyllä" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Voit myös odottaa tai\n" +"poistaa laitteen Wiistä\n" +"ja kytkeä uudelleen" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ MUUTETTU ]" + +msgid "[ FOUND ]" +msgstr "[ LÖYTYNYT ]" + +msgid "[ SAVED ]" +msgstr "[ TALLENNETTU ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[Ei HQ]" + +msgid "[USED]" +msgstr "[KÄYTÖSSÄ]" + +msgid "[default]" +msgstr "[oletus]" + +msgid "[saved]" +msgstr "[tallennettu]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "huono wbfs-tiedosto: %s" + +msgid "calculating space, please wait..." +msgstr "lasketaan tilaa, odota..." + +msgid "delete" +msgstr "poista" + +#, c-format +msgid "dest: %p - %p" +msgstr "kohde: %p - %p" + +msgid "discard" +msgstr "hylkää" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "palvelinta %d ei voitu selvittää" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "virhe luettaessa: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "%d pelaajalle" + +msgid "for 1 player" +msgstr "1 pelaajalle" + +msgid "format" +msgstr "alusta" + +msgid "free" +msgstr "vapaa" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "lineaarinen lukunopeus %.2f mb/s" + +msgid "linear..." +msgstr "lineaarinen..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin koko: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "musiikkitiedosto liian iso (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 tai music.mod ei löytynyt!" + +msgid "no file" +msgstr "ei tiedostoa" + +msgid "none" +msgstr "ei mitään" + +msgid "or format a WBFS partition." +msgstr "tai alusta WBFS osio." + +msgid "page" +msgstr "sivu" + +msgid "parse error" +msgstr "jäsennysvirhe" + +msgid "r51-" +msgstr "" + +msgid "r52+" +msgstr "" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "vaihteleva lukunopeus: %.2f mb/s" + +msgid "random..." +msgstr "vaihteleva..." + +msgid "reset" +msgstr "reset" + +msgid "revert" +msgstr "paluu" + +msgid "save" +msgstr "tallenna" + +#, c-format +msgid "split error: %s" +msgstr "jakovirhe: %s" + +msgid "uDraw GameTablet" +msgstr "uDrav Gametablet" + +msgid "unable to open wii disc" +msgstr "Ei voitu avata Wii levyä" + +#, c-format +msgid "used: %p - %p" +msgstr "käytetty: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Käynnistetään Wii peliä, Odota" + +#~ msgid "Console Def." +#~ msgstr "Oletus." + +#~ msgid "Download titles.txt" +#~ msgstr "Lataa titles.txt" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "päivitä WiiTDB tietokanta" + +#~ msgid "Update titles.txt" +#~ msgstr "Päivitä titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "WiiTDB pelitietokanta" diff --git a/Languages/FR.lang b/Languages/FR.lang new file mode 100644 index 0000000..2b74a68 --- /dev/null +++ b/Languages/FR.lang @@ -0,0 +1,2382 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-02-12 23:19+0100\n" +"Last-Translator: JABE \n" +"Language-Team: FRENCH \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGo libre sur %.1fGo" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% sur %.2fGo (%c) Estimé: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGo copié en %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d disponibles" + +#, c-format +msgid "%d more notes" +msgstr "%d plus de notes" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Scindé: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, veuillez patienter..." + +#, c-format +msgid "%s: Favorites" +msgstr "%s: Favoris" + +#, c-format +msgid "%s: GUI " +msgstr "%s: interface graphique " + +#, c-format +msgid "%s: Options " +msgstr "%s: Options " + +#, c-format +msgid "(%d online)" +msgstr "(%d en ligne)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d secondes de temps écoulé)" + +msgid "(This can take a couple of minutes)" +msgstr "(Cela peut prendre quelques minutes)" + +msgid ".dol too small" +msgstr ".dol est trop petit" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "Tir vue subjective" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "jaquette 3D" + +msgid "3rd-person Shooter" +msgstr "Tir vue objective" + +msgid "< ASC >" +msgstr "< CROI >" + +msgid "< DESC >" +msgstr "< DÉCR >" + +msgid "< DOWNLOAD >" +msgstr "< TÉLÉCHARGER >" + +msgid "About" +msgstr "À propos" + +msgid "Action" +msgstr "Action" + +msgid "Additional config:" +msgstr "Config. supplémentaire:" + +msgid "Admin Lock:" +msgstr "Blocage admin:" + +msgid "Admin Unlock" +msgstr "Déblocage admin:" + +msgid "Adult" +msgstr "Adulte" + +msgid "Adventure" +msgstr "Aventure" + +msgid "All" +msgstr "Tous" + +msgid "All Channels" +msgstr "Toutes les chaînes" + +msgid "All Games" +msgstr "Tous les jeux" + +msgid "All themes up to date." +msgstr "Tous les thèmes à jour." + +msgid "Alt Button Cfg:" +msgstr "Cfg bouton alt." + +msgid "Alt dol:" +msgstr "dol altern." + +msgid "Alternative .dol:" +msgstr ".dol alternatif:" + +msgid "Anti 002 Fix:" +msgstr "Régl. anti 002:" + +#, c-format +msgid "App. Path: %s" +msgstr "Chemin de l'appl.: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Êtes-vous sûr de vouloir %s\n" +"cette partition ?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Êtes-vous sûr de vouloir RÉPARER\n" +"cette partition ?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Êtes-vous sûr de vouloir supprimer\n" +"ce jeu ?" + +msgid "Ascending" +msgstr "Croissant" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Démarrage-auto du jeu: %.6s introuvable !" + +msgid "Available Updates" +msgstr "Mises à jour disponibles" + +msgid "Back" +msgstr "Retour" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Baseball" + +msgid "Basic" +msgstr "Basique" + +msgid "Basketball" +msgstr "Basket-ball" + +msgid "Bike Racing" +msgstr "Course de vélo" + +msgid "Billiards" +msgstr "Billard" + +msgid "Block IOS Reload:" +msgstr "Bloq. rchgmnt IOS:" + +msgid "Board Game" +msgstr "Jeu de société" + +msgid "Boot Disc" +msgstr "Démarrer le disque" + +msgid "Boot disc" +msgstr "Démarrer le disque" + +msgid "Booting game, please wait..." +msgstr "Démarrage du jeu, patientez SVP..." + +msgid "Bowling" +msgstr "Bowling" + +msgid "Boxing" +msgstr "Boxe" + +msgid "Business Sim" +msgstr "Gestion économique" + +#, c-format +msgid "CFG base: %s" +msgstr "Base de CFG" + +msgid "Camera" +msgstr "Appareil photo" + +msgid "Can not dump GC savegames.\n" +msgstr "Dump de sauvegarde GC impossible.\n" + +msgid "Can't install Wii games!" +msgstr "Impossible d'installer des jeux Wii !" + +msgid "Cancelled." +msgstr "Annulé." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Impossible de créer le répertoire: %s" + +msgid "Cards" +msgstr "Cartes" + +msgid "Channel" +msgstr "Chaîne" + +msgid "Cheat Codes:" +msgstr "Codes de triche:" + +msgid "Cheats: " +msgstr "Codes: " + +msgid "Check For Updates" +msgstr "Vérifier les mises à jour" + +msgid "Checking for themes..." +msgstr "Vérification des thèmes..." + +msgid "Checking for updates..." +msgstr "Vérification des mises à jour..." + +msgid "Chess" +msgstr "Échecs" + +msgid "Choose a sorting method" +msgstr "Choisissez une méthode de tri" + +msgid "Classic" +msgstr "Classique" + +msgid "Classic Controller" +msgstr "Manette classique" + +msgid "Clear" +msgstr "Vider" + +msgid "Clear Patches:" +msgstr "Enlever correctifs:" + +msgid "Coaching" +msgstr "Coaching" + +msgid "Compare Type" +msgstr "Type de comparaison" + +msgid "Compilation" +msgstr "Compilation" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Terminé. Taille: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Erreur de configuration, MAX_DNS_ENTRIES atteint alors que la liste est vide" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Erreur de connexion de net_read() Code d'erreur: %i" + +msgid "Console" +msgstr "Console" + +msgid "Construction Sim" +msgstr "Construction" + +msgid "Contains" +msgstr "Contient" + +msgid "Controller" +msgstr "Manette" + +msgid "Cooking" +msgstr "Cuisine" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Impossible d'initialiser le module DIP ! (ret = %d)" + +msgid "Country Fix:" +msgstr "Réglage de pays:" + +msgid "Cover" +msgstr "Jaquette" + +msgid "Cover Image:" +msgstr "Jaquettes:" + +msgid "Cover Style" +msgstr "Style de jaquette" + +msgid "Covers Available" +msgstr "Jaquettes disponibles" + +msgid "Cover~~Back" +msgstr "Jaquette~~Arrière" + +msgid "Cover~~Front" +msgstr "Jaquette~~Avant" + +msgid "Create" +msgstr "Créer" + +msgid "Creator" +msgstr "Créateur" + +msgid "Cricket" +msgstr "Cricket" + +#, c-format +msgid "Current Version: %s" +msgstr "Version actuelle: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Le Custom IOS %d n'a pas pu être trouvé !\n" +"Veuillez l'installer." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Le Custom IOS %d n'a pas pu être chargé ! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Le Custom IOS %d est un stub !\n" +"Veuillez le réinstaller." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s Chargé OK" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "jaquette du disque" + +msgid "DOWN" +msgstr "BAS" + +msgid "Dance" +msgstr "Danse" + +msgid "Dance Pad" +msgstr "Tapis de danse" + +msgid "Darts" +msgstr "Fléchettes" + +msgid "Database update successful." +msgstr "Base de données mise à jour avec succès." + +msgid "Debug" +msgstr "Débogage" + +msgid "Delete Game" +msgstr "Supprimer le jeu" + +msgid "Deleting" +msgstr "Suppression" + +msgid "Descending" +msgstr "Décroissant" + +msgid "Developer" +msgstr "Développeur" + +msgid "Device is not responding!" +msgstr "Le périphérique ne répond pas !" + +msgid "Device:" +msgstr "Périph.:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolution accepte seulement les dumps propres !\n" + +msgid "Devolution:" +msgstr "Devolution :" + +msgid "Disc" +msgstr "Disque" + +msgid "Disc (Ask)" +msgstr "Disque (Demande)" + +msgid "Done." +msgstr "Terminé." + +msgid "Download .txt" +msgstr "Télécharger .txt" + +msgid "Download All Covers" +msgstr "Télécharger toutes les jaquettes" + +msgid "Download All Missing Covers" +msgstr "Tél. toutes jaquettes manquantes" + +msgid "Download Missing Covers" +msgstr "Télécharger les jaquettes manquantes" + +msgid "Download Plugins" +msgstr "Télécharger des plugins" + +msgid "Download Themes" +msgstr "Télécharger des thèmes" + +msgid "Download complete." +msgstr "Téléchargement terminé." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Erreur de téléchargement de profil de joueur #%d." + +msgid "Download game information" +msgstr "Télécharger les informations des jeux" + +msgid "Downloadable Content" +msgstr "Contenu téléchargeable" + +msgid "Downloading ALL MISSING covers" +msgstr "Téléch. les jaquettes MANQUANTES" + +msgid "Downloading ALL covers" +msgstr "Téléch. TOUTES les jaquettes" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Télécharge TOUTES les jaquettes pour %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Télécharge les jaquettes MANQUANTES pour %.6s" + +msgid "Downloading Theme previews..." +msgstr "Téléchargement d'aperçus de thèmes..." + +msgid "Downloading cheats..." +msgstr "Téléchargement de codes..." + +msgid "Downloading database." +msgstr "Téléchargement de la base de données." + +msgid "Downloading devolution." +msgstr "Téléchargement de devolution." + +msgid "Downloading mighty plugin." +msgstr "Téléchargement du plugin mighty." + +msgid "Downloading neek2o plugin." +msgstr "Téléchargement du plugin neek2o." + +msgid "Downloading titles.txt ..." +msgstr "Téléchargement de titles.txt ..." + +msgid "Downloading translation file." +msgstr "Téléchargement du fichier de traduction." + +msgid "Downloading unifont." +msgstr "Téléchargement d'unifont" + +#, c-format +msgid "Downloading: %s" +msgstr "Téléchargement: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Téléchargement: %s sur %s" + +msgid "Downloads" +msgstr "Téléchargements" + +msgid "Drawing" +msgstr "Dessin" + +msgid "Drums" +msgstr "Batterie" + +msgid "Dump savegame" +msgstr "Dump sauvegarde" + +msgid "Duplicate ID3" +msgstr "Dupliquer ID3" + +msgid "Dutch" +msgstr "Néerlandais" + +msgid "ERROR" +msgstr "ERREUR" + +msgid "ERROR creating file" +msgstr "ERREUR de création du fichier" + +msgid "ERROR game opt" +msgstr "ERREUR d'option du jeu" + +msgid "ERROR reading BCA!" +msgstr "ERREUR de lecture BCA !" + +#, c-format +msgid "ERROR removing %s" +msgstr "ERREUR de suppression de %s" + +msgid "ERROR writing BCA!" +msgstr "ERREUR d'écriture BCA !" + +msgid "ERROR!" +msgstr "ERREUR !" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ERREUR ! (ret = %d)" + +msgid "ERROR:" +msgstr "ERREUR:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ERREUR: %s n'est pas accessible" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ERROR: ChargementAppl. %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ERREUR: CIOS222/223 v4 requis pour la BCA" + +msgid "ERROR: Cache Close" +msgstr "ERREUR: Fermeture de cache" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ERREUR: Impossible d'ouvrir le jeu ! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ERREUR: Jeu déjà installé !!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ERREUR: ObtenirCertif. %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ERREUR: ID du Jeu Invalide" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ERREUR: Chargement du module EHC ! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERREUR: écriture NTFS désactivée !\n" +"(mettez ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ERREUR: Aucune partition trouvée ! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ERREUR: Ce n'est pas un disque Wii !!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ERREUR: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ERREUR: OuverturePartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ERREUR: OuverturePartition (0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ERREUR: RéglageListeFrag(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ERREUR: RéglageWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ERREUR: Réglage mode SD" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ERREUR: init. USB ! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "ERREUR: WBFS pas monté !" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"ERREUR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 ou supérieur requis\n" +"pour lancer le jeu d'une partition FAT !\n" +"Mettez à jour l'IOS249 ou choisissez un IOS différent." + +msgid "ERROR: cache: out of memory" +msgstr "ERREUR: cache: plus de mémoire disponible" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ERREUR: création: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ERREUR: obtenir_liste_frag: %d" + +msgid "ERROR: memory overlap!" +msgstr "ERREUR: chevauchement de mémoire !" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERREUR: les partitions wbfs multiples\n" +"ne sont supportées qu'avec l'IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ERREUR: espace libre insuffisant !!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ERREUR: ntfs n'a pas été démonté correctement" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ERREUR: ouverture %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ERREUR: réglage_liste_frag: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ERREUR: réglage de fragments %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "ERREUR : le disque ou la partition %s/ n'est pas connecté !!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ERREUR: écriture %s (%d)." + +msgid "EXTEND" +msgstr "ÉTENDRE" + +msgid "Educational" +msgstr "Éducatif" + +msgid "Ejecting DVD..." +msgstr "Éjection du DVD..." + +msgid "English" +msgstr "Anglais" + +msgid "English Title" +msgstr "Titre anglais" + +msgid "Enter Code: " +msgstr "Entrer le code:" + +msgid "Error GRRLIB init" +msgstr "Erreur d'init. GRRLIB" + +msgid "Error Initializing Network." +msgstr "Erreur d'initialisation du réseau." + +msgid "Error adding disc!" +msgstr "Erreur d'ajout de disque" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Erreur d'allocation tampon : %d" + +msgid "Error creating directory..." +msgstr "Erreur de création de dossier..." + +msgid "Error discarding options!" +msgstr "Erreur d'annulation d'options !" + +msgid "Error downloading theme preview..." +msgstr "Erreur de téléchargement d'aperçu de thème..." + +msgid "Error downloading theme..." +msgstr "Erreur de téléchargement de thème..." + +msgid "Error downloading themes..." +msgstr "rreur de téléchargement de thèmes..." + +msgid "Error downloading update..." +msgstr "Erreur de téléchargement de mise à jour..." + +msgid "Error downloading updates..." +msgstr "Erreur de téléchargement de mises à jour..." + +msgid "Error downloading." +msgstr "Erreur de téléchargement." + +msgid "Error establishing connection" +msgstr "Erreur de connexion établie" + +msgid "Error extracting theme..." +msgstr "Erreur d'extraction de thème..." + +msgid "Error opening database, update did not complete." +msgstr "Erreur d'ouverture de la base de données, la mise à jour ne s'est pas terminée." + +#, c-format +msgid "Error opening: %s" +msgstr "Erreur d'ouverture de: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Erreur de lecture de %s" + +msgid "Error reading .dol" +msgstr "Erreur de lecture .dol" + +msgid "Error reading dol header" +msgstr "Erreur de lecture d'entête .dol" + +#, c-format +msgid "Error saving %s" +msgstr "Erreur de sauvegarde de %s" + +msgid "Error saving options!" +msgstr "Erreur de sauvegarde des options !" + +msgid "Error saving settings!" +msgstr "Erreur de sauvegarde des réglages !" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Erreur de sauvegarde d'historique de jeu.\n" +"Démarrez à partir du menu Wii pour réparer." + +msgid "Error: Invalid PNG image!" +msgstr "Erreur: Image PNG invalide !" + +msgid "Error: no URL." +msgstr "Erreur: aucune URL." + +msgid "Error: no data." +msgstr "Erreur: pas de données." + +msgid "Exercise" +msgstr "Exercice" + +msgid "Exit" +msgstr "Quitter" + +#, c-format +msgid "Extracting: %s" +msgstr "Extraction: %s" + +msgid "FAIL" +msgstr "ÉCHEC" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ERREUR d'ALLOC %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: grille d'alloc(%d)" + +#, c-format +msgid "FILL %d" +msgstr "OCCUPE %d" + +msgid "FLAT cover" +msgstr "jaquette plate" + +msgid "FULL cover" +msgstr "jaquette complète" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Désact." + +msgid "Fav: On" +msgstr "Fav: Activé" + +msgid "Favorite" +msgstr "Favori" + +msgid "Favorite Games" +msgstr "Jeux Favoris" + +msgid "Favorite:" +msgstr "Favori:" + +msgid "Favorites:" +msgstr "Favoris:" + +msgid "Fighting" +msgstr "Combat" + +#, c-format +msgid "File not found! %s" +msgstr "Fichier introuvable ! %s" + +msgid "Filter" +msgstr "Filtrer" + +msgid "Filter Games" +msgstr "Filtrer les jeux" + +msgid "Filter by Controller" +msgstr "Filtre par manette" + +msgid "Filter by Game Type" +msgstr "Filtrer par type de jeu" + +msgid "Filter by Genre" +msgstr "Filtre par genre" + +msgid "Filter by Online Features" +msgstr "Filtre par caractérist. en ligne" + +msgid "Filter:" +msgstr "Filtrer:" + +msgid "Fishing" +msgstr "Pêche" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "Réglage de la partition ÉTENDUE..." + +msgid "Flight Sim" +msgstr "Simulation de vol" + +msgid "Football" +msgstr "Football" + +msgid "Force Devolution:" +msgstr "Forcer Devolut°:" + +msgid "Force NTSC" +msgstr "Forcer le NTSC" + +msgid "Force NTSC 480p" +msgstr "Forcer le NTSC 480p" + +msgid "Force PAL" +msgstr "Forcer le PAL" + +msgid "Force PAL 480p" +msgstr "Forcer le PAL 480p" + +msgid "Force PAL50" +msgstr "Forcer le PAL50" + +msgid "Force PAL60" +msgstr "Forcer le PAL60" + +msgid "Formatting" +msgstr "Formatage" + +#, c-format +msgid "Found %s" +msgstr "Trouvé %s" + +msgid "French" +msgstr "Français" + +msgid "Full" +msgstr "Complet" + +msgid "Futuristic Racing" +msgstr "Course futuriste" + +msgid "GB" +msgstr "Go" + +msgid "Game Default" +msgstr "Jeu par défaut" + +msgid "Game ID" +msgstr "ID de jeu" + +msgid "Game Options" +msgstr "Options du jeu" + +#, c-format +msgid "Game Options: %s" +msgstr "Options du jeu: %s" + +msgid "Game Type" +msgstr "Type de jeu" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "GameCube" + +msgid "Gamer Card:" +msgstr "Profil de joueur:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Profil de joueur #%d rapporté: %.*s" + +msgid "Genre" +msgstr "Genre" + +msgid "German" +msgstr "Allemand" + +msgid "Global Options" +msgstr "Options générales" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Guitare" + +msgid "HQ cover" +msgstr "Jaquette HQ" + +msgid "HTTP Response was without a file" +msgstr "La réponse HTTP était sans données" + +msgid "Health" +msgstr "Santé" + +msgid "Hidden Object" +msgstr "Objet caché" + +msgid "Hide" +msgstr "Masquer" + +msgid "Hide Game:" +msgstr "Masquer le jeu:" + +msgid "Hockey" +msgstr "Hockey" + +msgid "Hold button B to cancel." +msgstr "Maintenir B pour annuler." + +msgid "Home Menu" +msgstr "Menu Wii" + +msgid "Homebrew Channel" +msgstr "Chaîne Homebrew" + +msgid "Hook Type:" +msgstr "Type de Hook:" + +msgid "Hunting" +msgstr "Chasse" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "Discordance d'ID: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "Recharg. IOS Bloqué" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "L'Ouverture_IOS(%s) a échouée avec le code %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Si Cfg ne démarre pas bien la\n" +"prochaine fois, copiez boot.dol.bak\n" +"par dessus boot.dol et réessayez." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Incohérence dans l'encodage LZ77 %x" + +msgid "Infinity Base" +msgstr "Lecteur Infinity" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info du fichier: %s" + +msgid "Initializing Network..." +msgstr "Initialisation du réseau..." + +msgid "Install" +msgstr "Installer" + +msgid "Install Date" +msgstr "Date installat°" + +msgid "Install Game" +msgstr "Installer le jeu" + +msgid "Install game" +msgstr "Installer le jeu" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "ERREUR d'installation ! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Installation du jeu, patientez SVP..." + +msgid "Invalid .dol" +msgstr "Fichier .dol invalide" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Partition invalide: '%s'" + +msgid "Italian" +msgstr "Italien" + +msgid "Japanese" +msgstr "Japonais" + +msgid "Japanese Title" +msgstr "Titre japonais" + +msgid "Jump" +msgstr "Saut" + +msgid "Karaoke" +msgstr "Karaoké" + +msgid "Kart Racing" +msgstr "Course de kart" + +msgid "Keyboard" +msgstr "Clavier" + +msgid "Korean" +msgstr "Coréen" + +msgid "LED:" +msgstr "LED :" + +msgid "LEFT" +msgstr "GAUCHE" + +msgid "LOCKED!" +msgstr "VÉROUILLÉ !" + +msgid "Language:" +msgstr "Langue:" + +msgid "Last Play Date" +msgstr "Dernier accès" + +msgid "Launch Methods" +msgstr "Méthodes de lancement" + +msgid "Life Simulation" +msgstr "Simulation de vie" + +msgid "Load OK!" +msgstr "Chargement OK !" + +#, c-format +msgid "Loader Version: %s" +msgstr "Version du programme: %s" + +msgid "Loading ..." +msgstr "Chargement..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Chargement..%s\n" + +msgid "Main" +msgstr "Principal" + +msgid "Main Menu" +msgstr "Menu principal" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Vérifiez que le port USB 0 est utilisé.\n" +"(Celui le plus proche du côté sur Wii \n" +"mais sur Wii U c'est le plus éloigné)" + +msgid "Manage" +msgstr "Gérer" + +msgid "Manage Cheats" +msgstr "Gérer les codes" + +msgid "Management Sim" +msgstr "Gestion" + +msgid "Martial Arts" +msgstr "Arts martiaux" + +msgid "Microphone" +msgstr "Micro" + +msgid "Mighty Plugin" +msgstr "Plugin Mighty" + +msgid "Motion+" +msgstr "Motion Plus" + +msgid "Motorcycle Racing" +msgstr "Course de moto" + +msgid "Mounting device, please wait..." +msgstr "" +"Montage du périphérique,\n" +" veuillez patienter..." + +msgid "Music" +msgstr "Musique" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Fichier de musique à partir du dossier: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Taille du fichier de musique: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Fichier de musique: %s" + +msgid "Music: Disabled" +msgstr "Musique: Désactivée" + +msgid "Music: Enabled" +msgstr "Musique: Activée" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Musique: Recherche des fichiers %s dans: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Musique: Prochain index de fichiers à jouer: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Musique: Nombre de fichiers %s trouvés: %i" + +msgid "Music: musicArray contents: " +msgstr "Musique: contenu de CollectiondeMusique:" + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"Chemin NAND Emu :\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "Emu NAND:" + +msgid "NMM:" +msgstr "NMM :" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTE: CIOS249 inférieur à la rev10:\n" +"Le chargement de jeux à partir de carte SDHC n'est pas supporté !" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTE: CIOS249 inférieur à la rev14:\n" +"L'erreur #001 éventuelle n'est pas gérée !" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTE: CIOS249 inférieur à la rev9:\n" +"Le chargement de jeux à partir d'USB n'est pas supporté !" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTE: Vous utilisez le CIOS249 rev13:\n" +"Pour quitter le jeu vous devez redémarrer ou\n" +"éteindre la Wii avant que vous démarriez\n" +"un autre jeu ou cela bloquera ici !" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTE: Vous utilisez le CIOS249 rev14:\n" +"Il a des problèmes connus avec les jeux\n" +"double couche. Il est vivement recommandé\n" +"d'utiliser un IOS different ou une révision\n" +"pour l'installation/la lecture de ce jeu." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTE : le redémarrage est requis\n" +"pour que la MàJ prenne effet." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"NOTE: le redémarrage peut être requis\n" +"pour que le réglage prenne effet." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTE: la partition P#%d est de type ÉTENDU mais\n" +"contient un système de fichiers WBFS. C'est une\n" +"configuration invalide. Appuyez sur %s pour changer\n" +"le type de partition d'ÉTENDU à données." + +msgid "NTFS compression not supported!" +msgstr "La compression NTFS n'est pas supportée !" + +msgid "NTFS encryption not supported!" +msgstr "Le chiffrement NTFS n'est pas supporté !" + +msgid "NTSC-J patch:" +msgstr "Patch NTSC-J:" + +msgid "Neek2o Plugin" +msgstr "Plugin Neek2o" + +msgid "Network connection established." +msgstr "Connexion au réseau établie." + +msgid "Network error. Can't update gamercards." +msgstr "Erreur réseau. Impossible de mettre à jour les profils de joueur." + +msgid "New Themes" +msgstr "Nouveaux Thèmes" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Connectivité avec la Nintendo DS" + +msgid "No" +msgstr "Non" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Aucune partie de domaine dans l'URL '%s'" + +msgid "No games found!" +msgstr "Aucun jeu trouvé !" + +msgid "No partition selected!" +msgstr "Aucune partition sélectionnée !" + +msgid "No themes found." +msgstr "Aucun thème trouvé." + +msgid "No updates found." +msgstr "Aucune mise à jour trouvée." + +msgid "NoDisc:" +msgstr "SansDisque:" + +msgid "None found on disc" +msgstr "Aucun trouvé sur le disque" + +msgid "Not Found boot.dol!" +msgstr "boot.dol introuvable !" + +msgid "Not Found!" +msgstr "Introuvable !" + +msgid "Number of Online Players" +msgstr "Nombre joueurs en ligne" + +msgid "Number of Players" +msgstr "Nombre de joueurs" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK !" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (codes):" + +msgid "Ocarina Cheat Manager" +msgstr "Gestionnaire de codes Ocarina" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Erreur de code" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Codes trouvés." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Aucun code trouvé" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Erreur plus de mémoire disponible" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Recherche de codes..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Trop de codes" + +msgid "Off" +msgstr "Désactivé" + +msgid "Off-Road Racing" +msgstr "Course tout-terrain" + +msgid "On" +msgstr "Activé" + +msgid "Online" +msgstr "En ligne" + +msgid "Online Content" +msgstr "Contenu en ligne" + +msgid "Online Play" +msgstr "Jeux en ligne" + +msgid "Online Players" +msgstr "Joueurs en ligne" + +msgid "Online Score List" +msgstr "Liste de scores en ligne" + +msgid "Online Updates" +msgstr "Mises à jour en ligne" + +msgid "Open Sort" +msgstr "Ouvrir tri" + +msgid "Open Style" +msgstr "Ouvrir style" + +msgid "Opening DVD disc..." +msgstr "Ouverture du DVD..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Ouverture de la partition: %s" + +msgid "Options" +msgstr "Options" + +msgid "Options discarded for this game." +msgstr "Options annulées pour ce jeu." + +msgid "Options saved for this game." +msgstr "Options sauvegardées pour ce jeu." + +msgid "Out of memory" +msgstr "Plus de mémoire disponible" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK :" + +msgid "Page:" +msgstr "Page:" + +msgid "Partial" +msgstr "Partiel" + +msgid "Partition:" +msgstr "Partition:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partition: %s introuvable !" + +msgid "Party" +msgstr "Party Game" + +msgid "Paused Start" +msgstr "Démarrage suspendu" + +msgid "Petanque" +msgstr "Pétanque" + +msgid "Pinball" +msgstr "Flipper" + +msgid "Platformer" +msgstr "Plates-formes" + +msgid "Play Count" +msgstr "Nombre utilisat°" + +msgid "Players" +msgstr "Joueurs" + +msgid "Please insert a game disc..." +msgstr "Veuillez insérer un disque de jeu..." + +msgid "Plugin:" +msgstr "Plugin :" + +msgid "Poker" +msgstr "Poker" + +msgid "Portal of Power" +msgstr "Portail du Pouvoir" + +#, c-format +msgid "Press %s button for options." +msgstr "Appuyez sur %s pour les options." + +#, c-format +msgid "Press %s button to %s." +msgstr "Appuyez sur %s pour %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Appuyez sur %s pour appliquer les codes." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Appuyez sur %s pour annuler." + +#, c-format +msgid "Press %s button to change device." +msgstr "Faites %s pour changer de périphérique." + +#, c-format +msgid "Press %s button to continue." +msgstr "Appuyez sur %s pour continuer." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "" +"Appuyez sur %s pour supprimer le sys-\n" +"tème de fichiers." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Appuyez sur %s pour 'dumper' la BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Faites %s pour quitter." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Faites %s pour régler ÉTENDRER/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Appuyez sur %s pour formater en WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Appuyez sur %s pour revenir." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Faites %s pour choisir une partition." + +#, c-format +msgid "Press %s button to select." +msgstr "Appuyez sur %s pour choisir." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Appuyez sur %s pour passer les codes." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Appuyez sur %s pour synchroniser le FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Faire %s pour un aperçu en plein écran" + +#, c-format +msgid "Press %s for game options" +msgstr "Faites %s pour les options du jeu" + +#, c-format +msgid "Press %s for global options" +msgstr "Faites %s pour les options générales" + +#, c-format +msgid "Press %s to discard options" +msgstr "Faites %s pour annuler les options" + +#, c-format +msgid "Press %s to download and update" +msgstr "Appuyez sur %s pour téléch. et MàJ" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Faire %s pour télécharger ce thème" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Appuyez sur %s pour télécharger, %s pour revenir" + +#, c-format +msgid "Press %s to return" +msgstr "Appuyez sur %s pour revenir" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Faites %s sauve les options générales" + +#, c-format +msgid "Press %s to save options" +msgstr "Faites %s pour sauver les options" + +#, c-format +msgid "Press %s to save selection" +msgstr "Appuyez sur %s pour sauver la sélection" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Appuyez sur %s pour choisir le filtre" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "" +"Faites %s pour choisir la méthode de\n" +"tri" + +#, c-format +msgid "Press %s to start game" +msgstr "Faites %s pour démarrer le jeu" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Appuyez sur %s pour MàJ sans meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Appuyez sur %s/%s pour choisir le périphérique." + +msgid "Press 1 to skip WBFS mounting" +msgstr "Faites 1 pour passer le montage WBFS" + +msgid "Press 2 to reload IOS" +msgstr "Faites 2 pour recharger l'IOS" + +msgid "Press A to continue without config.txt" +msgstr "Faites A pour continuer sans config.txt" + +msgid "Press A to select device" +msgstr "Faites A pour choisir le périphérique" + +msgid "Press B to exit to HBC" +msgstr "Faites B pour sortir vers l'HBC " + +msgid "Press HOME to reset" +msgstr "Faites HOME pour redémarrer" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Faites GAUCHE/DROITE pour choisir l'IOS" + +msgid "Press any button to continue..." +msgstr "Appuyez sur un bouton pour continuer..." + +msgid "Press any button to exit..." +msgstr "Appuyez sur un bouton pour quitter..." + +msgid "Press any button to restart..." +msgstr "Appuyez sur un bouton pour redémarrer..." + +msgid "Press any button.\n" +msgstr "Appuyez sur un bouton.\n" + +msgid "Press any button..." +msgstr "Appuyez sur un bouton..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Appuyez sur %s pour télécharger des jaquettes." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Appuyez sur %s pour éjecter le DVD." + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Profil:" + +#, c-format +msgid "Profile: %s" +msgstr "Profil: %s" + +msgid "Program Updates" +msgstr "Mises à jour du programme" + +msgid "Publisher" +msgstr "Éditeur" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Quitter" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "DROITE" + +msgid "RPG" +msgstr "Jeux de rôle" + +msgid "Racing" +msgstr "Course" + +msgid "Rail Shooter" +msgstr "Tir sur rails" + +#, c-format +msgid "Rated %s" +msgstr "Classé %s" + +msgid "Rating" +msgstr "Note" + +msgid "Read" +msgstr "Lecture" + +msgid "Reading BCA..." +msgstr "Lecture de la BCA..." + +msgid "Reboot" +msgstr "Redémarrer" + +msgid "Region" +msgstr "Région" + +msgid "Release Date" +msgstr "Date de sortie" + +msgid "Release Notes: (short)" +msgstr "Notes de sortie: (courtes)" + +msgid "Removing game, please wait..." +msgstr "Suppression du jeu, veuillez patienter..." + +msgid "Restarting Wii..." +msgstr "Redémarrage de la Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Retourné ! (ret = %d)" + +msgid "Rhythm" +msgstr "Rythme" + +msgid "Rows:" +msgstr "Lignes:" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Test de performance en cours, veuillez patienter" + +msgid "S. Chinese" +msgstr "Chinois S." + +msgid "SD/SDHC Card" +msgstr "Carte SD/SDHC" + +msgid "SUCCESS!" +msgstr "RÉUSSI !" + +msgid "Save .gct" +msgstr "Sauvegarder .gct" + +msgid "Save Debug" +msgstr "Sauvegarder débogage" + +msgid "Save Settings" +msgstr "Sauver paramètres" + +msgid "Save debug.log" +msgstr "Sauvegarder debug.log" + +msgid "Savegame not found.\n" +msgstr "Sauvegarde introuvable.\n" + +msgid "Savegame:" +msgstr "Sauvegarde :" + +msgid "Saving Settings... " +msgstr "Sauvegarde des Paramètres..." + +msgid "Saving cheats..." +msgstr "Sauvegarde des codes..." + +msgid "Saving gamelist.txt ... " +msgstr "Sauvegarde de gamelist.txt ... " + +msgid "Saving settings..." +msgstr "Sauvegarde des paramètres..." + +msgid "Saving:" +msgstr "Sauvegarde:" + +#, c-format +msgid "Saving: %s" +msgstr "Sauvegarde: %s" + +msgid "Screenshot:" +msgstr "Capture d'écran :" + +msgid "Scroll:" +msgstr "Défilement:" + +msgid "Search" +msgstr "Recherche" + +msgid "Search for:" +msgstr "Rechercher :" + +msgid "Select Alternative .dol:" +msgstr "Choisissez .dol alternatif:" + +msgid "Select WBFS device:" +msgstr "Choisissez le périphérique WBFS:" + +msgid "Select a different partition" +msgstr "Choisissez une partition différente" + +msgid "Select a partition" +msgstr "Choisissez une partition" + +msgid "Select all" +msgstr "Sélectionner tout" + +msgid "Select device:" +msgstr "Sélectionner le périphérique :" + +msgid "Select the game you want to boot" +msgstr "Choisissez le jeu à démarrer" + +msgid "Selected Game" +msgstr "Jeu sélectionné" + +msgid "Settings" +msgstr "Paramètres" + +msgid "Shooter" +msgstr "Tir" + +msgid "Show All" +msgstr "Voir tous" + +msgid "Show cIOS info" +msgstr "Montrer les infos des cIOS" + +msgid "Shutdown" +msgstr "Éteindre" + +msgid "Side:" +msgstr "Côté:" + +msgid "Sim Racing" +msgstr "Simulation course" + +msgid "Simulation" +msgstr "Simulation" + +msgid "Size(GB) Type Mount Used" +msgstr "Taille(Go) Type Monté Utilisé" + +#, c-format +msgid "Size: %d bytes" +msgstr "Taille: %d octets" + +msgid "Skateboard" +msgstr "Skateboard" + +msgid "Skateboarding" +msgstr "Skateboard" + +msgid "Skiing" +msgstr "Ski" + +msgid "Snowboarding" +msgstr "Snowboard" + +msgid "Soccer" +msgstr "Football" + +msgid "Sort" +msgstr "Trier" + +msgid "Sort Games" +msgstr "Trier les jeux" + +msgid "Sort Order:" +msgstr "Ordre de tri:" + +msgid "Sort Type:" +msgstr "Type de tri:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Tri: %s-%s" + +msgid "Spanish" +msgstr "Espagnol" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Démarrer" + +msgid "Start Game" +msgstr "Démarrer le jeu" + +msgid "Start this game?" +msgstr "Démarrer ce jeu ?" + +msgid "Stealth Action" +msgstr "Infiltration" + +msgid "Stopping DVD..." +msgstr "Arrêt du DVD..." + +msgid "Strategy" +msgstr "Stratégie" + +msgid "Style" +msgstr "Style" + +msgid "Style:" +msgstr "Style:" + +msgid "Surfing" +msgstr "Surf" + +msgid "Survival Horror" +msgstr "Survival horror" + +msgid "Sync FAT free space info?" +msgstr "Synchroniser FAT information d'espace libre ?" + +msgid "Synchronizing, please wait." +msgstr "Synchronisation, veuillez patienter." + +msgid "Synopsis" +msgstr "Synopsis" + +msgid "Synopsis Length" +msgstr "Longueur du synopsis" + +msgid "System" +msgstr "Système" + +msgid "System Def." +msgstr "Système / défaut" + +msgid "T. Chinese" +msgstr "Chinois T." + +msgid "Table Tennis" +msgstr "Tennis de table" + +msgid "Tennis" +msgstr "Tennis" + +msgid "Theme" +msgstr "Thème" + +msgid "Theme Info" +msgstr "Info du thème" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Le thème est peut-être trop grand.\n" +"Le redémarrage de Cfg est suggéré.\n" + +msgid "Theme:" +msgstr "Thème:" + +#, c-format +msgid "Theme: %s" +msgstr "Thème: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Thèmes fournis par wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Thèmes à télécharger" + +msgid "Themes with updates" +msgstr "Thèmes avec des mises à jour" + +msgid "Title" +msgstr "Titre" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Trop de codes ! (%d)" + +msgid "Too many code lines!" +msgstr "Trop de lignes de code !" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Trop de fragments ! %d" + +msgid "Total Body Tracking" +msgstr "Cardiofréquencemètre" + +msgid "Train Simulation" +msgstr "Simulation train" + +msgid "Trivia" +msgstr "Questions/réponses" + +msgid "Truck Racing" +msgstr "Course de camion" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Essai (url#%d) ..." + +msgid "Turntable" +msgstr "Platine" + +msgid "UNUSED" +msgstr "INUTILISÉ" + +msgid "UP" +msgstr "HAUT" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "L'URL '%s' ne commence pas par 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "L'URL '%s' n'a aucune partie de CHEMIN" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko trouvé. Le débogage est activé." + +msgid "USB Mass Storage Device" +msgstr "Périphérique de stockage USB" + +msgid "Unknown" +msgstr "Inconnu" + +msgid "Unknown syntax!" +msgstr "Syntaxe inconnue !" + +msgid "Unplayed" +msgstr "Jamais lus" + +msgid "Unplayed Games" +msgstr "Jeux jamais lus" + +msgid "Update themes" +msgstr "Mettre à jour les thèmes" + +msgid "Updates" +msgstr "Mises à jour" + +msgid "Updating database." +msgstr "Mise à jour de la base de données." + +msgid "Updating devolution" +msgstr "Mise à jour de devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Utilisé: %.1fGo Libre: %.1fGo [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Utilise le .dol alternatif sauvegardé:" + +msgid "VC-Arcade" +msgstr "Cons. Virt. Arcade" + +msgid "VC-Commodore 64" +msgstr "C.V. Commodore 64" + +msgid "VC-N64" +msgstr "Cons. Virt. N64" + +msgid "VC-NES" +msgstr "Cons. Virt. Nes" + +msgid "VC-Neo Geo" +msgstr "Cons. Virt. Neo Geo" + +msgid "VC-SMS" +msgstr "CV SegaMasterSystem" + +msgid "VC-SNES" +msgstr "Cons. Virt. SNES" + +msgid "VC-Sega Genesis" +msgstr "C.V. Mega Drive" + +msgid "VC-TurboGrafx-16" +msgstr "C.V. TurboGrafx-16" + +msgid "Version" +msgstr "Version" + +msgid "Video Patch:" +msgstr "Correctif vidéo:" + +msgid "Video:" +msgstr "Vidéo:" + +msgid "View" +msgstr "Affichage" + +msgid "Virtual Pet" +msgstr "Animal virtuel" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "Volleyball" +msgstr "Volley-ball" + +msgid "WARNING:" +msgstr "AVERTISSEMENT:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGo libre sur %.1fGo" + +msgid "Watercraft Racing" +msgstr "Course motomarine" + +msgid "Wheel" +msgstr "Volant" + +msgid "Wide Screen:" +msgstr "Écran large:" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Chaîne Wii" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Lutte" + +msgid "Write Playlog:" +msgstr "Historique de jeu:" + +#, c-format +msgid "Writing to %s" +msgstr "Écriture sur %s" + +#, c-format +msgid "Writing: %s" +msgstr "Écriture: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Taille incorrecte: %d (%d)" + +msgid "Yes" +msgstr "Oui" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Vous pouvez aussi tenter de débrancher\n" +"et rebrancher le périphérique,\n" +"ou patienter juste un peu plus" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ MODIFIÉ ]" + +msgid "[ FOUND ]" +msgstr "[ TROUVÉ ]" + +msgid "[ SAVED ]" +msgstr "[ SAUVEGARDÉ ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[Pas HQ]" + +msgid "[USED]" +msgstr "[UTILISÉ]" + +msgid "[default]" +msgstr "[Défaut]" + +msgid "[saved]" +msgstr "[sauvegardé]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "mauvais fichier wbfs: %s" + +msgid "calculating space, please wait..." +msgstr "calcule l'espace, veuillez patienter..." + +msgid "delete" +msgstr "supprimer" + +#, c-format +msgid "dest: %p - %p" +msgstr "destin.: %p - %p" + +msgid "discard" +msgstr "annuler" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "le domaine %s ne peut être atteint" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "erreur de lecture de: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "pour %d joueurs" + +msgid "for 1 player" +msgstr "pour 1 joueur" + +msgid "format" +msgstr "formater" + +msgid "free" +msgstr "libre" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "vitesse de lecture linéaire: %.2f Mo/s" + +msgid "linear..." +msgstr "linéaire..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin taille : %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "le fichier de musique est trop grand (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 ou music.mod introuvables !" + +msgid "no file" +msgstr "aucun fichier" + +msgid "none" +msgstr "aucun" + +msgid "or format a WBFS partition." +msgstr "ou formater une partition WBFS." + +msgid "page" +msgstr "page" + +msgid "parse error" +msgstr "erreur d'analyse" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "vitesse de lecture aléatoire: %.2f Mo/s" + +msgid "random..." +msgstr "aléatoire..." + +msgid "reset" +msgstr "rétablir" + +msgid "revert" +msgstr "rétablir" + +msgid "save" +msgstr "sauver" + +#, c-format +msgid "split error: %s" +msgstr "erreur en scindant: %s" + +msgid "uDraw GameTablet" +msgstr "Tablette de jeu uDraw" + +msgid "unable to open wii disc" +msgstr "incapable d'ouvrir le disque wii" + +#, c-format +msgid "used: %p - %p" +msgstr "utilisé: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Démarrage du jeu, patientez SVP..." + +#~ msgid "Console Def." +#~ msgstr "Console / défaut" + +#~ msgid "DML version:" +#~ msgstr "Version DML:" + +#~ msgid "Dol Booter" +#~ msgstr "Lanceur DOL" + +#~ msgid "Download titles.txt" +#~ msgstr "Télécharger titles.txt" + +#~ msgid "Front" +#~ msgstr "Avant" + +#~ msgid "Loading previous game list..." +#~ msgstr "Charge la liste de jeux précédente..." + +#~ msgid "No Hooks" +#~ msgstr "Pas de hook" + +#~ msgid "Remove Game" +#~ msgstr "Supprimer le jeu" + +#~ msgid "Retry: %d ...\n" +#~ msgstr "Réessayer: %d ...\n" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "Mettre à jour BDD de jeux WiiTDB" + +#~ msgid "Update titles.txt" +#~ msgstr "Mettre à jour titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "Base de données de jeux WiiTDB" diff --git a/Languages/GR.lang b/Languages/GR.lang new file mode 100644 index 0000000..4d876de --- /dev/null +++ b/Languages/GR.lang @@ -0,0 +1,2355 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-26 23:18+0100\n" +"Last-Translator: xxdimixx\n" +"Language-Team: GREEK \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: Greek\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB από %.1fGB ελεÏθεÏα" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% από %.2fGB (%c) ΥΧ: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB αντιγÏάφτηκαν σε %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d διαθέσιμα" + +#, c-format +msgid "%d more notes" +msgstr "%d πεÏισσότεÏες σημειώσεις" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s ΧώÏισμα: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, παÏακαλώ πεÏιμένετε..." + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d δευτ. timeout)" + +msgid "(This can take a couple of minutes)" +msgstr "(Ίσως να αÏγήσει λίγα λεπτά)" + +msgid ".dol too small" +msgstr ".dol Ï€Î¿Î»Ï Î¼Î¹ÎºÏÏŒ" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "1st-Person Shooter" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "3D cover" + +msgid "3rd-person Shooter" +msgstr "3rd-person Shooter" + +msgid "< ASC >" +msgstr "< ΑÎΟΔ. >" + +msgid "< DESC >" +msgstr "< ΚΑΘΟΔ. >" + +msgid "< DOWNLOAD >" +msgstr "< ΚΑΤΕΒΑΣΜΑ >" + +msgid "About" +msgstr "ΠεÏί" + +msgid "Action" +msgstr "Action" + +msgid "Additional config:" +msgstr "ΠÏόσθετες Ïυθμίσεις:" + +msgid "Admin Lock:" +msgstr "Κλείδωμα άντμιν" + +msgid "Admin Unlock" +msgstr "Ξεκλείδωμα άντμιν" + +msgid "Adult" +msgstr "Ενήλικας" + +msgid "Adventure" +msgstr "ΠεÏιπέτεια" + +msgid "All" +msgstr "Όλα" + +msgid "All Channels" +msgstr "Όλα τα κανάλια" + +msgid "All Games" +msgstr "Όλα τα παιχνίδια" + +msgid "All themes up to date." +msgstr "Όλα τα Θέματα είναι επίκαιÏα" + +msgid "Alt Button Cfg:" +msgstr "Εναλ. πληκτÏÏŒ Cfg" + +msgid "Alt dol:" +msgstr "Εναλ. dol" + +msgid "Alternative .dol:" +msgstr "Εναλλακτικό .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix:" + +#, c-format +msgid "App. Path: %s" +msgstr "App. Path: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Είστε σίγουÏος πως θέλετε να %s\n" +"αυτό το partition;" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Είστε σίγουÏος πως θέλετε να επισκευάσετε \n" +"αυτό το partition;" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Είστε σίγουÏος πως θέλετε να διαγÏάψετε αυτό\n" +"το παιχνίδι;" + +msgid "Ascending" +msgstr "Ανοδικά" + +msgid "Auto" +msgstr "Αυτόματα" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Αυτόματο ξεκίνημα παιχνιδιοÏ: %.6s δεν εβÏέθηκε!" + +msgid "Available Updates" +msgstr "Διαθέσιμες αναβαθμίσεις" + +msgid "Back" +msgstr "Πίσω" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Μπέιζμπολ" + +msgid "Basic" +msgstr "Βασικό" + +msgid "Basketball" +msgstr "Μπασκετμπόλ" + +msgid "Bike Racing" +msgstr "Αγώνες ποδηλάτων" + +msgid "Billiards" +msgstr "ΜπιλιάÏδο" + +msgid "Block IOS Reload:" +msgstr "Μπλοκ IOS Reload:" + +msgid "Board Game" +msgstr "Board Game" + +msgid "Boot Disc" +msgstr "ΈναÏξη Disc" + +msgid "Boot disc" +msgstr "ΈναÏξη disc" + +msgid "Booting game, please wait..." +msgstr "Εκκίνηση παιχνιδιοÏ, παÏακαλώ πεÏιμένετε" + +msgid "Bowling" +msgstr "Μποουλινγκ" + +msgid "Boxing" +msgstr "Μποξ" + +msgid "Business Sim" +msgstr "Business Sim" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG βάση: %s" + +msgid "Camera" +msgstr "ΚάμεÏα" + +msgid "Can not dump GC savegames.\n" +msgstr "ΑδÏνατον το dump των GC savegame.\n" + +msgid "Can't install Wii games!" +msgstr "ΑδÏνατον η εγκατάσταση του Wii παιχνιδιοÏ!" + +msgid "Cancelled." +msgstr "ΑκυÏόθηκε." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "ΑδÏνατον Η δημιουÏγία ενώς φακέλου: %s" + +msgid "Cards" +msgstr "ΧαÏτιά" + +msgid "Channel" +msgstr "Κανάλι" + +msgid "Cheat Codes:" +msgstr "Cheat Codes:" + +msgid "Cheats: " +msgstr "Cheats: " + +msgid "Check For Updates" +msgstr "Ψάξιμο για αναβαθμίσεις" + +msgid "Checking for themes..." +msgstr "Ψάξιμο για θέματα..." + +msgid "Checking for updates..." +msgstr "Ψάξιμο για αναβαθμίσεις..." + +msgid "Chess" +msgstr "Σκάκι" + +msgid "Choose a sorting method" +msgstr "διαλέξτε μέθοδο ταξινόμισης" + +msgid "Classic" +msgstr "Κλασικό" + +msgid "Classic Controller" +msgstr "Classic Controller" + +msgid "Clear" +msgstr "ΔιαγÏαφή" + +msgid "Clear Patches:" +msgstr "ΔιαγÏαφή Patches:" + +msgid "Coaching" +msgstr "Coaching" + +msgid "Compare Type" +msgstr "ΣÏγκÏιση Ï„Ïπων" + +msgid "Compilation" +msgstr "Compilation" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Έτοιμο. Μέγεθος: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Λάθος ÏÏθμισης, MAX_DNS_ENTRIES έφτασαν καθός είναι άδια η λίστα." + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Λάθος σÏνδεσης από net_read() Errorcode: %i" + +msgid "Console" +msgstr "Κονσόλα" + +msgid "Construction Sim" +msgstr "Construction Sim" + +msgid "Contains" +msgstr "ΠεÏιέχει" + +msgid "Controller" +msgstr "Controller" + +msgid "Cooking" +msgstr "ΜαγείÏεμα" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Η έναÏξη του DIP Modul δεν ήταν δυνατόν! (ret= %d)" + +msgid "Country Fix:" +msgstr "Country Fix:" + +msgid "Cover" +msgstr "Cover" + +msgid "Cover Image:" +msgstr "Εικόνα cover:" + +msgid "Cover Style" +msgstr "Cover Style" + +msgid "Covers Available" +msgstr "Διαθέσιμα cover" + +msgid "Cover~~Back" +msgstr "Cover~~πίσω" + +msgid "Cover~~Front" +msgstr "Cover~~μπÏοστινό" + +msgid "Create" +msgstr "ΔημιουÏγία" + +msgid "Creator" +msgstr "ΔημιουÏγός" + +msgid "Cricket" +msgstr "ΚÏίκετ" + +#, c-format +msgid "Current Version: %s" +msgstr "ΕπίκαιÏη έκδοση: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Το custom IOS %d δεν βÏέθηκε!\n" +"ΠαÏακαλό εγκαταστήστε το." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Το Custom IOS %d δεν μποÏοÏσε να φοÏτωθεί! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Το custom IOS %d είναι stub!\n" +"ΠαÏακαλώ επανεγκαταστήστε το." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "ΦόÏτωμα custom IOS %s OK" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "DISC cover" + +msgid "DOWN" +msgstr "ΚΑΤΩ" + +msgid "Dance" +msgstr "ΧοÏός" + +msgid "Dance Pad" +msgstr "Dance Pad" + +msgid "Darts" +msgstr "Βελάκια" + +msgid "Database update successful." +msgstr "Ανανέωση της Ï„Ïάπεζας πληÏοφωÏιών επιτυχάνθηκε." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "ΔιαγÏαφή παιχνιδιοÏ" + +msgid "Deleting" +msgstr "ΔιαγÏαφή" + +msgid "Descending" +msgstr "Καθοδικά" + +msgid "Developer" +msgstr "ΠÏογÏαμματιστής" + +msgid "Device is not responding!" +msgstr "Η μονάδα δεν αντιδÏά" + +msgid "Device:" +msgstr "Μονάδα:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Το Devolution δέχεται μόνο καθαÏά dumps!\n" + +msgid "Devolution:" +msgstr "Devolution" + +msgid "Disc" +msgstr "Disc" + +msgid "Disc (Ask)" +msgstr "Disc (Ask)" + +msgid "Done." +msgstr "Έτοιμο." + +msgid "Download .txt" +msgstr "Κατέβασμα .txt" + +msgid "Download All Covers" +msgstr "Κατέβασμα όλων των cover" + +msgid "Download All Missing Covers" +msgstr "Κατέβασε όλα τα απόν Cover" + +msgid "Download Missing Covers" +msgstr "Κατέβασμα των λειπών cover" + +msgid "Download Plugins" +msgstr "Κατέβασμα των πλάγκινς" + +msgid "Download Themes" +msgstr "Κατέβασμα θεμάτων" + +msgid "Download complete." +msgstr "Download ολοκληÏώθηκε." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Λάθος στο κατέβασμα στην gamercard #%d." + +msgid "Download game information" +msgstr "Κατέβασμα πληÏοφοÏιών παιχνιδιοÏ" + +msgid "Downloadable Content" +msgstr "Κατεβάσιμο πεÏιεχόμενο" + +msgid "Downloading ALL MISSING covers" +msgstr "Κατέβασμα απόν cover" + +msgid "Downloading ALL covers" +msgstr "Κατέβασμα όλων των cover" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Κατέβασμα όλων των cover για %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Κατέβασμα απόν Cover για %.6s" + +msgid "Downloading Theme previews..." +msgstr "Κατέβασμα Ï€Ïοεπίδειξη θέματος" + +msgid "Downloading cheats..." +msgstr "Κατεβάσμα cheats..." + +msgid "Downloading database." +msgstr "Κατέβασμα Ï„Ïάπεζα πληÏοφωÏιών." + +msgid "Downloading devolution." +msgstr "Κατέβασμα devolution" + +msgid "Downloading mighty plugin." +msgstr "Κατέβασμα mighty plugin" + +msgid "Downloading neek2o plugin." +msgstr "Κατέβασμα neek2o plugin" + +msgid "Downloading titles.txt ..." +msgstr "Κατέβασμα titles.txt ..." + +msgid "Downloading translation file." +msgstr "Κατέβασμα αÏχείων μεταγλώττισης" + +msgid "Downloading unifont." +msgstr "Κατέβασμα unifont." + +#, c-format +msgid "Downloading: %s" +msgstr "Κατέβασμα: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Κατέβασμα: %s as %s" + +msgid "Downloads" +msgstr "Λήψεις" + +msgid "Drawing" +msgstr "Σχεδιασμός" + +msgid "Drums" +msgstr "ΤÏμπανα" + +msgid "Dump savegame" +msgstr "Dump savegame" + +msgid "Duplicate ID3" +msgstr "ΑντιγÏαφή ID3" + +msgid "Dutch" +msgstr "Ολλανδικά" + +msgid "ERROR" +msgstr "ΛΑΘΟΣ" + +msgid "ERROR creating file" +msgstr "ΛΑΘΟΣ δημιουÏγία αÏχείου" + +msgid "ERROR game opt" +msgstr "ΛΑΘΟΣ επιλογές παιχνιδιών" + +msgid "ERROR reading BCA!" +msgstr "ΛΑΘΟΣ διάβασμα BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "ΛΑΘΟΣ διαγÏαφή από %s" + +msgid "ERROR writing BCA!" +msgstr "ΛΑΘΟΣ εγγÏαφή BCA!" + +msgid "ERROR!" +msgstr "ΛΑΘΟΣ!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ΛΑΘΟΣ! (ret = %d)" + +msgid "ERROR:" +msgstr "ΛΑΘΟΣ:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ΛΑΘΟΣ: %s δεν είναι Ï€Ïοσβάσιμο" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ΛΑΘΟΣ: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ΛΑΘΟΣ: Το CIOS222/223 v4 είναι απαÏέτητο για BCA" + +msgid "ERROR: Cache Close" +msgstr "ΛΑΘΟΣ: Κλείσιμο cache" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ΛΑΘΟΣ: Το ξεκίνημα Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï Î´ÎµÎ½ ήταν δυνατόν! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ΛΑΘΟΣ: Το παιχνίδι έχει είδη εγκατασταθεί!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ΛΑΘΟΣ: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ΛΑΘΟΣ: ΆκυÏο Game ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ΛΑΘΟΣ: ΦόÏτωμα EHC module! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ΛΑΘΟΣ: ΓÏάψιμο σε NTFS απενεÏγοποιημένο!\n" +"(βάλτε ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ΛΑΘΟΣ: Δεν βÏέθηκαν partitions! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ΛΑΘΟΣ: Δεν Ï€Ïόκειτε για Wii Disk!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ΛΑΘΟΣ: Offset (0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ΛΑΘΟΣ: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ΛΑΘΟΣ: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ΛΑΘΟΣ: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ΛΑΘΟΣ: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ΛΑΘΟΣ: Setting SD mode" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ΛΑΘΟΣ: USB init! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "ΛΑΘΟΣ: WBFS not mounted" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"ΛΑΘΟΣ: για εκκίνηση από FAT partition\n" +"απαιτείται cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 ή υψηλότεÏο\n" +"Αναβαθμίστε IOS249 ή επιλέξτε διαφοÏετικό IOS." + +msgid "ERROR: cache: out of memory" +msgstr "ΛΑΘΟΣ: Cache: δεν υπάÏχει πεÏίσιος χώÏος στη μνήμη" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ΛΑΘΟΣ: δημιουÏγία: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ΛΑΘΟΣ: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "ΛΑΘΟΣ: επικάλυψη μνήμης!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ΛΑΘΟΣ: πολαπλά WBFS Partitions\n" +"υποστηÏίζονται μόνο με το CIOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ΛΑΘΟΣ: δεν υπάÏχει ελεÏθεÏος χώÏος!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ΛΑΘΟΣ: το ntfs δεν αφαιÏέθηκε σωστά" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ΛΑΘΟΣ: άνοιγμα %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ΛΑΘΟΣ: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ΛΑΘΟΣ: setting up fragments %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "ΛΑΘΟΣ: η μονάδα ή το partition %s/ δεν έχει συνδεθεί!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ΛΑΘΟΣ: γÏάψιμο %s (%d)." + +msgid "EXTEND" +msgstr "επέκταση" + +msgid "Educational" +msgstr "Εκπαιδευτικά" + +msgid "Ejecting DVD..." +msgstr "Εξαγωγή DVD..." + +msgid "English" +msgstr "Αγγλικά" + +msgid "English Title" +msgstr "Αγγλικός τίτλος" + +msgid "Enter Code: " +msgstr "Εισάγετε κωδικό: " + +msgid "Error GRRLIB init" +msgstr "Λάθος GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Λάθος στην αÏχικοποίηση δικτÏου." + +msgid "Error adding disc!" +msgstr "Λάθος στην Ï€Ïόσθεση του disc!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Λάθος στην εκχώÏηση μνήμης" + +msgid "Error creating directory..." +msgstr "Λάθος στην δημιουÏγία φακέλου" + +msgid "Error discarding options!" +msgstr "Λάθος στην αποÏÏίψη των επιλογών!" + +msgid "Error downloading theme preview..." +msgstr "Λάθος στο κατέβασμα για Ï€Ïοεπίδειξη θεμάτων" + +msgid "Error downloading theme..." +msgstr "Λάθος στο κατέβασμα του θέματος" + +msgid "Error downloading themes..." +msgstr "Λάθος στο κατέβασμα των θεμάτων" + +msgid "Error downloading update..." +msgstr "Λάθος στο κατέβασμα αναβάθμισης..." + +msgid "Error downloading updates..." +msgstr "Λάθος στο κατέβασμα αναβαθμίσεων..." + +msgid "Error downloading." +msgstr "Λάθος στο κατέβασμα." + +msgid "Error establishing connection" +msgstr "Η δημιουÏγία σÏνδεσης απέτυχε" + +msgid "Error extracting theme..." +msgstr "Λάθος στην εξαγωγή του θέματος" + +msgid "Error opening database, update did not complete." +msgstr "" +"ΛΑΘΟΣ στο άνοιγμα Ï„Ïάπεζα πληÏοφωÏιών,\n" +" αναβάθμιση μη ολοκληÏομένη." + +#, c-format +msgid "Error opening: %s" +msgstr "Λάθος στο άνοιγμα: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Λάθος στο παίξιμο %s" + +msgid "Error reading .dol" +msgstr "Λάθος στην ανάγνωση .dol" + +msgid "Error reading dol header" +msgstr "Λάθος στην ανάγνωση του dol header!" + +#, c-format +msgid "Error saving %s" +msgstr "Λάθος στην αποθήκευση %s" + +msgid "Error saving options!" +msgstr "Λάθος στην αποθήκευση επιλογών!" + +msgid "Error saving settings!" +msgstr "Λάθος στην αποθήκευση Ïυθμίσεων!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Λάθος στην αποθήκευση του αÏχείου Playlog.\n" +"Ξεκινήστε από το Wii-Menu για να αποφÏγετε το λάθος." + +msgid "Error: Invalid PNG image!" +msgstr "Λάθος: ΆκυÏη PNG εικόνα!" + +msgid "Error: no URL." +msgstr "Λάθος: δεν υπάÏχει URL." + +msgid "Error: no data." +msgstr "Λάθος: δεν υπάÏχουν δεδομένα." + +msgid "Exercise" +msgstr "Άσκηση" + +msgid "Exit" +msgstr "Έξοδος" + +#, c-format +msgid "Extracting: %s" +msgstr "Εξαγωγή: %s" + +msgid "FAIL" +msgstr "ΑΠΕΤΥΧΕ" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC ERROR %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "ΚΡΙΣΙΜΟ: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "ΣΗΜΠΛΗΡΩΣΤΕ %d" + +msgid "FLAT cover" +msgstr "FLAT cover" + +msgid "FULL cover" +msgstr "FULL cover" + +msgid "Fav" +msgstr "Αγαπ." + +msgid "Fav: Off" +msgstr "Αγαπ.: Off" + +msgid "Fav: On" +msgstr "Αγαπ.: On" + +msgid "Favorite" +msgstr "Αγαπημένο" + +msgid "Favorite Games" +msgstr "Αγαπημένα παιχνίδια" + +msgid "Favorite:" +msgstr "Αγαπημένο:" + +msgid "Favorites:" +msgstr "Αγαπημένα:" + +msgid "Fighting" +msgstr "Παιχνίδια πάλης" + +#, c-format +msgid "File not found! %s" +msgstr "Το αÏχείο δεν βÏέθηκε! %s" + +msgid "Filter" +msgstr "ΦίλτÏο" + +msgid "Filter Games" +msgstr "ΦιλτÏάÏισμα παιχνιδιών" + +msgid "Filter by Controller" +msgstr "ΦιλτÏάÏισμα με Controller" + +msgid "Filter by Game Type" +msgstr "ΦιλτÏάÏισμα με Ï„Ïπο παιχνιδιοÏ" + +msgid "Filter by Genre" +msgstr "ΦιλτÏάÏισμα με Genre" + +msgid "Filter by Online Features" +msgstr "ΦιλτÏάÏισμα με Online Features" + +msgid "Filter:" +msgstr "ΦίλτÏο:" + +msgid "Fishing" +msgstr "ΨάÏεμα" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "Επισκευή επεκταμένου Partition..." + +msgid "Flight Sim" +msgstr "ΠÏοσομοίωση πτήσης" + +msgid "Football" +msgstr "ΦοÏτμπολ" + +msgid "Force Devolution:" +msgstr "Εξαναγκασμός Devolution:" + +msgid "Force NTSC" +msgstr "Εξαναγκασμός NTSC" + +msgid "Force NTSC 480p" +msgstr "Εξαναγκασμός NTSC 480p" + +msgid "Force PAL" +msgstr "Εξαναγκασμός PAL" + +msgid "Force PAL 480p" +msgstr "Εξαναγκασμός PAL 480p" + +msgid "Force PAL50" +msgstr "Εξαναγκασμός PAL50" + +msgid "Force PAL60" +msgstr "Εξαναγκασμός PAL60" + +msgid "Formatting" +msgstr "ΜοÏφοποίηση" + +#, c-format +msgid "Found %s" +msgstr "Î’Ïέθηκε %s" + +msgid "French" +msgstr "Γαλλικά" + +msgid "Full" +msgstr "Full" + +msgid "Futuristic Racing" +msgstr "Μελλοντικά αγωνιστικά" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Default παιχνίδη" + +msgid "Game ID" +msgstr "Game ID" + +msgid "Game Options" +msgstr "Επιλογές παιχνιδιοÏ" + +#, c-format +msgid "Game Options: %s" +msgstr "Επιλογές παιχνιδιοÏ: %s" + +msgid "Game Type" +msgstr "ΤÏπος παιχνιδιοÏ" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d reported: %.*s" + +msgid "Genre" +msgstr "Είδος" + +msgid "German" +msgstr "ΓεÏμανικά" + +msgid "Global Options" +msgstr "Γενικές Ïυθμίσεις" + +msgid "Golf" +msgstr "Γκολφ" + +msgid "Guitar" +msgstr "ΚιθάÏα" + +msgid "HQ cover" +msgstr "HQ cover" + +msgid "HTTP Response was without a file" +msgstr "Απάντηση HTTP χωÏίς αÏχείο" + +msgid "Health" +msgstr "Υγεία" + +msgid "Hidden Object" +msgstr "ΚÏυμμένο αντικείμενο" + +msgid "Hide" +msgstr "ΑπόκÏυψη" + +msgid "Hide Game:" +msgstr "ΑπόκÏυψη παιχνιδιοÏ:" + +msgid "Hockey" +msgstr "Χόκευ" + +msgid "Hold button B to cancel." +msgstr "ΚÏατήστε B πατημένο για ακÏÏοση." + +msgid "Home Menu" +msgstr "ΠληκτÏÏŒ Menu" + +msgid "Homebrew Channel" +msgstr "Κανάλι Homebrew" + +msgid "Hook Type:" +msgstr "ΤÏπος Hook:" + +msgid "Hunting" +msgstr "Κυνήγι" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID mismatch: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Reload: μπλοκάÏισμα" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS-Open(%s) απέτυχε code %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Αν υπάÏχει Ï€Ïόβλημα με την έναÏξη Cfg\n" +"αντικαταστήστε το boot.dol με\n" +"το boot.dol.bak και ξαναπÏοσπαθήστε." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Ασυνέπεια στην κωδικοποίηση LZ77 %x" + +msgid "Infinity Base" +msgstr "Infinity Base" + +msgid "Info" +msgstr "ΠληÏοφοÏίες" + +#, c-format +msgid "Info file: %s" +msgstr "ΑÏχείο πληÏοφοÏιών: %s" + +msgid "Initializing Network..." +msgstr "Initializing Network..." + +msgid "Install" +msgstr "Εγκατάσταση" + +msgid "Install Date" +msgstr "ΗμεÏομηνία εγκατάστασης" + +msgid "Install Game" +msgstr "Εγκατάσταση παιχνιδιοÏ" + +msgid "Install game" +msgstr "Εγκατάσταση παιχνιδιοÏ" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Λάθος στην εγκατάσταση! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Εγκατάσταση παιχνιδιοÏ, παÏακαλώ πεÏιμένετε..." + +msgid "Invalid .dol" +msgstr "ΆκυÏο .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "ΆκυÏο partition '%s'" + +msgid "Italian" +msgstr "Ιταλικά" + +msgid "Japanese" +msgstr "Ιαπωνέζικα" + +msgid "Japanese Title" +msgstr "Ιαπωνέζικος τίτλος" + +msgid "Jump" +msgstr "Jump" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Αγώνες καÏÏ„" + +msgid "Keyboard" +msgstr "ΠληκτÏολόγιο" + +msgid "Korean" +msgstr "ΚοÏεάτικα" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "ΑΡΙΣΤΕΡΑ" + +msgid "LOCKED!" +msgstr "ΚΛΕΙΔΩΜΕÎΟ!" + +msgid "Language:" +msgstr "Γλώσσα: " + +msgid "Last Play Date" +msgstr "Παίχθηκε τελευταία" + +msgid "Launch Methods" +msgstr "Μέθοδοι έναÏξης" + +msgid "Life Simulation" +msgstr "ΠÏοσομοίωση ζωής" + +msgid "Load OK!" +msgstr "ΦόÏτωμα OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Loader Version: %s" + +msgid "Loading ..." +msgstr "ΦόÏτωμα ..." + +#, c-format +msgid "Loading..%s\n" +msgstr "ΦόÏτωμα..%s\n" + +msgid "Main" +msgstr "ΚÏÏιο" + +msgid "Main Menu" +msgstr "ΚÏÏιο ΜενοÏ" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"ΣιγουÏευτείτε για την χÏησιμοποίηση του USB Port 0!\n" +"(USB Port κοντά στην άκÏη)" + +msgid "Manage" +msgstr "ΔιαχειÏίση" + +msgid "Manage Cheats" +msgstr "ΔιαχειÏίση Cheats" + +msgid "Management Sim" +msgstr "ΔιαχειÏίση Sim" + +msgid "Martial Arts" +msgstr "Martial Arts" + +msgid "Microphone" +msgstr "ΜικÏόφωνο" + +msgid "Mighty Plugin" +msgstr "Mighty Plugin" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Αγώνες μηχανών" + +msgid "Mounting device, please wait..." +msgstr "Mounting μονάδας, παÏακαλώ πεÏιμένετε.." + +msgid "Music" +msgstr "Μουσική" + +#, c-format +msgid "Music file from dir: %s" +msgstr "ΑÏχείο μουσικής από φάκελο: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Μέγεθος αÏχείου μουσικής: %d" + +#, c-format +msgid "Music file: %s" +msgstr "ΑÏχείο μουσικής: %s" + +msgid "Music: Disabled" +msgstr "Μουσική: Κλειστή" + +msgid "Music: Enabled" +msgstr "Μουσική: ανοιχτή" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Μουσική: Ψάξιμο %s αÏχείων: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Μουσική: Επόμενο αÏχείο για παίξιμο: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Μουσική: Î’Ïεθήκαν %s αÏχεία: %i" + +msgid "Music: musicArray contents: " +msgstr "Musik: Συλλογή μουσικής πεÏιέχει:" + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"NAND Emu Path:\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "NAND Emu:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"ΠÏοσοχή: CIOS249 Ï€Ïιν από rev10:\n" +"ΦόÏτωμα από SDHC δεν υποστηÏίζεται!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"ΠÏοσοχή: CIOS249 Ï€Ïιν από rev14:\n" +"ΧωÏίς διόÏθωση ERROR #001!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"ΠÏοσοχή: CIOS249 Ï€Ïιν από rev9:\n" +"Παιχνίδια από USB δεν υποστηÏίζονται!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"ΠÏοσοχή: χÏησιμοποιείτε το CIOS249 rev13:\n" +"ΤεÏματίστε το παιχνίδι μέσω επανεκκίνησης ή\n" +"τεÏματισμοÏ, αλλιώς η Wii θα κολλήσει, με\n" +"την έναÏξη ενός άλλου παιχνιδιοÏ!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"ΠÏοσοχή: χÏησιμοποιείται το CIOS 249 rev 14 :\n" +"Έχει γνωστά Ï€Ïοβλήματα με DVD-9\n" +"(Dual Layer) δίσκους. Για παίξημο και\n" +"εγκατάσταση αυτών των παιχνιδιών\n" +"συνιστάται ανώτεÏο CIOS!" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"ΠÏοσοχή: επανεκκίνηση αναγκαστική\n" +"για την ολοκλήÏωση της αναβάθμισης." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"NOTE: Ίσως να χÏειαστεί η επανεκκίνηση του loader\n" +"για την αποδοχή Ïυθμίσεων." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"ΠÏοσοχή: Το partition P#%d είναι επεκταμένο αλλά\n" +"πεÏιέχει WBFS σÏστημα αÏχείων. Αυτό είναι\n" +"άκυÏο setup. Πατήστε %s για να\n" +"αλλάξετε τον Ï„Ïπο του partition από επεκταμένο σε DATA." + +msgid "NTFS compression not supported!" +msgstr "Η συμπίεση NTFS δεν υποστηÏίζεται" + +msgid "NTFS encryption not supported!" +msgstr "Η κωδικοποίηση NTFS δεν υποστηÏίζεται" + +msgid "NTSC-J patch:" +msgstr "NTSC-J patch:" + +msgid "Neek2o Plugin" +msgstr "Neek2o Plugin" + +msgid "Network connection established." +msgstr "ΣÏνδεση δικτÏου OK." + +msgid "Network error. Can't update gamercards." +msgstr "Λάθος δικτÏου. Αναβάθμιση των gamercards αδÏνατον" + +msgid "New Themes" +msgstr "Îέα θέματα" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS Connectivity" + +msgid "No" +msgstr "Όχι" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "No domain part in URL '%s'" + +msgid "No games found!" +msgstr "Δεν βÏέθηκαν παιχνίδια!" + +msgid "No partition selected!" +msgstr "Δεν επιλέξατε partition!" + +msgid "No themes found." +msgstr "Δεν βÏέθηκαν θέματα" + +msgid "No updates found." +msgstr "Δεν βÏέθηκαν αναβαθμίσης." + +msgid "NoDisc:" +msgstr "NoDisc:" + +msgid "None found on disc" +msgstr "Δεν βÏέθηκε στο δίσκο" + +msgid "Not Found boot.dol!" +msgstr "Δεν βÏέθηκε boot.dol!" + +msgid "Not Found!" +msgstr "Δεν βÏέθηκε!" + +msgid "Number of Online Players" +msgstr "ΑÏιθμός των online παικτών" + +msgid "Number of Players" +msgstr "ΑÏιθμός των παικτών" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (cheats):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina Cheat Manager" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Λάθος κωδικοÏ" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Κωδικοί βÏέθηκαν." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Δεν βÏέθηκαν κωδικοί" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Η μνήμη γέμισε" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Ψάξιμο κωδικών..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Πολλοί κωδικοί" + +msgid "Off" +msgstr "ΑπενεÏγά" + +msgid "Off-Road Racing" +msgstr "Off-Road αγωνιστικά" + +msgid "On" +msgstr "ΕνεÏγά" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Online Content" + +msgid "Online Play" +msgstr "Online Play" + +msgid "Online Players" +msgstr "Online παίκτες" + +msgid "Online Score List" +msgstr "Online-Λίστα πόντων" + +msgid "Online Updates" +msgstr "Online αναβαθμίσεις" + +msgid "Open Sort" +msgstr "ΕλεÏθεÏη ταξινόμηση" + +msgid "Open Style" +msgstr "ΕλεÏθεÏο στυλ" + +msgid "Opening DVD disc..." +msgstr "Άνοιγμα DVD Disk..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Άνοιγμα partition: %s" + +msgid "Options" +msgstr "Επιλογές" + +msgid "Options discarded for this game." +msgstr "ΑκυÏώθηκαν οι επιλογές του παιχνιδιοÏ." + +msgid "Options saved for this game." +msgstr "ΑποθηκεÏτηκαν οι επιλογές του παιχνιδιοÏ." + +msgid "Out of memory" +msgstr "Η μνήμη είναι γεμάτη" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK:" + +msgid "Page:" +msgstr "Σελίδα:" + +msgid "Partial" +msgstr "" + +msgid "Partition:" +msgstr "Partition:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partition: %s δεν βÏέθηκε!" + +msgid "Party" +msgstr "Party" + +msgid "Paused Start" +msgstr "Paused Start" + +msgid "Petanque" +msgstr "Petanque" + +msgid "Pinball" +msgstr "Pinball" + +msgid "Platformer" +msgstr "Platformer" + +msgid "Play Count" +msgstr "Παίχθηκε" + +msgid "Players" +msgstr "Παίκτες" + +msgid "Please insert a game disc..." +msgstr "ΠαÏακαλώ εισαγάγετε Wii game disc..." + +msgid "Plugin:" +msgstr "Πλαγκιν" + +msgid "Poker" +msgstr "ΠόκεÏ" + +msgid "Portal of Power" +msgstr "Portal of Power" + +#, c-format +msgid "Press %s button for options." +msgstr "Πατήστε %s για επιλογές" + +#, c-format +msgid "Press %s button to %s." +msgstr "Πατήστε %s για %s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Πατήστε %s για την αποδοχή κωδικών" + +#, c-format +msgid "Press %s button to cancel." +msgstr "Πατήστε %s για ακÏÏοση" + +#, c-format +msgid "Press %s button to change device." +msgstr "Πατήστε %s: Αλλαγή μονάδας" + +#, c-format +msgid "Press %s button to continue." +msgstr "Πατήστε %s: συνέχεια" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Πατήστε %s: ΔιαγÏαφή σÏστημα αÏχείων" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Πατήστε %s για γÏάψιμο BCA" + +#, c-format +msgid "Press %s button to exit." +msgstr "Πατήστε %s για έξοδο" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Πατήστε %s: επισκευή επεκταμένο/WBFS" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Πατήστε %s: ΜοÏφοποίηση WBFS" + +#, c-format +msgid "Press %s button to go back." +msgstr "Πατήστε %s για επιστÏοφή" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Πατήστε %s: επιλέξτε partition" + +#, c-format +msgid "Press %s button to select." +msgstr "Πατήστε %s: επιλογή" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Πατήστε %s:skip codes" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Πατήστε %s για sync FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Πατήστε %s για Ï€Ïοεπίδειξη πλήÏης οθόνης" + +#, c-format +msgid "Press %s for game options" +msgstr "Πατήστε %s για επιλογές του παιχνιδιοÏ" + +#, c-format +msgid "Press %s for global options" +msgstr "Πατήστε %s για γενικές επιλογές" + +#, c-format +msgid "Press %s to discard options" +msgstr "Πατήστε %s: απόÏÏιψη των επιλογών" + +#, c-format +msgid "Press %s to download and update" +msgstr "Πατήστε %s για κατέβαζμα και αναβάθμιση" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Πατήστε %s για κατέβαζμα του θέματος" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Πατήστε: %s = κατέβαζμα, %s = πίσω" + +#, c-format +msgid "Press %s to return" +msgstr "Πατήστε %s για επιστÏοφή" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Πατήστε %s για αποθήκευση γενικών Ïυθμίσεων" + +#, c-format +msgid "Press %s to save options" +msgstr "Πατήστε %s για αποθήκευση των επιλογώς" + +#, c-format +msgid "Press %s to save selection" +msgstr "Πατήστε %s για αποθήκευση επιλογής" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Πατήστε %s: Επιλογή Ï„Ïπου φίλτÏου" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Πατήστε %s: επιλογή μεθόδου ταξινόμισης" + +#, c-format +msgid "Press %s to start game" +msgstr "Πατήστε %s: έναÏξη παιχνιδιοÏ" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Πατήστε %s: Αναβάθμιση χωÏίς meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Πατήστε αÏιστεÏά/δεξιά: επιλογή μονάδας" + +msgid "Press 1 to skip WBFS mounting" +msgstr "Πατήστε 1 για να παÏαλείψετε το WBFS mounting" + +msgid "Press 2 to reload IOS" +msgstr "Πατήστε 2 για να επαναφοÏτωθεί το IOS" + +msgid "Press A to continue without config.txt" +msgstr "Πατήστε A για συνέχεια χωÏίς config.txt" + +msgid "Press A to select device" +msgstr "Πατήστε A για επιλογή της μονάδας" + +msgid "Press B to exit to HBC" +msgstr "Πατήστε B για έξοδο στο HBC" + +msgid "Press HOME to reset" +msgstr "Πατήστε HOME για επαναφοÏά" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Πατήστε LEFT/RIGHT για επιλογή του IOS" + +msgid "Press any button to continue..." +msgstr "Πατήστε πλήκτÏο για συνέχεια..." + +msgid "Press any button to exit..." +msgstr "Πατήστε ΠλήκτÏο επιλόγης για έξοδο..." + +msgid "Press any button to restart..." +msgstr "Πατήστε ΠλήκτÏο επιλόγης για επανεκκίνηση..." + +msgid "Press any button.\n" +msgstr "Πατήστε ένα πλήκτÏο.\n" + +msgid "Press any button..." +msgstr "Πατήστε ένα πλήκτÏο..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Πατήστε πλήκτÏο %s για κατέβασμα των cover" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Πατήστε %s: eject DVD" + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "ΠÏοφίλ:" + +#, c-format +msgid "Profile: %s" +msgstr "ΠÏοφίλ: %s" + +msgid "Program Updates" +msgstr "Αναβαθμίσεις του Ï€ÏογÏάμματος" + +msgid "Publisher" +msgstr "Εκδότης" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Έξοδος" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "ΔΕΞΙΑ" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Αγωνιστικά" + +msgid "Rail Shooter" +msgstr "Rail Shooter" + +#, c-format +msgid "Rated %s" +msgstr "Rated %s" + +msgid "Rating" +msgstr "Rating" + +msgid "Read" +msgstr "Ανάγνωση" + +msgid "Reading BCA..." +msgstr "Ανάγνωση BCA..." + +msgid "Reboot" +msgstr "Επανεκκίνηση" + +msgid "Region" +msgstr "Region" + +msgid "Release Date" +msgstr "ΗμεÏομηνία κυκλοφοÏίας" + +msgid "Release Notes: (short)" +msgstr "ΠληÏοφοÏίες έκδοσης: (πεÏιληπτηκά)" + +msgid "Removing game, please wait..." +msgstr "ΔιαγÏαφή παιχνιδιοÏ, παÏακαλώ πεÏιμένετε!" + +msgid "Restarting Wii..." +msgstr "Επανεκκίνηση της Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "ΕπιστÏοφή! (ret = %d)" + +msgid "Rhythm" +msgstr "Ρυθμός" + +msgid "Rows:" +msgstr "ΣειÏές" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Γίνετε μέτÏηση, παÏακαλώ πεÏιμένετε" + +msgid "S. Chinese" +msgstr "Α. Κινέζικα" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC κάÏτα" + +msgid "SUCCESS!" +msgstr "ΕΠΙΤΥΧΙΑ!" + +msgid "Save .gct" +msgstr "Αποθήκευση .gct" + +msgid "Save Debug" +msgstr "Αποθήκευση Debug" + +msgid "Save Settings" +msgstr "Αποθήκευση Ïυθμίσεων" + +msgid "Save debug.log" +msgstr "Αποθήκευση debug.log" + +msgid "Savegame not found.\n" +msgstr "Δεν βÏέθηκε αποθήκευση παιχνιδιοÏ.\n" + +msgid "Savegame:" +msgstr "Αποθήκευση παιχνιδιοÏ:" + +msgid "Saving Settings... " +msgstr "Αποθήκευση Ïυθμίσεις..." + +msgid "Saving cheats..." +msgstr "Αποθήκευση cheats..." + +msgid "Saving gamelist.txt ... " +msgstr "Αποθήκευση gamelist.txt..." + +msgid "Saving settings..." +msgstr "Αποθήκευση Ïυθμίσεις..." + +msgid "Saving:" +msgstr "Αποθήκευση:" + +#, c-format +msgid "Saving: %s" +msgstr "Αποθήκευση: %s" + +msgid "Screenshot:" +msgstr "Στιγμιότυπο:" + +msgid "Scroll:" +msgstr "Scroll:" + +msgid "Search" +msgstr "Αναζήτηση" + +msgid "Search for:" +msgstr "Αναζήτηση για" + +msgid "Select Alternative .dol:" +msgstr "Επιλέξτε Alternative .dol:" + +msgid "Select WBFS device:" +msgstr "Επιλέξτε μονάδα WBFS:" + +msgid "Select a different partition" +msgstr "Επιλέξτε άλλο Partition" + +msgid "Select a partition" +msgstr "Επιλέξτε partition" + +msgid "Select all" +msgstr "Επιλέξτε όλα" + +msgid "Select device:" +msgstr "Επιλέξτε μονάδα" + +msgid "Select the game you want to boot" +msgstr "Διαλέξτε το παιχνίδι που θέλετε να ξεκινήσετε" + +msgid "Selected Game" +msgstr "Παιχνίδι επιλογής" + +msgid "Settings" +msgstr "Ρυθμίσεις" + +msgid "Shooter" +msgstr "Shooter" + +msgid "Show All" +msgstr "Επιδειξη όλων" + +msgid "Show cIOS info" +msgstr "Επιδειξη πληÏοφοÏιών cIOS" + +msgid "Shutdown" +msgstr "ΤεÏματισμός λειτουÏγίας" + +msgid "Side:" +msgstr "Side:" + +msgid "Sim Racing" +msgstr "ΠÏοσομοίωση αγωνιστικών" + +msgid "Simulation" +msgstr "ΠÏοσομοίωση" + +msgid "Size(GB) Type Mount Used" +msgstr "Size(GB) Type Mount Used" + +#, c-format +msgid "Size: %d bytes" +msgstr "Μέγεθος: %d bytes" + +msgid "Skateboard" +msgstr "ΣκέιτμποÏντ" + +msgid "Skateboarding" +msgstr "ΣκέιτμποÏντινγκ" + +msgid "Skiing" +msgstr "Skiing" + +msgid "Snowboarding" +msgstr "ΣνοουμποÏντινγκ" + +msgid "Soccer" +msgstr "ΠοδόσφαιÏο" + +msgid "Sort" +msgstr "Ταξινόμηση" + +msgid "Sort Games" +msgstr "Ταξινόμιση παιχνιδιών" + +msgid "Sort Order:" +msgstr "ΣειÏά ταξινόμησης" + +msgid "Sort Type:" +msgstr "Είδος ταξινόμησης" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Ταξινόμιση: %s-%s" + +msgid "Spanish" +msgstr "Ισπανικά" + +msgid "Sports" +msgstr "ΣποÏ" + +msgid "Start" +msgstr "ΈναÏξη" + +msgid "Start Game" +msgstr "ΈναÏξη παιχνιδιοÏ" + +msgid "Start this game?" +msgstr "ΈναÏξη παιχνιδιοÏ;" + +msgid "Stealth Action" +msgstr "Stealth Action" + +msgid "Stopping DVD..." +msgstr "Stopping DVD..." + +msgid "Strategy" +msgstr "ΣτÏατηγική" + +msgid "Style" +msgstr "Στυλ" + +msgid "Style:" +msgstr "Στυλ" + +msgid "Surfing" +msgstr "ΣεÏφάÏισμα" + +msgid "Survival Horror" +msgstr "Survival Horror" + +msgid "Sync FAT free space info?" +msgstr "Sync FAT free space info?" + +msgid "Synchronizing, please wait." +msgstr "ΣυγχÏονισμός, παÏακαλώ πεÏιμένετε" + +msgid "Synopsis" +msgstr "Synopsis" + +msgid "Synopsis Length" +msgstr "Synopsis Length" + +msgid "System" +msgstr "ΣÏστημα" + +msgid "System Def." +msgstr "ΟÏισμός συστήματος" + +msgid "T. Chinese" +msgstr "Π. Κινέζικα" + +msgid "Table Tennis" +msgstr "Πινγκ πονγκ" + +msgid "Tennis" +msgstr "Τένις" + +msgid "Theme" +msgstr "Θέμα" + +msgid "Theme Info" +msgstr "ΠληÏοφοÏίες θέματος" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Το θέμα ίσως έχει μεγάλο μέγεθος.\n" +"συνιστάται η επανεκκινηση του Cfg.\n" + +msgid "Theme:" +msgstr "Θέμα:" + +#, c-format +msgid "Theme: %s" +msgstr "Θέμα: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Τα θέματα παÏέχονται από wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Θέματα για κατέβασμα" + +msgid "Themes with updates" +msgstr "Θέματα με αναβαθμίσεις" + +msgid "Title" +msgstr "Τίτλος" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Πολλά cheats! (%d)" + +msgid "Too many code lines!" +msgstr "Πολλά code lines!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Πολλά fragments! %d" + +msgid "Total Body Tracking" +msgstr "Total Body Tracking" + +msgid "Train Simulation" +msgstr "ΠÏοσομοίωση Ï„Ïένων" + +msgid "Trivia" +msgstr "Trivia" + +msgid "Truck Racing" +msgstr "Αγώνες Ï„Ïουκ" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Δοκιμή (url#%d)..." + +msgid "Turntable" +msgstr "Turntable" + +msgid "UNUSED" +msgstr "ΑΧΡΗΣΙΜΟΠΟΙΗΤΑ" + +msgid "UP" +msgstr "ΠΑÎΩ" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' δεν ξεκινάει με 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' δεν έχει PATH part" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko αναγνωÏίστηκε. Debugging ενεÏγωποιήθικε." + +msgid "USB Mass Storage Device" +msgstr "USB μονάδα" + +msgid "Unknown" +msgstr "'Αγνωστο" + +msgid "Unknown syntax!" +msgstr "Άγνωστο syntax!" + +msgid "Unplayed" +msgstr "Άπαιχτο" + +msgid "Unplayed Games" +msgstr "Άπαιχτα παιχνίδια" + +msgid "Update themes" +msgstr "Αναβάθμιση θεμάτων" + +msgid "Updates" +msgstr "Αναβάθμισεις" + +msgid "Updating database." +msgstr "Αναβάθμιση Ï„Ïάπεζας πληÏοφωÏιών..." + +msgid "Updating devolution" +msgstr "Αναβάθμιση devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "ΧÏησιμοποιείται: %.1fGB ΕλεÏθεÏα: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "ΧÏησιμοποίηση αποθηκευμένο Alternative .dol:" + +msgid "VC-Arcade" +msgstr "VC-Arcade" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-N64" + +msgid "VC-NES" +msgstr "VC-NES" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC-SNES" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "Εκδοχή" + +msgid "Video Patch:" +msgstr "Video Patch:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Θέα" + +msgid "Virtual Pet" +msgstr "Virtual Pet" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "Volleyball" +msgstr "Βόλειμπολ" + +msgid "WARNING:" +msgstr "ΠΡΟΣΟΧΗ:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB ελεÏθεÏα από %.1fGB" + +msgid "Watercraft Racing" +msgstr "Watercraft Racing" + +msgid "Wheel" +msgstr "Τιμόνι" + +msgid "Wide Screen:" +msgstr "ΕυÏεία οθόνη" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Wii κανάλι" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Wrestling" + +msgid "Write Playlog:" +msgstr "ΓÏάφω Playlog:" + +#, c-format +msgid "Writing to %s" +msgstr "ΓÏάψιμο στο %s" + +#, c-format +msgid "Writing: %s" +msgstr "ΓÏάψιμο: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Λάθος μέγεθος: %d (%d)" + +msgid "Yes" +msgstr "Îαι" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"ΠÏοσπαθήστε την επανασÏνδεση\n" +"της συσκευής,\n" +"ή πεÏιμένετε λίγο ακόμα" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ ΆΛΑΞΕ ]" + +msgid "[ FOUND ]" +msgstr "[ ΒΡΕΘΗΚΑΠ]" + +msgid "[ SAVED ]" +msgstr "[ ΑΠΟΘΗΚΕΥΤΗΚΕ ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[No HQ]" + +msgid "[USED]" +msgstr "[ΧΡΗΣΙΜΟΠΟΙΕΙΤΑΙ]" + +msgid "[default]" +msgstr "[default]" + +msgid "[saved]" +msgstr "[αποθηκεÏτηκε]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "άκυÏο wbfs αÏχείο: %s" + +msgid "calculating space, please wait..." +msgstr "υπολογισμός ελεÏθεÏου χώÏου, παÏακαλώ πεÏιμένετε..." + +msgid "delete" +msgstr "διαγÏαφή" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "απόÏÏιψη" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "domain %s could not be resolved" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "λάθος στην ανάγνωση: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "για %d παίκτες" + +msgid "for 1 player" +msgstr "για 1 παίκτη" + +msgid "format" +msgstr "μοÏφοποίηση" + +msgid "free" +msgstr "ελεÏθεÏα" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "ταχÏτητα γÏαμμικής ανάγνωσης: %.2f mb/s" + +msgid "linear..." +msgstr "γÏαμμικά..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin μέγεθος: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "μουσκό αÏχείο Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "musik.mp3 ή musik.mod δεν βÏέθηκαν!" + +msgid "no file" +msgstr "δεν υπάÏχει αÏχείο" + +msgid "none" +msgstr "τίποτα" + +msgid "or format a WBFS partition." +msgstr "ή μοÏφοποιήστε ένα WBFS partition." + +msgid "page" +msgstr "σελίδα" + +msgid "parse error" +msgstr "λάθος ανάλυσης" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "ταχÏτητα τυχαίας ανάγνωσης: %.2f mb/s" + +msgid "random..." +msgstr "τυχαία..." + +msgid "reset" +msgstr "επαναφοÏά" + +msgid "revert" +msgstr "επιστÏοφή " + +msgid "save" +msgstr "αποθήκευση" + +#, c-format +msgid "split error: %s" +msgstr "λάθος στο split: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "το άνοιγμα του Wii Disk δεν είναι δυνατόν" + +#, c-format +msgid "used: %p - %p" +msgstr "χÏησιμοποιείται: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "ΈναÏξη παιχνιδιοÏ, παÏακαλώ πεÏιμένετε..." + +#~ msgid "Loading previous game list..." +#~ msgstr "ΦόÏτωμα Ï€ÏοηγοÏμενης λίστας παιχνιδιών..." + +#~ msgid "Remove Game" +#~ msgstr "ΔιαγÏαφή παιχνιδιοÏ" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "Αναβ. Ï„Ïάπεζας πληÏοφωÏιών παιχνιδιών" + +#~ msgid "Update titles.txt" +#~ msgstr "Αναβάθμιση titles.txt" diff --git a/Languages/IT.lang b/Languages/IT.lang new file mode 100644 index 0000000..83fbfee --- /dev/null +++ b/Languages/IT.lang @@ -0,0 +1,2363 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-25 23:59-0100\n" +"Last-Translator: Cambo \n" +"Language-Team: ITALIAN \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB liberi su %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% di %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB copiati in %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d disponibili" + +#, c-format +msgid "%d more notes" +msgstr "%d ulteriori note" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Suddividi: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, attendere prego..." + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d secondi timeout)" + +msgid "(This can take a couple of minutes)" +msgstr "(Questo può richiedere un paio di minuti)" + +msgid ".dol too small" +msgstr ".dol troppo piccolo" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "Sparat. 1a pers." + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "Copertina 3D" + +msgid "3rd-person Shooter" +msgstr "Sparat. 3a pers." + +msgid "< ASC >" +msgstr "< CRES >" + +msgid "< DESC >" +msgstr "< DECR >" + +msgid "< DOWNLOAD >" +msgstr "< SCARICA >" + +msgid "About" +msgstr "Informazioni" + +msgid "Action" +msgstr "Azione" + +msgid "Additional config:" +msgstr "Config addizionali:" + +msgid "Admin Lock:" +msgstr "Blocca CFG:" + +msgid "Admin Unlock" +msgstr "Sblocca CFG" + +msgid "Adult" +msgstr "Adulti" + +msgid "Adventure" +msgstr "Avventura" + +msgid "All" +msgstr "Tutti" + +msgid "All Channels" +msgstr "Tutti i Canali" + +msgid "All Games" +msgstr "Tutti i Giochi" + +msgid "All themes up to date." +msgstr "Tutti i temi sono aggiornati." + +msgid "Alt Button Cfg:" +msgstr "Pulsante Cfg Alt:" + +msgid "Alt dol:" +msgstr "Alt dol" + +msgid "Alternative .dol:" +msgstr ".dol Alternativo:" + +msgid "Anti 002 Fix:" +msgstr "Correz. Anti 002:" + +#, c-format +msgid "App. Path: %s" +msgstr "Percorso App: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Sei sicuro di voler %s\n" +"questa partizione?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Sei sicuro di voler correggere\n" +"questa partizione?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Sei sicuro di voler eliminare\n" +"questo gioco?" + +msgid "Ascending" +msgstr "Crescente" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto-avvio gioco: %.6s non trovato!" + +msgid "Available Updates" +msgstr "Aggiornamenti Disponibili" + +msgid "Back" +msgstr "Indietro" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Baseball" + +msgid "Basic" +msgstr "Versione" + +msgid "Basketball" +msgstr "Pallacanestro" + +msgid "Bike Racing" +msgstr "Corse Bici" + +msgid "Billiards" +msgstr "Biliardo" + +msgid "Block IOS Reload:" +msgstr "No Ricaric. IOS:" + +msgid "Board Game" +msgstr "Gioco da Tavolo" + +msgid "Boot Disc" +msgstr "Carica Disco" + +msgid "Boot disc" +msgstr "Avvia DVD" + +msgid "Booting game, please wait..." +msgstr "Avvio il gioco, attendere prego..." + +msgid "Bowling" +msgstr "Bowling" + +msgid "Boxing" +msgstr "Boxe" + +msgid "Business Sim" +msgstr "Sim. Affari" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG base: %s" + +msgid "Camera" +msgstr "Telecamera" + +msgid "Can not dump GC savegames.\n" +msgstr "Impossibile copiare salvataggi GC.\n" + +msgid "Can't install Wii games!" +msgstr "Impossibile installare giochi Wii!" + +msgid "Cancelled." +msgstr "Annullato." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Impossibile creare cartella: %s" + +msgid "Cards" +msgstr "Carte" + +msgid "Channel" +msgstr "Canale" + +msgid "Cheat Codes:" +msgstr "Codici Trucchi:" + +msgid "Cheats: " +msgstr "Trucchi: " + +msgid "Check For Updates" +msgstr "Verifica Aggiornamenti" + +msgid "Checking for themes..." +msgstr "Sto controllando i temi…" + +msgid "Checking for updates..." +msgstr "Sto verificando gli aggiornamenti..." + +msgid "Chess" +msgstr "Scacchi" + +msgid "Choose a sorting method" +msgstr "Scegli un criterio di ordinamento" + +msgid "Classic" +msgstr "Classico" + +msgid "Classic Controller" +msgstr "Controller Classico" + +msgid "Clear" +msgstr "Annulla" + +msgid "Clear Patches:" +msgstr "Annulla Correzioni:" + +msgid "Coaching" +msgstr "Allenamento" + +msgid "Compare Type" +msgstr "Confronta Tipo" + +msgid "Compilation" +msgstr "Collezione" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Completato. Dimensione: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Errore configurazione, raggiunto il MAX_DNS_ENTRIES ma l'elenco è vuoto" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Errore di connessione da net_read() Codice Errore: %i" + +msgid "Console" +msgstr "Modo Testo" + +msgid "Construction Sim" +msgstr "Sim. Costruzioni" + +msgid "Contains" +msgstr "Contiene" + +msgid "Controller" +msgstr "Controller" + +msgid "Cooking" +msgstr "Cucina" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Impossibile inizializzare il modulo DIP! (ret = %d)" + +msgid "Country Fix:" +msgstr "Correz. Regione:" + +msgid "Cover" +msgstr "Copertina" + +msgid "Cover Image:" +msgstr "Copertina:" + +msgid "Cover Style" +msgstr "Stile Copertina" + +msgid "Covers Available" +msgstr "Copertine Disponibili" + +msgid "Cover~~Back" +msgstr "Retro" + +msgid "Cover~~Front" +msgstr "Fronte" + +msgid "Create" +msgstr "Crea" + +msgid "Creator" +msgstr "Autore" + +msgid "Cricket" +msgstr "Cricket" + +#, c-format +msgid "Current Version: %s" +msgstr "Versione Attuale: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Il Custom IOS %d non è stato trovato!\n" +"Occorre installarlo." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Impossibile caricare il Custom IOS %d! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Il Custom IOS %d è fittizio!\n" +"Occorre reinstallarlo." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s OK Caricamento" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "Imm. DISCO" + +msgid "DOWN" +msgstr "GIU'" + +msgid "Dance" +msgstr "Ballo" + +msgid "Dance Pad" +msgstr "Dance Pad" + +msgid "Darts" +msgstr "Freccette" + +msgid "Database update successful." +msgstr "Database aggiornato con successo." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Elimina Gioco" + +msgid "Deleting" +msgstr "Sto eliminando" + +msgid "Descending" +msgstr "Decrescente" + +msgid "Developer" +msgstr "Sviluppatore" + +msgid "Device is not responding!" +msgstr "La periferica non risponde!" + +msgid "Device:" +msgstr "Periferica:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolution accetta solo copie valide!\n" + +msgid "Devolution:" +msgstr "Devolution:" + +msgid "Disc" +msgstr "Disco" + +msgid "Disc (Ask)" +msgstr "Disco (Chiedi)" + +msgid "Done." +msgstr "Fatto." + +msgid "Download .txt" +msgstr "Scarica .txt" + +msgid "Download All Covers" +msgstr "Scarica Tutte le Copertine" + +msgid "Download All Missing Covers" +msgstr "Scarica Copertine Mancanti" + +msgid "Download Missing Covers" +msgstr "Scarica Copertine Mancanti" + +msgid "Download Plugins" +msgstr "Scarica Plugins" + +msgid "Download Themes" +msgstr "Scarica Temi" + +msgid "Download complete." +msgstr "Download completato." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Errore nel download della gamercard #%d." + +msgid "Download game information" +msgstr "Scarica informazioni sul gioco" + +msgid "Downloadable Content" +msgstr "Contenuto Scaricabile" + +msgid "Downloading ALL MISSING covers" +msgstr "Sto scaricando le copertine MANCANTI" + +msgid "Downloading ALL covers" +msgstr "Sto scaricando TUTTE le copertine" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Sto scaricando TUTTE le copertine per %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Sto scaricando le copertine MANCANTI per %.6s" + +msgid "Downloading Theme previews..." +msgstr "Sto scaricando le anteprime dei temi..." + +msgid "Downloading cheats..." +msgstr "Sto scaricando i trucchi..." + +msgid "Downloading database." +msgstr "Sto scaricando il database." + +msgid "Downloading devolution." +msgstr "Sto scaricando devolution." + +msgid "Downloading mighty plugin." +msgstr "Sto scaricando il mighty plugin." + +msgid "Downloading neek2o plugin." +msgstr "Sto scaricando il neek2o plugin." + +msgid "Downloading titles.txt ..." +msgstr "Sto scaricando titles.txt ..." + +msgid "Downloading translation file." +msgstr "Sto scaricando i file delle traduzioni." + +msgid "Downloading unifont." +msgstr "Sto scaricando unifont." + +#, c-format +msgid "Downloading: %s" +msgstr "Sto scaricando: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Sto scaricando: %s come %s" + +msgid "Downloads" +msgstr "Num. Download" + +msgid "Drawing" +msgstr "Disegno" + +msgid "Drums" +msgstr "Tamburi" + +msgid "Dump savegame" +msgstr "Copia salvataggi" + +msgid "Duplicate ID3" +msgstr "Duplica ID3" + +msgid "Dutch" +msgstr "Olandese" + +msgid "ERROR" +msgstr "ERRORE" + +msgid "ERROR creating file" +msgstr "ERRORE creando file" + +msgid "ERROR game opt" +msgstr "ERRORE opz gioco" + +msgid "ERROR reading BCA!" +msgstr "ERRORE leggendo BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "ERRORE rimuovendo %s" + +msgid "ERROR writing BCA!" +msgstr "ERRORE scrivendo BCA!" + +msgid "ERROR!" +msgstr "ERRORE!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ERRORE! (ret = %d)" + +msgid "ERROR:" +msgstr "ERRORE:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ERRORE: %s non accessibile" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ERRORE: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ERRORE: CIOS222/223 v4 necessario per BCA" + +msgid "ERROR: Cache Close" +msgstr "ERRORE: Cache Chiusa" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ERRORE: Impossibile caricare gioco! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ERRORE: Gioco già installato !!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ERRORE: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ERRORE: ID Gioco Non Valido" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ERRORE: Loading EHC module! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERRORE: scrittura su NTFS disabilitata!\n" +"(impostare ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ERRORE: Nessuna partizione trovata! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ERRORE: Non è un disco Wii!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ERRORE: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ERRORE: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ERRORE: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ERRORE: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ERRORE: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ERRORE: Setting SD mode" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ERRORE: USB init! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "ERRORE: WBFS non caricata!" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"ERRORE: è necessario il cIOS249rev18,\n" +"cIOS222v4, cIOS223v4, cIOS224v5 o superiori\n" +"per caricare i giochi da una partizione FAT!\n" +"Aggiornare il cIOS249 o scegliere un cIOS diverso." + +msgid "ERROR: cache: out of memory" +msgstr "ERRORE: cache: memoria terminata" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ERRORE: nella creazione di: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ERRORE: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "ERRORE: memory overlap!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERRORE: partizioni wbfs multiple\n" +"utilizzabili solo con IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ERRORE: spazio insufficiente!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ERRORE: unmount scorretto della partizione ntfs" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ERRORE: opening %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ERRORE: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ERRORE: impostando frammenti %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "ERRORE: il disco o la partizione %s/ non è collegata!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ERRORE: scrivendo %s (%d)." + +msgid "EXTEND" +msgstr "ESTESA" + +msgid "Educational" +msgstr "Istruzione" + +msgid "Ejecting DVD..." +msgstr "Espulsione DVD..." + +msgid "English" +msgstr "Inglese" + +msgid "English Title" +msgstr "Titolo Inglese" + +msgid "Enter Code: " +msgstr "Inserire Codice: " + +msgid "Error GRRLIB init" +msgstr "Errore GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Errore Inizializzazione Rete." + +msgid "Error adding disc!" +msgstr "Errore aggiungendo disco!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Errore allocando il buffer: %d" + +msgid "Error creating directory..." +msgstr "Errore creando la cartella…." + +msgid "Error discarding options!" +msgstr "Errore sostituendo opzioni!" + +msgid "Error downloading theme preview..." +msgstr "Errore scaricando l'anteprima del tema..." + +msgid "Error downloading theme..." +msgstr "Errore scaricando il tema..." + +msgid "Error downloading themes..." +msgstr "Errore scaricando i temi..." + +msgid "Error downloading update..." +msgstr "Errore scaricando aggiornamento..." + +msgid "Error downloading updates..." +msgstr "Errore scaricando aggiornamenti..." + +msgid "Error downloading." +msgstr "Errore durante download." + +msgid "Error establishing connection" +msgstr "Errore stabilendo connessione" + +msgid "Error extracting theme..." +msgstr "Errore scompattando il tema…" + +msgid "Error opening database, update did not complete." +msgstr "Errore apertura database, aggiornamento non completato." + +#, c-format +msgid "Error opening: %s" +msgstr "Errore aprendo: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Errore giocando %s" + +msgid "Error reading .dol" +msgstr "Errore leggendo .dol" + +msgid "Error reading dol header" +msgstr "Errore leggendo dol header" + +#, c-format +msgid "Error saving %s" +msgstr "Errore salvando %s" + +msgid "Error saving options!" +msgstr "Errore salvando opzioni!" + +msgid "Error saving settings!" +msgstr "Errore salvando impostazioni!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Errore salvando il file playlog.\n" +"Caricare dal Menu Wii per correggerlo." + +msgid "Error: Invalid PNG image!" +msgstr "Errore: Immagine PNG non valida!" + +msgid "Error: no URL." +msgstr "Errore: nessun URL." + +msgid "Error: no data." +msgstr "Errore: nessun dato." + +msgid "Exercise" +msgstr "Esercizio" + +msgid "Exit" +msgstr "Esci" + +#, c-format +msgid "Extracting: %s" +msgstr "Sto scompattando: %s" + +msgid "FAIL" +msgstr "FALLITO" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ERRORE ALLOC. %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "RIEMPI %d" + +msgid "FLAT cover" +msgstr "Copertina 2D" + +msgid "FULL cover" +msgstr "Copertina FULL" + +msgid "Fav" +msgstr "Pref" + +msgid "Fav: Off" +msgstr "Pref: No" + +msgid "Fav: On" +msgstr "Pref: Si" + +msgid "Favorite" +msgstr "Preferito" + +msgid "Favorite Games" +msgstr "Giochi Preferiti" + +msgid "Favorite:" +msgstr "Preferiti:" + +msgid "Favorites:" +msgstr "Preferiti:" + +msgid "Fighting" +msgstr "Combattimenti" + +#, c-format +msgid "File not found! %s" +msgstr "File non trovato! %s" + +msgid "Filter" +msgstr "Filtro" + +msgid "Filter Games" +msgstr "Seleziona Giochi" + +msgid "Filter by Controller" +msgstr "Seleziona per Controller" + +msgid "Filter by Game Type" +msgstr "Seleziona per Tipo di Gioco" + +msgid "Filter by Genre" +msgstr "Seleziona per Genere" + +msgid "Filter by Online Features" +msgstr "Seleziona per Funzionalità Online" + +msgid "Filter:" +msgstr "Filtro:" + +msgid "Fishing" +msgstr "Pesca" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "Sto correggendo partizione EXTEND..." + +msgid "Flight Sim" +msgstr "Sim. Volo" + +msgid "Football" +msgstr "Football" + +msgid "Force Devolution:" +msgstr "Forza Devolution:" + +msgid "Force NTSC" +msgstr "Forza NTSC" + +msgid "Force NTSC 480p" +msgstr "Forza NTSC 480p" + +msgid "Force PAL" +msgstr "Forza PAL" + +msgid "Force PAL 480p" +msgstr "Forza PAL 480p" + +msgid "Force PAL50" +msgstr "Forza PAL50" + +msgid "Force PAL60" +msgstr "Forza PAL60" + +msgid "Formatting" +msgstr "Sto formattando" + +#, c-format +msgid "Found %s" +msgstr "Trovata %s" + +msgid "French" +msgstr "Francese" + +msgid "Full" +msgstr "Completa" + +msgid "Futuristic Racing" +msgstr "Corse Futuro" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Predef. Gioco" + +msgid "Game ID" +msgstr "ID Gioco" + +msgid "Game Options" +msgstr "Opzioni Gioco" + +#, c-format +msgid "Game Options: %s" +msgstr "Opzioni Gioco: %s" + +msgid "Game Type" +msgstr "Tipo Gioco" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "La gamercard #%d riporta: %.*s" + +msgid "Genre" +msgstr "Genere" + +msgid "German" +msgstr "Tedesco" + +msgid "Global Options" +msgstr "Opzioni Generali" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Chitarra" + +msgid "HQ cover" +msgstr "Copertina HQ" + +msgid "HTTP Response was without a file" +msgstr "HTTP Response senza file" + +msgid "Health" +msgstr "Salute" + +msgid "Hidden Object" +msgstr "Oggetto Nascosto" + +msgid "Hide" +msgstr "Nascondi" + +msgid "Hide Game:" +msgstr "Nascondi Gioco:" + +msgid "Hockey" +msgstr "Hockey" + +msgid "Hold button B to cancel." +msgstr "Tener premuto B per annullare." + +msgid "Home Menu" +msgstr "Menu Home" + +msgid "Homebrew Channel" +msgstr "Canale Homebrew" + +msgid "Hook Type:" +msgstr "Aggancio Ocarina:" + +msgid "Hunting" +msgstr "Caccia" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID discordi: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Reload: Bloccato" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) fallito con codice %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Se Cfg non riparte bene,\n" +"copiare il file boot.dol.bak sostituendo\n" +"con esso il file boot.dol e riprovare." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Inconsistenza in LZ77 codificando %x" + +msgid "Infinity Base" +msgstr "Infinity Base" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info file: %s" + +msgid "Initializing Network..." +msgstr "Inizializzazione Rete..." + +msgid "Install" +msgstr "Installa" + +msgid "Install Date" +msgstr "Data Installazione" + +msgid "Install Game" +msgstr "Installa Gioco" + +msgid "Install game" +msgstr "Installa gioco" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "ERRORE Installazione! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Sto installando il gioco, attendere prego..." + +msgid "Invalid .dol" +msgstr ".dol Non Valido" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Partizione non valida: '%s'" + +msgid "Italian" +msgstr "Italiano" + +msgid "Japanese" +msgstr "Giapponese" + +msgid "Japanese Title" +msgstr "Titolo Giapponese" + +msgid "Jump" +msgstr "Salto" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Corse Kart" + +msgid "Keyboard" +msgstr "Tastiera" + +msgid "Korean" +msgstr "Coreano" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "SINISTRA" + +msgid "LOCKED!" +msgstr "BLOCCATO!" + +msgid "Language:" +msgstr "Lingua:" + +msgid "Last Play Date" +msgstr "Caricamento Recente" + +msgid "Launch Methods" +msgstr "Modalità Avvio" + +msgid "Life Simulation" +msgstr "Sim. Sociale" + +msgid "Load OK!" +msgstr "Caricamento OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Versione Loader: %s" + +msgid "Loading ..." +msgstr "Sto caricando ..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Sto caricando..%s\n" + +msgid "Main" +msgstr "Principale" + +msgid "Main Menu" +msgstr "Menu Principale" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Controlla di utilizzare la porta USB0!\n" +"(la più vicina all'angolo della Wii)" + +msgid "Manage" +msgstr "Gestisci" + +msgid "Manage Cheats" +msgstr "Gestisci Trucchi" + +msgid "Management Sim" +msgstr "Sim. Gestione" + +msgid "Martial Arts" +msgstr "Arti Marziali" + +msgid "Microphone" +msgstr "Microfono" + +msgid "Mighty Plugin" +msgstr "Mighty Plugin" + +msgid "Motion+" +msgstr "Motion Plus" + +msgid "Motorcycle Racing" +msgstr "Corse Moto" + +msgid "Mounting device, please wait..." +msgstr "" +"Sto caricando la periferica,\n" +"attendere prego..." + +msgid "Music" +msgstr "Musica" + +#, c-format +msgid "Music file from dir: %s" +msgstr "File musica da cartella: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Dimensione file musica: %d" + +#, c-format +msgid "Music file: %s" +msgstr "File musica: %s" + +msgid "Music: Disabled" +msgstr "Musica: Disabilitata" + +msgid "Music: Enabled" +msgstr "Musica: Abilitata" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Musica: Sto cercando %s file in: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Musica: Prossimo file da suonare: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Musica: Numero di %s file trovati: %i" + +msgid "Music: musicArray contents: " +msgstr "Musica: contenuto musicArray: " + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"Cartella Emu NAND:\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "Emu NAND:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTA: CIOS249 precedente rev10:\n" +"Caricamento giochi da SDHC non supportato!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTA: CIOS249 precedente rev14:\n" +"Eventuali errori #001 non sono gestiti!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTA: CIOS249 precedente rev9:\n" +"Caricamento giochi da USB non supportato!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTA: Stai usando il CIOS249 rev13:\n" +"Per uscire dal gioco devi resettare o\n" +"spengere la Wii prima di caricare\n" +"un altro gioco, o la Wii si blocca!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTA: Stai usando il CIOS249 rev14:\n" +"E' noto che ha problemi con i dischi\n" +"dual layer. Si raccomanda di utilizzare\n" +"un IOS diverso o una versione diversa\n" +"per installare/caricare questo gioco." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTA: è necessario riavviare il loader\n" +"per rendere effettivo l'aggiornamento." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTA: la partizione P#%d è di tipo EXTEND\n" +"ma contiene un filesystem WBFS. Questa\n" +"impostazione è errata. Premi %s per cambiare\n" +"il tipo di partizione da EXTEND a DATA." + +msgid "NTFS compression not supported!" +msgstr "compressione NTFS non supportata!" + +msgid "NTFS encryption not supported!" +msgstr "criptazione NTFS non supportata!" + +msgid "NTSC-J patch:" +msgstr "Patch NTSC-J:" + +msgid "Neek2o Plugin" +msgstr "Plugin Neek2o" + +msgid "Network connection established." +msgstr "Connessione di rete stabilita." + +msgid "Network error. Can't update gamercards." +msgstr "Errore di rete. Impossibile aggiornare le gamercards." + +msgid "New Themes" +msgstr "Nuovi Temi" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Connessione Nintendo DS" + +msgid "No" +msgstr "No" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Nessun dominio nel URL '%s'" + +msgid "No games found!" +msgstr "Nessun gioco trovato!" + +msgid "No partition selected!" +msgstr "Nessuna partizione selezionata!" + +msgid "No themes found." +msgstr "Nessun tema trovato." + +msgid "No updates found." +msgstr "Nessun aggiornamento trovato." + +msgid "NoDisc:" +msgstr "NoDisco:" + +msgid "None found on disc" +msgstr "Disco vuoto" + +msgid "Not Found boot.dol!" +msgstr "boot.dol Non Trovato!" + +msgid "Not Found!" +msgstr "Non Trovato!" + +msgid "Number of Online Players" +msgstr "Numero di Giocatori Online" + +msgid "Number of Players" +msgstr "Numero di Giocatori" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (trucchi):" + +msgid "Ocarina Cheat Manager" +msgstr "Gestore Trucchi Ocarina" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Errore Codice" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Codici trovati." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Nessun codice trovato" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Memoria Insufficiente" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Sto cercando i codici..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Troppi codici" + +msgid "Off" +msgstr "Disattivato" + +msgid "Off-Road Racing" +msgstr "Corse Fuoristrada" + +msgid "On" +msgstr "Attivato" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Contenuto Online" + +msgid "Online Play" +msgstr "Gioco Online" + +msgid "Online Players" +msgstr "Giocatori Online" + +msgid "Online Score List" +msgstr "Punteggi Online" + +msgid "Online Updates" +msgstr "Aggiornamenti Online" + +msgid "Open Sort" +msgstr "Menu Ordinamento" + +msgid "Open Style" +msgstr "Menu Stile" + +msgid "Opening DVD disc..." +msgstr "Sto aprendo il DVD..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Sto aprendo la partizione: %s" + +msgid "Options" +msgstr "Opzioni" + +msgid "Options discarded for this game." +msgstr "Opzioni del gioco sostituite." + +msgid "Options saved for this game." +msgstr "Opzioni del gioco salvate." + +msgid "Out of memory" +msgstr "Memoria terminata" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK:" + +msgid "Page:" +msgstr "Pagina:" + +msgid "Partial" +msgstr "" + +msgid "Partition:" +msgstr "Partizione:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partizione: %s non trovata!" + +msgid "Party" +msgstr "Party" + +msgid "Paused Start" +msgstr "Avvio Sospeso" + +msgid "Petanque" +msgstr "Bocce" + +msgid "Pinball" +msgstr "Flipper" + +msgid "Platformer" +msgstr "Platformer" + +msgid "Play Count" +msgstr "Numero Giocate" + +msgid "Players" +msgstr "Giocatori" + +msgid "Please insert a game disc..." +msgstr "Inserire un disco..." + +msgid "Plugin:" +msgstr "Plugin:" + +msgid "Poker" +msgstr "Poker" + +msgid "Portal of Power" +msgstr "Portale Potere" + +#, c-format +msgid "Press %s button for options." +msgstr "Premere %s per le opzioni" + +#, c-format +msgid "Press %s button to %s." +msgstr "Premere %s per %s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Premere %s per utilizzare i codici" + +#, c-format +msgid "Press %s button to cancel." +msgstr "Premere %s per annullare" + +#, c-format +msgid "Press %s button to change device." +msgstr "Premere %s per cambiare periferica" + +#, c-format +msgid "Press %s button to continue." +msgstr "Premere %s per continuare" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Premere %s per cancellare FS" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Premere %s per scaricare BCA" + +#, c-format +msgid "Press %s button to exit." +msgstr "Premere %s per uscire" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Premere %s per correggere EXTEND/WBFS" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Premere %s per formattare WBFS" + +#, c-format +msgid "Press %s button to go back." +msgstr "Premere %s per tornare indietro" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Premere %s per scegliere una partizione" + +#, c-format +msgid "Press %s button to select." +msgstr "Premere %s per scegliere" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Premere %s per saltare i codici" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Premere %s per sincronizzare il disco FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Premere %s per l'anteprima a tutto schermo" + +#, c-format +msgid "Press %s for game options" +msgstr "Premere %s per le opzioni del gioco" + +#, c-format +msgid "Press %s for global options" +msgstr "Premere %s per le opzioni generali" + +#, c-format +msgid "Press %s to discard options" +msgstr "Premere %s per sostituire opzioni" + +#, c-format +msgid "Press %s to download and update" +msgstr "Premere %s per scaricare ed aggiornare" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Premere %s per scaricare questo tema" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Premere %s per scaricare, %s per tornare indietro" + +#, c-format +msgid "Press %s to return" +msgstr "Premere %s per tornare indietro" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Premere %s per salvare opz. generali" + +#, c-format +msgid "Press %s to save options" +msgstr "Premere %s per salvare le opzioni" + +#, c-format +msgid "Press %s to save selection" +msgstr "Premere %s per salvare la scelta" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Premere %s per scegliere il criterio" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Premere %s per scegliere l'ordinamento" + +#, c-format +msgid "Press %s to start game" +msgstr "Premere %s per caricare il gioco" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Premere %s per aggiornare senza meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Premere DESTRA/SINISTRA per periferica" + +msgid "Press 1 to skip WBFS mounting" +msgstr "Premere 1 per non caricare WBFS" + +msgid "Press 2 to reload IOS" +msgstr "Premere 2 per ricaricare l'IOS" + +msgid "Press A to continue without config.txt" +msgstr "Premere A per continuare senza config.txt" + +msgid "Press A to select device" +msgstr "Premere A per scegliere la periferica" + +msgid "Press B to exit to HBC" +msgstr "Premere B per uscire verso l'HBC" + +msgid "Press HOME to reset" +msgstr "Premere HOME per riavviare la Wii" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Premere SINISTRA/DESTRA per scelta IOS" + +msgid "Press any button to continue..." +msgstr "Premere un pulsante per continuare..." + +msgid "Press any button to exit..." +msgstr "Premere un pulsante per uscire..." + +msgid "Press any button to restart..." +msgstr "Premere un pulsante per riavviare..." + +msgid "Press any button.\n" +msgstr "Premere un pulsante.\n" + +msgid "Press any button..." +msgstr "Premere un pulsante..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Premere %s per scaricare le copertine." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Premere %s per espellere il DVD" + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Profilo:" + +#, c-format +msgid "Profile: %s" +msgstr "Profilo: %s" + +msgid "Program Updates" +msgstr "Aggiorna CFG Loader" + +msgid "Publisher" +msgstr "Casa Editrice" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Uscita" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "DESTRA" + +msgid "RPG" +msgstr "Giochi Ruolo" + +msgid "Racing" +msgstr "Corse" + +msgid "Rail Shooter" +msgstr "Sparat. Rotaia" + +#, c-format +msgid "Rated %s" +msgstr "Età %s" + +msgid "Rating" +msgstr "Valutazione" + +msgid "Read" +msgstr "Leggi" + +msgid "Reading BCA..." +msgstr "Sto leggendo BCA..." + +msgid "Reboot" +msgstr "Riavvia" + +msgid "Region" +msgstr "Regione" + +msgid "Release Date" +msgstr "Data di Rilascio" + +msgid "Release Notes: (short)" +msgstr "Note di Rilascio: (brevi)" + +msgid "Removing game, please wait..." +msgstr "Sto eliminando il gioco, attendere prego..." + +msgid "Restarting Wii..." +msgstr "Sto riavviando la Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Errore! (ret = %d)" + +msgid "Rhythm" +msgstr "Ritmo" + +msgid "Rows:" +msgstr "Righe:" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Eseguo benchmark, attendere prego" + +msgid "S. Chinese" +msgstr "Cinese Semplif." + +msgid "SD/SDHC Card" +msgstr "Scheda SD/SDHC" + +msgid "SUCCESS!" +msgstr "SUCCESSO!" + +msgid "Save .gct" +msgstr "Salva .gct" + +msgid "Save Debug" +msgstr "Salva Debug File" + +msgid "Save Settings" +msgstr "Salva Impost." + +msgid "Save debug.log" +msgstr "Salva debug.log" + +msgid "Savegame not found.\n" +msgstr "Savegame non trovato.\n" + +msgid "Savegame:" +msgstr "Savegame:" + +msgid "Saving Settings... " +msgstr "Salvataggio Impostazioni..." + +msgid "Saving cheats..." +msgstr "Sto salvando i trucchi..." + +msgid "Saving gamelist.txt ... " +msgstr "Sto salvando gamelist.txt ..." + +msgid "Saving settings..." +msgstr "Sto salvando le impostazioni..." + +msgid "Saving:" +msgstr "Sto salvando:" + +#, c-format +msgid "Saving: %s" +msgstr "Salvataggio: %s" + +msgid "Screenshot:" +msgstr "Screenshot:" + +msgid "Scroll:" +msgstr "Scorrimento:" + +msgid "Search" +msgstr "Cerca" + +msgid "Search for:" +msgstr "Trova:" + +msgid "Select Alternative .dol:" +msgstr "Scegli .dol Alternativo:" + +msgid "Select WBFS device:" +msgstr "Scegli periferica WBFS:" + +msgid "Select a different partition" +msgstr "Scegli una partizione diversa" + +msgid "Select a partition" +msgstr "Scegli una partizione" + +msgid "Select all" +msgstr "Seleziona tutto" + +msgid "Select device:" +msgstr "Seleziona device:" + +msgid "Select the game you want to boot" +msgstr "Scegli il gioco che vuoi caricare" + +msgid "Selected Game" +msgstr "Gioco Selezionato" + +msgid "Settings" +msgstr "Impostazioni" + +msgid "Shooter" +msgstr "Sparatutto" + +msgid "Show All" +msgstr "Mostra Tutto" + +msgid "Show cIOS info" +msgstr "Mostra informazioni sui cIOS" + +msgid "Shutdown" +msgstr "Spegni" + +msgid "Side:" +msgstr "Lato Copert.:" + +msgid "Sim Racing" +msgstr "Sim. Corse" + +msgid "Simulation" +msgstr "Simulazione" + +msgid "Size(GB) Type Mount Used" +msgstr "Dim.(GB) Tipo Mount Usata" + +#, c-format +msgid "Size: %d bytes" +msgstr "Dimensione: %d bytes" + +msgid "Skateboard" +msgstr "Skateboard" + +msgid "Skateboarding" +msgstr "Skateboard" + +msgid "Skiing" +msgstr "Sci" + +msgid "Snowboarding" +msgstr "Snowboard" + +msgid "Soccer" +msgstr "Calcio" + +msgid "Sort" +msgstr "Ordina" + +msgid "Sort Games" +msgstr "Ordina Giochi" + +msgid "Sort Order:" +msgstr "Ordinamento:" + +msgid "Sort Type:" +msgstr "Ordina per:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Ordinamento: %s-%s" + +msgid "Spanish" +msgstr "Spagnolo" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Avvia" + +msgid "Start Game" +msgstr "Avvia Gioco" + +msgid "Start this game?" +msgstr "Vuoi avviare questo gioco?" + +msgid "Stealth Action" +msgstr "Spionaggio" + +msgid "Stopping DVD..." +msgstr "Sto fermando il DVD..." + +msgid "Strategy" +msgstr "Strategia" + +msgid "Style" +msgstr "Stile" + +msgid "Style:" +msgstr "Stile:" + +msgid "Surfing" +msgstr "Surf" + +msgid "Survival Horror" +msgstr "Horror" + +msgid "Sync FAT free space info?" +msgstr "Sincronizzare lo spazio libero nel disco FAT?" + +msgid "Synchronizing, please wait." +msgstr "Sto sincronizzando, attendere prego." + +msgid "Synopsis" +msgstr "Sommario" + +msgid "Synopsis Length" +msgstr "Lunghezza Sommario" + +msgid "System" +msgstr "Sistema" + +msgid "System Def." +msgstr "Predef. Sistema" + +msgid "T. Chinese" +msgstr "Cinese Tradiz." + +msgid "Table Tennis" +msgstr "Ping Pong" + +msgid "Tennis" +msgstr "Tennis" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Informazioni sul Tema" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Il tema potrebbe essere troppo grande.\n" +"Meglio ricaricare il CFG Loader.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temi forniti da wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Temi da scaricare" + +msgid "Themes with updates" +msgstr "Temi con aggiornamenti" + +msgid "Title" +msgstr "Titolo" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Troppi trucchi! (%d)" + +msgid "Too many code lines!" +msgstr "Troppe linee di codice!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Troppi frammenti! %d" + +msgid "Total Body Tracking" +msgstr "Palestra" + +msgid "Train Simulation" +msgstr "Sim. Treno" + +msgid "Trivia" +msgstr "Trivial" + +msgid "Truck Racing" +msgstr "Corse Camion" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Sto provando (url#%d) ..." + +msgid "Turntable" +msgstr "Tavola Girevole" + +msgid "UNUSED" +msgstr "NON USATO" + +msgid "UP" +msgstr "SU" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' non inizia con 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' non ha alcun PATH" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "Trovato USB Gecko. Debugging attivato." + +msgid "USB Mass Storage Device" +msgstr "Memoria di Massa USB" + +msgid "Unknown" +msgstr "Sconosciuto" + +msgid "Unknown syntax!" +msgstr "Sintassi sconosciuta!" + +msgid "Unplayed" +msgstr "Non Giocati" + +msgid "Unplayed Games" +msgstr "Giochi mai caricati" + +msgid "Update themes" +msgstr "Aggiorna temi" + +msgid "Updates" +msgstr "Aggiornamenti" + +msgid "Updating database." +msgstr "Sto aggiornando il database." + +msgid "Updating devolution" +msgstr "Sto aggiornando devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Usati: %.1fGB Liberi: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Uso .dol Alternativo Salvato:" + +msgid "VC-Arcade" +msgstr "VC-Arcade" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-N64" + +msgid "VC-NES" +msgstr "VC-NES" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC-SNES" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "Versione" + +msgid "Video Patch:" +msgstr "Correzione Video:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Vista" + +msgid "Virtual Pet" +msgstr "Cuccioli Virtuali" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "Volleyball" +msgstr "Pallavolo" + +msgid "WARNING:" +msgstr "ATTENZIONE:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB liberi di %.1fGB" + +msgid "Watercraft Racing" +msgstr "Corse Motoscafo" + +msgid "Wheel" +msgstr "Ruota" + +msgid "Wide Screen:" +msgstr "Schermo Wide" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "canale Wii" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Wrestling" + +msgid "Write Playlog:" +msgstr "Scrivi Log Giocate" + +#, c-format +msgid "Writing to %s" +msgstr "Sto scrivendo a %s" + +#, c-format +msgid "Writing: %s" +msgstr "Scrittura: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Dimensione Errata: %d (%d)" + +msgid "Yes" +msgstr "Si" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Puoi provare anche a sconnettere\n" +"e riconnettere la periferica,\n" +"oppure puoi aspettare ancora" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ MODIFICATA ]" + +msgid "[ FOUND ]" +msgstr "[ TROVATA ]" + +msgid "[ SAVED ]" +msgstr "[ SALVATA ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[No HQ]" + +msgid "[USED]" +msgstr "[USATO]" + +msgid "[default]" +msgstr "[predef.]" + +msgid "[saved]" +msgstr "[salvata]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "file wbfs corrotto: %s" + +msgid "calculating space, please wait..." +msgstr "sto calcolando lo spazio, attendere prego..." + +msgid "delete" +msgstr "eliminare" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "scarta" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "dominio %s non risolto" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "errore lettura: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "per %d giocatori" + +msgid "for 1 player" +msgstr "per 1 giocatore" + +msgid "format" +msgstr "formattare" + +msgid "free" +msgstr "libero" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "velocità lettura lineare: %.2f mb/s" + +msgid "linear..." +msgstr "lineare..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "dimensione loader.bin: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "file musica troppo grosso (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 o music.mod non trovati!" + +msgid "no file" +msgstr "nessun file" + +msgid "none" +msgstr "nessuno" + +msgid "or format a WBFS partition." +msgstr "o formatta una partizione WBFS." + +msgid "page" +msgstr "pagina" + +msgid "parse error" +msgstr "errore parsing" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "velocità lettura casuale: %.2f mb/s" + +msgid "random..." +msgstr "casuale..." + +msgid "reset" +msgstr "ripristina" + +msgid "revert" +msgstr "inverti" + +msgid "save" +msgstr "salva" + +#, c-format +msgid "split error: %s" +msgstr "errore suddivisione: %s" + +msgid "uDraw GameTablet" +msgstr "Tavoletta uDraw" + +msgid "unable to open wii disc" +msgstr "impossibile aprire disco Wii" + +#, c-format +msgid "used: %p - %p" +msgstr "usati: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Sto caricando il gioco, attendere prego..." + +#~ msgid "Console Def." +#~ msgstr "Predef. Console" + +#~ msgid "Download titles.txt" +#~ msgstr "Scarica titles.txt" + +#~ msgid "Front" +#~ msgstr "Fronte" + +#~ msgid "Loading previous game list..." +#~ msgstr "Sto caricando l'elenco precedente..." + +#~ msgid "Remove Game" +#~ msgstr "Elimina Gioco" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "Aggiorna WiiTDB" + +#~ msgid "Update titles.txt" +#~ msgstr "Aggiorna titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "Scarica Database WiiTDB" diff --git a/Languages/JA.lang b/Languages/JA.lang new file mode 100644 index 0000000..9b1d8ca --- /dev/null +++ b/Languages/JA.lang @@ -0,0 +1,1931 @@ +・ CFG USB Loader language file template.e +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2010-02-11 HO:MI+ZONE\n" +"Last-Translator: Trickart \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB空ã„ã¦ã„ã¾ã™(%.1fGB中)" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%%[%.2fGB中](%c)残り%d時間%02d分%02d秒" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%d時間%02d分%02d秒ã§%.2fGBコピーã—ã¾ã—ãŸ" + +#, c-format +msgid "%d available" +msgstr "%dãŒåˆ©ç”¨å¯èƒ½ã§ã™" + +#, c-format +msgid "%d more notes" +msgstr "%dãã®ä»–ã®ãƒŽãƒ¼ãƒˆ" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s分割: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%sã€ãŠå¾…ã¡ãã ã•ã„…" + +#, c-format +msgid "(%d online)" +msgstr "(%d人ã¾ã§é€šä¿¡å¯èƒ½)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d秒ã§ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ)" + +msgid "(This can take a couple of minutes)" +msgstr "(ã“ã‚Œã«ã¯æ•°åˆ†ã‹ã‹ã‚Šã¾ã™)" + +msgid ".dol too small" +msgstr ".dolãŒå°ã•ã™ãŽã¾ã™" + +msgid "3D cover" +msgstr "3Dã‚«ãƒãƒ¼" + +msgid "< ASC >" +msgstr "< 昇順 >" + +msgid "< DESC >" +msgstr "< é™é † >" + +msgid "< DOWNLOAD >" +msgstr "<ダウンロード>" + +msgid "About" +msgstr "情報" + +msgid "Action" +msgstr "アクション" + +msgid "Additional config:" +msgstr "設定ã®ä¿å­˜å ´æ‰€:" + +msgid "Admin Lock:" +msgstr "管ç†è€…ロック:" + +msgid "Admin Unlock" +msgstr "管ç†è€…アンロック" + +msgid "Adult" +msgstr "æˆäººå‘ã‘" + +msgid "Adventure" +msgstr "アドベンãƒãƒ£ãƒ¼" + +msgid "All" +msgstr "ã™ã¹ã¦" + +msgid "All Games" +msgstr "ã™ã¹ã¦ã®ã‚²ãƒ¼ãƒ " + +msgid "All themes up to date." +msgstr "å…¨ã¦ã®ãƒ†ãƒ¼ãƒžã‚’æ›´æ–°ã—ã¾ã—ãŸ" + +msgid "Alt dol:" +msgstr "代替dol" + +msgid "Alternative .dol:" +msgstr "代替dol" + +msgid "Anti 002 Fix:" +msgstr "エラー002対策" + +#, c-format +msgid "App. Path: %s" +msgstr "アプリã®ãƒ‘ス: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "ã“ã®é ˜åŸŸã‚’%sã—ã¾ã™ã‹?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "ã“ã®é ˜åŸŸã‚’修復ã—ã¾ã™ã‹?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "ã“ã®ã‚²ãƒ¼ãƒ ã‚’削除ã—ã¾ã™ã‹?" + +msgid "Ascending" +msgstr "昇順" + +msgid "Auto" +msgstr "自動" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "自動起動ゲーム: %.6sãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "Available Updates" +msgstr "利用å¯èƒ½ãªæ›´æ–°" + +msgid "Back" +msgstr "戻る" + +msgid "Balance Board" +msgstr "ãƒãƒ©ãƒ³ã‚¹ãƒœãƒ¼ãƒ‰" + +msgid "Basic" +msgstr "ベーシック" + +msgid "Block IOS Reload:" +msgstr "IOSã®å†èª­è¾¼ã¿ç¦æ­¢" + +msgid "Boot Disc" +msgstr "ディスクを起動" + +msgid "Boot disc" +msgstr "ディスク起動" + +msgid "Booting Wii game, please wait..." +msgstr "起動中ã§ã™ã€ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„" + +#, c-format +msgid "CFG base: %s" +msgstr "基本ディレクトリ: %s" + +msgid "Cancelled." +msgstr "キャンセルã—ã¾ã—ãŸ" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "ディレクトリを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸ: %s" + +msgid "Channel" +msgstr "ãƒãƒ£ãƒ³ãƒãƒ«" + +msgid "Cheat Codes:" +msgstr "ãƒãƒ¼ãƒˆã‚³ãƒ¼ãƒ‰:" + +msgid "Cheats: " +msgstr "ãƒãƒ¼ãƒˆ:" + +msgid "Check For Updates" +msgstr "更新を確èª" + +msgid "Checking for themes..." +msgstr "テーマを確èªä¸­ã§ã™â€¦" + +msgid "Checking for updates..." +msgstr "更新を探ã—ã¦ã„ã¾ã™â€¦" + +msgid "Choose a sorting method" +msgstr "並ã³æ›¿ãˆæ–¹æ³•ã‚’é¸ã‚“ã§ãã ã•ã„" + +msgid "Classic" +msgstr "クラシック" + +msgid "Classic Controller" +msgstr "クラシックコントローラ" + +msgid "Clear Patches:" +msgstr "パッãƒã‚’無効化:" + +#, c-format +msgid "Complete. Size: %d" +msgstr "完了ã—ã¾ã—ãŸ(サイズ: %d)" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "設定エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "接続エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ:%i" + +msgid "Console" +msgstr "コンソール" + +msgid "Console Def." +msgstr "コンソールã®æ¨™æº–" + +msgid "Controller" +msgstr "コントローラ" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "DIPモジュールをåˆæœŸåŒ–ã§ãã¾ã›ã‚“ã§ã—ãŸ" + +msgid "Country Fix:" +msgstr "リージョン回é¿:" + +msgid "Cover" +msgstr "ã‚«ãƒãƒ¼" + +msgid "Cover Image:" +msgstr "ã‚«ãƒãƒ¼ç”»åƒ:" + +msgid "Cover Style" +msgstr "ã‚«ãƒãƒ¼ã‚¹ã‚¿ã‚¤ãƒ«" + +msgid "Cover~~Back" +msgstr "ã‚«ãƒãƒ¼~~背é¢" + +msgid "Cover~~Front" +msgstr "ã‚«ãƒãƒ¼~~æ­£é¢" + +msgid "Create" +msgstr "作æˆ" + +msgid "Creator" +msgstr "制作者" + +#, c-format +msgid "Current Version: %s" +msgstr "ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"cIOS%dãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“\n" +"インストールã—ã¦ãã ã•ã„" + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "cIOS%dãŒèª­ã¿è¾¼ã‚ã¾ã›ã‚“(ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"cIOS%dã¯ã‚¹ã‚¿ãƒ–ã§ã™!\n" +"å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„" + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "cIOS%sを読ã¿è¾¼ã¿ã¾ã—ãŸ" + +msgid "DISC cover" +msgstr "ディスクカãƒãƒ¼" + +msgid "Dol Booter" +msgstr "DOL Booter" + +msgid "DOWN" +msgstr "下" + +msgid "Dance Pad" +msgstr "ダンスマット" + +msgid "Database update successful." +msgstr "データベースを更新ã—ã¾ã—ãŸ" + +msgid "Debug" +msgstr "デãƒãƒƒã‚°" + +msgid "default" +msgstr "デフォルト" + +msgid "Delete Game" +msgstr "ゲームを削除" + +msgid "Deleting" +msgstr "削除中" + +msgid "Descending" +msgstr "é™é †" + +msgid "Developer" +msgstr "開発者" + +msgid "Device is not responding!" +msgstr "デãƒã‚¤ã‚¹ãŒå¿œç­”ã—ã¦ã„ã¾ã›ã‚“!" + +msgid "Device:" +msgstr "使用デãƒã‚¤ã‚¹:" + +msgid "Disc" +msgstr "ディスク" + +msgid "Disc (Ask)" +msgstr "ディスク(å°‹ã­ã‚‹)" + +msgid "DML version:" +msgstr "DMlãƒãƒ¼ã‚¸ãƒ§ãƒ³:" + +msgid "Done." +msgstr "完了ã—ã¾ã—ãŸ" + +msgid "Download .txt" +msgstr "txtをダウンロード" + +msgid "Download All Covers" +msgstr "å…¨ã¦ã®ã‚«ãƒãƒ¼ã‚’ダウンロード" + +msgid "Download All Missing Covers" +msgstr "ä¸è¶³ã—ã¦ã„ã‚‹ã‚«ãƒãƒ¼ã‚’ダウンロード" + +msgid "Download game information" +msgstr "ゲームインフォをダウンロード" + +msgid "Download Missing Covers" +msgstr "ä¸è¶³ã—ã¦ã„ã‚‹ã‚«ãƒãƒ¼ã‚’ダウンロード" + +msgid "Download Plugins" +msgstr "プラグインをダウンロード" + +msgid "Download Themes" +msgstr "テーマをダウンロード" + +msgid "Download complete." +msgstr "ダウンロードã—ã¾ã—ãŸ" + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "ゲーマーカード#%dã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Download titles.txt" +msgstr "titles.txtをダウンロード" + +msgid "Downloadable Content" +msgstr "追加データをå—ä¿¡å¯èƒ½" + +msgid "Downloading ALL MISSING covers" +msgstr "ã™ã¹ã¦ã®ä¸è¶³ã‚«ãƒãƒ¼ã‚’ダウンロードã—ã¦ã„ã¾ã™" + +msgid "Downloading ALL covers" +msgstr "ã™ã¹ã¦ã®ã‚«ãƒãƒ¼ã‚’ダウンロードã—ã¦ã„ã¾ã™" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "%.6sã®ã™ã¹ã¦ã®ã‚«ãƒãƒ¼ã‚’ダウンロードã—ã¦ã„ã¾ã™" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "" +"%.6sã®ä¸è¶³ã—ã¦ã„ã‚‹ã‚«ãƒãƒ¼ã‚’\n" +"ダウンロードã—ã¦ã„ã¾ã™" + +msgid "Downloading Theme previews..." +msgstr "プレビューをダウンロードã—ã¦ã„ã¾ã™â€¦" + +msgid "Downloading cheats..." +msgstr "コードをダウンロード中…" + +msgid "Downloading database." +msgstr "データベースをダウンロード中…" + +msgid "Downloading titles.txt ..." +msgstr "titles.txtをダウンロード中…" + +#, c-format +msgid "Downloading: %s" +msgstr "ダウンロード中: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "%sã‚’%sã¨ã—ã¦ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ã¦ã„ã¾ã™" + +msgid "Downloads" +msgstr "ダウンロード数" + +msgid "Drums" +msgstr "タルコンガ" + +msgid "Dump savegame" +msgstr "セーブデータをダンプ" + +msgid "Dutch" +msgstr "オランダ語" + +msgid "ERROR" +msgstr "エラー" + +msgid "ERROR creating file" +msgstr "ファイルã®ä½œæˆã‚¨ãƒ©ãƒ¼" + +msgid "ERROR game opt" +msgstr "ゲーム設定エラー" + +msgid "ERROR reading BCA!" +msgstr "BCAã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼" + +#, c-format +msgid "ERROR removing %s" +msgstr "%s移動中エラー" + +msgid "ERROR writing BCA!" +msgstr "BCA書ãè¾¼ã¿ä¸­ã‚¨ãƒ©ãƒ¼" + +msgid "ERROR!" +msgstr "エラー!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "エラー!(ret = %d)" + +msgid "ERROR:" +msgstr "エラー:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "エラー:%sã¯ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "エラー:アプリローダー %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "エラー:BCAã«ã¯cIOS222/223v4ãŒå¿…è¦ã§ã™" + +msgid "ERROR: Cache Close" +msgstr "エラー:キャッシュãŒé–‰ã˜ã‚‰ã‚Œã¦ã„ã¾ã™" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "エラー:ゲームを起動ã§ãã¾ã›ã‚“ (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "エラー:ã™ã§ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã™" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "エラー:証明書ã®å–å¾—%d" + +msgid "ERROR: Invalid Game ID" +msgstr "エラー:ä¸æ­£ãªã‚²ãƒ¼ãƒ IDã§ã™" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "エラー:EHCモジュールã®èª­ã¿è¾¼ã¿ä¸­(%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERROR: NTFS書ãè¾¼ã¿ã¯ç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™!\n" +"(「ntfs_write=1ã€ã‚’セットã—ã¦ãã ã•ã„)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "エラー:領域ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "エラー:Wiiディスクã§ã¯ã‚ã‚Šã¾ã›ã‚“" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "エラー:オフセット(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "エラー:領域ã®å±•é–‹%d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "エラー:領域ã®å±•é–‹(0x%llx)%d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "エラー: フラグリストã®ã‚»ãƒƒãƒˆ(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "エラー: WBFSã®ã‚»ãƒƒãƒˆ: %d" + +msgid "ERROR: Setting SD mode" +msgstr "エラー:SDモードã¸ã®è¨­å®š" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "エラー:USBã®åˆæœŸåŒ– (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"エラー: cIOS249rev18、cIOS222v4、\n" +"cIOS223v4、cIOS224v5以上ãŒ\n" +"FAT領域ã‹ã‚‰ã®ã‚²ãƒ¼ãƒ ã®èµ·å‹•ã«å¿…è¦ã§ã™!\n" +"IOS249ã‚’æ›´æ–°ã™ã‚‹ã‹åˆ¥ã®IOSã‚’é¸æŠžã—ã¦ãã ã•ã„" + +msgid "ERROR: cache: out of memory" +msgstr "エラー:メモリä¸è¶³" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "%s作æˆä¸­ã®ã‚¨ãƒ©ãƒ¼" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "エラー:フラグå–得リスト: %d" + +msgid "ERROR: memory overlap!" +msgstr "エラー:メモリã®é‡è¤‡" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"マルãƒWBFS領域ã¯\n" +"IOS222/223-mloadã ã‘ã§ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™" + +msgid "ERROR: not enough free space!!" +msgstr "エラー:空ã容é‡ãŒã‚ã‚Šã¾ã›ã‚“" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "エラー:NTFSã¯å–り外ã•ã‚Œã¦ã„ã¾ã›ã‚“" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "エラー:%.6sé–‹ã中" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "エラー: フラグリストã®ã‚»ãƒƒãƒˆ: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "エラー: フラグメントã®æ§‹æˆ %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "エラー:%s (%d)書ãè¾¼ã¿ä¸­" + +msgid "EXTEND" +msgstr "æ‹¡å¼µ" + +msgid "Ejecting DVD..." +msgstr "DVDã‚’å–り出ã—ã¦ã„ã¾ã™â€¦" + +msgid "English" +msgstr "英語" + +msgid "English Title" +msgstr "英語å" + +msgid "Enter Code: " +msgstr "コードを入力ã—ã¦ãã ã•ã„:" + +msgid "Error GRRLIB init" +msgstr "GRRLIBã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error Initializing Network." +msgstr "接続ã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error adding disc!" +msgstr "ディスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error creating directory..." +msgstr "フォルダã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error discarding options!" +msgstr "設定ã®ç ´æ£„ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error downloading theme preview..." +msgstr "プレビューãŒãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ" + +msgid "Error downloading theme..." +msgstr "テーマã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error downloading themes..." +msgstr "テーマã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error downloading update..." +msgstr "æ›´æ–°ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error downloading updates..." +msgstr "æ›´æ–°ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸâ€¦" + +msgid "Error downloading." +msgstr "ダウンロードã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error establishing connection" +msgstr "接続ã®ç¢ºç«‹ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error extracting theme..." +msgstr "テーマã®è§£å‡ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error opening database, update did not complete." +msgstr "データベースを開ã‘ã¾ã›ã‚“ã€æ›´æ–°ã¯å®Œäº†ã—ã¦ã„ã¾ã›ã‚“" + +#, c-format +msgid "Error opening: %s" +msgstr "展開中エラー: %s" + +#, c-format +msgid "Error playing %s" +msgstr "%s起動中ã®ã‚¨ãƒ©ãƒ¼" + +msgid "Error reading .dol" +msgstr ".dolã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error reading dol header" +msgstr "dolã®ãƒ˜ãƒƒãƒ€ã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ" + +#, c-format +msgid "Error saving %s" +msgstr "%sä¿å­˜ä¸­ã®ã‚¨ãƒ©ãƒ¼" + +msgid "Error saving options!" +msgstr "設定ã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "Error saving settings!" +msgstr "設定ã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"プレイ履歴ファイルエラーã§ã™\n" +"Wiiメニューã‹ã‚‰å§‹ã‚ã‚‹ã¨ä¿®å¾©ã§ãã¾ã™" + +msgid "Error: Invalid PNG image!" +msgstr "エラー:ä¸æ­£ãªPNGファイルã§ã™" + +msgid "Error: no URL." +msgstr "URLãŒã‚ã‚Šã¾ã›ã‚“" + +msgid "Error: no data." +msgstr "データãŒã‚ã‚Šã¾ã›ã‚“" + +msgid "Exit" +msgstr "終了" + +#, c-format +msgid "Extracting: %s" +msgstr "解å‡ä¸­: %s" + +msgid "FAIL" +msgstr "失敗" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOCエラー %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "致命的:アロケーショングリッド(%d)" + +#, c-format +msgid "FILL %d" +msgstr "満タン %d" + +msgid "FLAT cover" +msgstr "æ­£é¢ã‚«ãƒãƒ¼" + +msgid "FULL cover" +msgstr "完全ãªã‚«ãƒãƒ¼" + +msgid "Fav" +msgstr "ãŠæ°—ã«å…¥ã‚Š" + +msgid "Fav: Off" +msgstr "ãŠæ°—ã«å…¥ã‚Š:オフ" + +msgid "Fav: On" +msgstr "ãŠæ°—ã«å…¥ã‚Š:オン" + +msgid "Favorite" +msgstr "ãŠæ°—ã«å…¥ã‚Š" + +msgid "Favorite Games" +msgstr "ãŠæ°—ã«å…¥ã‚Šã®ã‚²ãƒ¼ãƒ " + +msgid "Favorite:" +msgstr "ãŠæ°—ã«å…¥ã‚Š:" + +msgid "Favorites:" +msgstr "ãŠæ°—ã«å…¥ã‚Š:" + +msgid "Fighting" +msgstr "格闘" + +#, c-format +msgid "File not found! %s" +msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“%s" + +msgid "Filter" +msgstr "絞り込ã¿" + +msgid "Filter Games" +msgstr "ゲームã®çµžã‚Šè¾¼ã¿" + +msgid "Filter by Controller" +msgstr "コントローラã§çµžã‚Šè¾¼ã‚€" + +msgid "Filter by Genre" +msgstr "ジャンルã§çµžã‚Šè¾¼ã‚€" + +msgid "Filter by Online Features" +msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯å¯¾å¿œæ©Ÿèƒ½ã§çµžã‚Šè¾¼ã‚€" + +msgid "Filter:" +msgstr "絞り込ã¿:" + +msgid "Fixing EXTEND partition..." +msgstr "拡張領域を修復中ã§ã™â€¦" + +msgid "Force Devolution:" +msgstr "Devolutionを強制使用:" + +msgid "Force NTSC" +msgstr "強制NTSC" + +msgid "Force PAL50" +msgstr "強制PAL50" + +msgid "Force PAL60" +msgstr "強制PAL60" + +msgid "Formatting" +msgstr "åˆæœŸåŒ–中" + +#, c-format +msgid "Found %s" +msgstr "%s見ã¤ã‹ã‚Šã¾ã—ãŸ" + +msgid "French" +msgstr "フランス語" + +msgid "Full" +msgstr "フル" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "ゲームã®æ¨™æº–" + +msgid "Game Options" +msgstr "ゲーム設定" + +#, c-format +msgid "Game Options: %s" +msgstr "ゲーム設定:%s" + +msgid "Gamecube" +msgstr "ゲームキューブ" + +msgid "Gamer Card:" +msgstr "ゲーマーカード:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "ゲーマーカード#%dã«å ±å‘Šã—ã¾ã—ãŸ: %.*s" + +msgid "Genre" +msgstr "ジャンル" + +msgid "German" +msgstr "ドイツ語" + +msgid "Global Options" +msgstr "基本設定" + +msgid "Guitar" +msgstr "ギター" + +msgid "HQ cover" +msgstr "高å“質カãƒãƒ¼" + +msgid "HTTP Response was without a file" +msgstr "HTTPã®å¿œç­”ã¯ãƒ•ã‚¡ã‚¤ãƒ«ç„¡ã—ã§ã—ãŸ" + +msgid "Hide" +msgstr "éš ã™" + +msgid "Hide Game:" +msgstr "ゲームを隠ã™:" + +msgid "Hold button B to cancel." +msgstr "キャンセルã™ã‚‹ã«ã¯Bボタンを押ã—ã¦ãã ã•ã„" + +msgid "Home Menu" +msgstr "HOMEメニュー" + +msgid "Homebrew Channel" +msgstr "Homebrewãƒãƒ£ãƒ³ãƒãƒ«" + +msgid "Hook Type:" +msgstr "フックタイプ:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "IDãŒä¸€è‡´ã—ã¾ã›ã‚“: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOSã®å†èª­ã¿è¾¼ã¿:ç¦æ­¢" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "%dã®ã‚³ãƒ¼ãƒ‰ã§IOSã‚’é–‹ã(%s)ã®ã«å¤±æ•—ã—ã¾ã—ãŸ" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"ã‚‚ã—CFGãŒèµ·å‹•ã—ãªã‘ã‚Œã°boot.dolを削除後\n" +"boot.dol.bakã‚’boot.dolã«åå‰ã‚’変ãˆã¦\n" +"å†è©¦è¡Œã—ã¦ãã ã•ã„" + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "%xã‚’LZ77ã§ç¬¦å·åŒ–中ã«çŸ›ç›¾ãŒã‚ã‚Šã¾ã™" + +msgid "Info" +msgstr "情報" + +#, c-format +msgid "Info file: %s" +msgstr "ファイル情報:%s" + +msgid "Initializing Network..." +msgstr "接続中…" + +msgid "Install" +msgstr "インストール" + +msgid "Install Date" +msgstr "インストール日" + +msgid "Install Game" +msgstr "ゲームをインストール" + +msgid "Install game" +msgstr "ゲームをインストール" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "インストールエラー!(ret = %d)" + +msgid "Installing game, please wait..." +msgstr "インストール中ã§ã™ã€ãŠå¾…ã¡ãã ã•ã„…" + +msgid "Invalid .dol" +msgstr "ä¸æ­£ãªdolã§ã™" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "ä¸æ­£ãªé ˜åŸŸã§ã™: '%s'" + +msgid "Italian" +msgstr "イタリア語" + +msgid "Japanese" +msgstr "日本語" + +msgid "Japanese Title" +msgstr "日本語å" + +msgid "Keyboard" +msgstr "キーボード" + +msgid "Korean" +msgstr "韓国語" + +msgid "LED:" +msgstr "LED点ç¯:" + +msgid "LEFT" +msgstr "å·¦" + +msgid "LOCKED!" +msgstr "ロックã—ã¾ã—ãŸ" + +msgid "Language:" +msgstr "言語:" + +msgid "Last Play Date" +msgstr "最終プレイ日" + +msgid "Launch Methods" +msgstr "起動方法" + +msgid "Load OK!" +msgstr "読ã¿è¾¼ã¿ã¾ã—ãŸ" + +#, c-format +msgid "Loader Version: %s" +msgstr "ローダーã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³: %s" + +msgid "Loading ..." +msgstr "読ã¿è¾¼ã¿ä¸­ï½¥ï½¥ï½¥" + +msgid "Main" +msgstr "メイン" + +msgid "Main Menu" +msgstr "メインメニュー" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "端ã«è¿‘ã„æ–¹ã®USBãƒãƒ¼ãƒˆã‚’使用ã—ã¦ãã ã•ã„" + +msgid "Manage" +msgstr "管ç†ç”»é¢" + +msgid "Manage Cheats" +msgstr "ãƒãƒ¼ãƒˆã‚’管ç†" + +msgid "Microphone" +msgstr "マイク" + +msgid "Motion+" +msgstr "Wiiモーションプラス" + +msgid "Mounting device, please wait..." +msgstr "デãƒã‚¤ã‚¹ã‚’マウント中ã§ã™ã€ãŠå¾…ã¡ãã ã•ã„…" + +msgid "Music" +msgstr "音楽" + +#, c-format +msgid "Music file from dir: %s" +msgstr "音楽ファイルã®å ´æ‰€:%s" + +#, c-format +msgid "Music file size: %d" +msgstr "音楽ファイルã®ã‚µã‚¤ã‚º: %d" + +#, c-format +msgid "Music file: %s" +msgstr "音楽ファイル: %s" + +msgid "Music: Disabled" +msgstr "BGM:無効" + +msgid "Music: Enabled" +msgstr "BGM:有効" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "%sã‚’%sã‹ã‚‰æŽ¢ã—ã¦ã„ã¾ã™" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "BGM:å†ç”Ÿã™ã‚‹æ¬¡ã®ãƒ•ã‚¡ã‚¤ãƒ«ç›®æ¬¡:%i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "BGM:%sãŒ%i個見ã¤ã‹ã‚Šã¾ã—ãŸ" + +msgid "Music: musicArray contents: " +msgstr "BGM:音楽コンテンツ: " + +msgid "NAND Emu:" +msgstr "NANDエミュレーション:" + +msgid "NAND Emu Path:" +msgstr "エミュNANDã®ãƒ‘ス:" + +msgid "NoDisc:" +msgstr "ディスクãªã—èµ·å‹•:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"注æ„:rev10以å‰ã®cIOS249ã¯:\n" +"SDHCã‹ã‚‰ã®ã‚²ãƒ¼ãƒ ã®èµ·å‹•ã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"注æ„:rev14以å‰ã®cIOS249ã¯:\n" +"error#001ã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"注æ„:rev9以å‰ã®cIOS249ã¯:\n" +"USBã‹ã‚‰ã®ã‚²ãƒ¼ãƒ èµ·å‹•ã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"注æ„:cIOS249 rev13ã§ã¯:\n" +"ゲームを終了ã™ã‚‹ã¨ã\n" +"é›»æºã‚’切るã‹ãƒªã‚»ãƒƒãƒˆã—ã¦ã‹ã‚‰\n" +"別ã®ã‚²ãƒ¼ãƒ ã‚’èµ·å‹•ã—ã¦ãã ã•ã„" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"注æ„:cIOS249 rev14ã«ã¯:\n" +"二層ã®DVDã§ã®å•é¡ŒãŒã‚ã‚Šã¾ã™\n" +"ã“ã®ã‚²ãƒ¼ãƒ ã‚’インストールã™ã‚‹ã¨ãã¯\n" +"別ã®revã‹IOSを使用ã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™\n" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"注æ„:æ›´æ–°ã‚’å映ã•ã›ã‚‹ãŸã‚ã«\n" +"ローダーをå†èµ·å‹•ã—ã¦ãã ã•ã„" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"注æ„: 領域 P#%dã¯æ‹¡å¼µå½¢å¼ã§ã™ãŒ\n" +"WBFS領域をå«ã‚“ã§ã„ã¾ã™\n" +"ã“ã‚Œã¯ä¸æ­£ãªæ§‹æˆã§ã™\n" +"%sボタンを押ã™ã¨é ˜åŸŸå½¢å¼ã‚’拡張形å¼ã‹ã‚‰å¤‰æ›´ã—ã¾ã™" + +msgid "NTFS compression not supported!" +msgstr "NTFS圧縮ã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“" + +msgid "NTFS encryption not supported!" +msgstr "NTFS解å‡ã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“" + +msgid "Network connection established." +msgstr "接続ã—ã¾ã—ãŸ" + +msgid "Network error. Can't update gamercards." +msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ゲーマーカードを更新ã§ãã¾ã›ã‚“" + +msgid "New Themes" +msgstr "æ–°ã—ã„テーマ" + +msgid "Nintendo DS" +msgstr "ニンテンドーDS" + +msgid "Nintendo DS Connectivity" +msgstr "DSã¨ã®é€šä¿¡æ©Ÿèƒ½" + +msgid "No" +msgstr "ã„ã„ãˆ" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "'%s'ã®URLã«ãƒ‰ãƒ¡ã‚¤ãƒ³ãŒã‚ã‚Šã¾ã›ã‚“" + +msgid "No games found!" +msgstr "ゲームãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "No partition selected!" +msgstr "領域ãŒé¸ã°ã‚Œã¦ã„ã¾ã›ã‚“" + +msgid "No themes found." +msgstr "テーマãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "No updates found." +msgstr "æ›´æ–°ã¯ã‚ã‚Šã¾ã›ã‚“" + +msgid "None found on disc" +msgstr "ディスクã«ä½•ã‚‚見ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "NTSC-J patch:" +msgstr "セーブ破æ回é¿:" + +msgid "Not Found!" +msgstr "見ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "Number of Online Players" +msgstr "通信時ã®ãƒ—レイå¯èƒ½äººæ•°" + +msgid "Number of Players" +msgstr "プレイå¯èƒ½äººæ•°" + +msgid "Nunchuk" +msgstr "ヌンãƒãƒ£ã‚¯" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "完了ã—ã¾ã—ãŸ" + +msgid "Ocarina (cheats):" +msgstr "ãƒãƒ¼ãƒˆæ©Ÿèƒ½:" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarinaãƒãƒ¼ãƒˆãƒžãƒãƒ¼ã‚¸ãƒ£ãƒ¼" + +msgid "Ocarina: Code Error" +msgstr "Ocarina:コードエラー" + +msgid "Ocarina: Codes found." +msgstr "Ocarina:コードãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ" + +msgid "Ocarina: No codes found" +msgstr "Ocarina:コードãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina:メモリä¸è¶³ã§ã™" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina:コードを探ã—ã¦ã„ã¾ã™â€¦" + +msgid "Ocarina: Too many codes" +msgstr "コードãŒå¤šã™ãŽã¾ã™" + +msgid "Off" +msgstr "使用ã—ãªã„" + +msgid "On" +msgstr "使用ã™ã‚‹" + +msgid "Online" +msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯" + +msgid "Online Content" +msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯å¯¾å¿œ" + +msgid "Online Play" +msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ—レイ" + +msgid "Online Players" +msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ—レイ人数" + +msgid "Online Score List" +msgstr "オンラインスコアリスト" + +msgid "Online Updates" +msgstr "オンライン更新" + +msgid "Open Sort" +msgstr "並ã³æ›¿ãˆã‚’é–‹ã" + +msgid "Open Style" +msgstr "スタイルを開ã" + +msgid "Opening DVD disc..." +msgstr "DVDã‚’é–‹ã„ã¦ã„ã¾ã™â€¦" + +#, c-format +msgid "Opening partition: %s" +msgstr "é–‹ã„ã¦ã„る領域: %s" + +msgid "Options" +msgstr "オプション" + +msgid "Options discarded for this game." +msgstr "ã“ã®ã‚²ãƒ¼ãƒ ã®ãŸã‚設定を破棄ã—ã¾ã—ãŸ" + +msgid "Options saved for this game." +msgstr "ã“ã®ã‚²ãƒ¼ãƒ ã®ãŸã‚設定をä¿å­˜ã—ã¾ã—ãŸ" + +msgid "Out of memory" +msgstr "メモリä¸è¶³" + +msgid "PAD HOOK" +msgstr "パッドフック:" + +msgid "Page:" +msgstr "ページ:" + +msgid "Partition:" +msgstr "使用領域:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "領域: %sãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "Party" +msgstr "パーティ" + +msgid "Paused Start" +msgstr "ãƒãƒ¼ã‚ºã‚¹ã‚¿ãƒ¼ãƒˆ" + +msgid "Platformer" +msgstr "プラットフォーム" + +msgid "Play Count" +msgstr "プレイ回数" + +msgid "Players" +msgstr "プレイヤー" + +msgid "Please insert a game disc..." +msgstr "ゲームディスクを入れã¦ãã ã•ã„…" + +#, c-format +msgid "Press %s button for options." +msgstr "%sボタンã§è¨­å®šã¸ç§»å‹•ã—ã¾ã™" + +#, c-format +msgid "Press %s button to %s." +msgstr "%sボタンã§%sã—ã¾ã™" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "%sボタンã§ã‚³ãƒ¼ãƒ‰ã‚’é©ç”¨ã—ã¾ã™" + +#, c-format +msgid "Press %s button to cancel." +msgstr "%sボタンã§ã‚­ãƒ£ãƒ³ã‚»ãƒ«ã—ã¾ã™" + +#, c-format +msgid "Press %s button to change device." +msgstr "%sボタンã§ãƒ‡ãƒã‚¤ã‚¹ã‚’変更ã—ã¾ã™" + +#, c-format +msgid "Press %s button to continue." +msgstr "%sボタンã§ç¶šè¡Œã—ã¾ã™" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "%sボタンã§FSを削除ã—ã¾ã™" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "%sボタンã§BCAをダンプã—ã¾ã™" + +#, c-format +msgid "Press %s button to exit." +msgstr "%sボタンã§çµ‚了ã—ã¾ã™" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "%sボタンã§EXTEND/WBFSを修復ã—ã¾ã™" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "%sボタンã§WBFSフォーマットã—ã¾ã™" + +#, c-format +msgid "Press %s button to go back." +msgstr "%sボタンã§æˆ»ã‚Šã¾ã™" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "%sボタンã§é ˜åŸŸã‚’é¸ã³ã¾ã™" + +#, c-format +msgid "Press %s button to select." +msgstr "%sボタンã§é¸ã³ã¾ã™" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "%sボタンã§ã‚³ãƒ¼ãƒ‰ã‚’スキップã—ã¾ã™" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "%sボタンã§FATã‚’åŒæœŸã—ã¾ã™" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "%sボタンã§å…¨ç”»é¢ãƒ—レビューをã—ã¾ã™" + +#, c-format +msgid "Press %s for game options" +msgstr "%sボタンã§å‰ã®ç”»é¢ã«ç§»å‹•ã—ã¾ã™" + +#, c-format +msgid "Press %s for global options" +msgstr "%sボタンã§åŸºæœ¬è¨­å®šã«ç§»å‹•ã—ã¾ã™" + +#, c-format +msgid "Press %s to discard options" +msgstr "%sボタンã§è¨­å®šã‚’破棄ã—ã¾ã™" + +#, c-format +msgid "Press %s to download and update" +msgstr "%sボタンã§æ›´æ–°ã—ã¾ã™" + +#, c-format +msgid "Press %s to download this theme" +msgstr "%sボタンã§ã“ã®ãƒ†ãƒ¼ãƒžã‚’ダウンロードã—ã¾ã™" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "%sボタンã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ã€%sボタンã§æˆ»ã‚Šã¾ã™" + +#, c-format +msgid "Press %s to return" +msgstr "%sボタンã§æˆ»ã‚Šã¾ã™" + +#, c-format +msgid "Press %s to save global settings" +msgstr "%sボタンã§åŸºæœ¬è¨­å®šã‚’ä¿å­˜ã—ã¾ã™" + +#, c-format +msgid "Press %s to save options" +msgstr "%sボタンã§è¨­å®šã‚’ä¿å­˜ã—ã¾ã™" + +#, c-format +msgid "Press %s to save selection" +msgstr "%sボタンã§é¸æŠžã‚’ä¿å­˜ã—ã¾ã™" + +#, c-format +msgid "Press %s to select filter type" +msgstr "%sボタンã§çµžã‚Šè¾¼ã‚€æ¡ä»¶ã‚’é¸ã³ã¾ã™" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "%sボタンã§ä¸¦ã³æ›¿ãˆæ–¹æ³•ã‚’é¸ã³ã¾ã™" + +#, c-format +msgid "Press %s to start game" +msgstr "%sボタンã§ã‚²ãƒ¼ãƒ ã‚’èµ·å‹•ã—ã¾ã™" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "%sボタンã§boot.dolã®ã¿æ›´æ–°ã—ã¾ã™" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "%s/%sボタンã§ãƒ‡ãƒã‚¤ã‚¹ã‚’é¸ã‚“ã§ãã ã•ã„" + +msgid "Press 2 to reload IOS" +msgstr "2ボタンã§IOSã‚’å†èª­ã¿è¾¼ã¿ã—ã¾ã™" + +msgid "Press A to select device" +msgstr "Aボタンã§ãƒ‡ãƒã‚¤ã‚¹ã‚’é¸æŠžã—ã¾ã™" + +msgid "Press B to exit to HBC" +msgstr "Bボタンã§HBCã«ç§»å‹•ã—ã¾ã™" + +msgid "Press HOME to reset" +msgstr "HOMEボタンã§ãƒªã‚»ãƒƒãƒˆã—ã¾ã™" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "å·¦/å³ãƒœã‚¿ãƒ³ã§IOSã‚’é¸æŠžã—ã¾ã™" + +msgid "Press any button to continue..." +msgstr "何ã‹ãƒœã‚¿ãƒ³ã‚’押ã™ã¨ç¶šè¡Œã—ã¾ã™" + +msgid "Press any button to exit..." +msgstr "何ã‹ãƒœã‚¿ãƒ³ã‚’押ã™ã¨çµ‚了ã—ã¾ã™" + +msgid "Press any button to restart..." +msgstr "何ã‹ãƒœã‚¿ãƒ³ã‚’押ã™ã¨å†èµ·å‹•ã—ã¾ã™" + +msgid "Press any button..." +msgstr "何ã‹ãƒœã‚¿ãƒ³ã‚’押ã—ã¦ãã ã•ã„" + +#, c-format +msgid "Press button %s to download covers." +msgstr "%sボタンã§ã‚«ãƒãƒ¼ã‚’ダウンロードã—ã¾ã™" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "%sボタンã§DVDã‚’å–り出ã—ã¾ã™" + +msgid "Profile:" +msgstr "プロファイル:" + +#, c-format +msgid "Profile: %s" +msgstr "プロファイル: %s" + +msgid "Program Updates" +msgstr "プログラム更新" + +msgid "Publisher" +msgstr "発売元" + +msgid "Puzzle" +msgstr "パズル" + +msgid "Quit" +msgstr "終了" + +msgid "RAW" +msgstr "無圧縮" + +msgid "RIGHT" +msgstr "å³" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "レース" + +#, c-format +msgid "Rated %s" +msgstr "レーティング[%s]" + +msgid "Rating" +msgstr "レーティング" + +msgid "Read" +msgstr "読ã¿è¾¼ã¿" + +msgid "Reading BCA..." +msgstr "BCAを読ã¿è¾¼ã¿ä¸­â€¦" + +msgid "Reboot" +msgstr "Wiiメニュー" + +msgid "Release Date" +msgstr "発売日" + +msgid "Release Notes: (short)" +msgstr "変更点:" + +msgid "Removing game, please wait..." +msgstr "ゲームを削除ã—ã¦ã„ã¾ã™ã€ãŠå¾…ã¡ãã ã•ã„…" + +msgid "Restarting Wii..." +msgstr "Wiiã‚’å†èµ·å‹•ã—ã¦ã„ã¾ã™â€¦" + +msgid "Retry: %d ...\n" +msgstr "å†è©¦è¡Œ: %d ...\n" + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "戻りã¾ã—ãŸ(ret = %d)" + +msgid "Rhythm" +msgstr "リズム" + +msgid "Rows:" +msgstr "è¡Œ:" + +msgid "Running benchmark, please wait" +msgstr "ベンãƒãƒžãƒ¼ã‚¯ã‚’èµ·å‹•ã—ã¦ã„ã¾ã™ï½¤ãŠå¾…ã¡ãã ã•ã„" + +msgid "S. Chinese" +msgstr "簡体字中国語" + +msgid "SD/SDHC Card" +msgstr "SD/SDHCカード" + +msgid "SUCCESS!" +msgstr "æˆåŠŸã—ã¾ã—ãŸ!" + +msgid "Save .gct" +msgstr "gctã‚’ä¿å­˜" + +msgid "Save Debug" +msgstr "デãƒãƒƒã‚°ã‚’ä¿å­˜" + +msgid "Save Settings" +msgstr "設定をä¿å­˜" + +msgid "Save debug.log" +msgstr "デãƒãƒƒã‚°æƒ…報をä¿å­˜" + +msgid "Saving Settings... " +msgstr "設定をä¿å­˜ä¸­â€¦" + +msgid "Saving cheats..." +msgstr "ãƒãƒ¼ãƒˆã‚’ä¿å­˜ä¸­â€¦" + +msgid "Saving gamelist.txt ... " +msgstr "gamelist.txtã‚’ä¿å­˜ä¸­â€¦" + +msgid "Saving settings..." +msgstr "設定をä¿å­˜ä¸­â€¦" + +msgid "Saving:" +msgstr "ä¿å­˜ä¸­:" + +#, c-format +msgid "Saving: %s" +msgstr "ä¿å­˜ä¸­:%s" + +msgid "Screenshot:" +msgstr "スクリーンショット:" + +msgid "Scroll:" +msgstr "スクロール:" + +msgid "Select Alternative .dol:" +msgstr "代替dolã‚’é¸ã‚“ã§ãã ã•ã„:" + +msgid "Select WBFS device:" +msgstr "WBFSデãƒã‚¤ã‚¹ã‚’é¸ã‚“ã§ãã ã•ã„:" + +msgid "Select a different partition" +msgstr "é•ã†é ˜åŸŸã‚’é¸ã‚“ã§ãã ã•ã„" + +msgid "Select a partition" +msgstr "領域をé¸ã‚“ã§ãã ã•ã„" + +msgid "Select all" +msgstr "ã™ã¹ã¦é¸æŠž" + +msgid "Select the game you want to boot" +msgstr "èµ·å‹•ã—ãŸã„ゲームをé¸ã‚“ã§ãã ã•ã„" + +msgid "Selected Game" +msgstr "é¸æŠžã•ã‚ŒãŸã‚²ãƒ¼ãƒ " + +msgid "Settings" +msgstr "設定" + +msgid "Shooter" +msgstr "シューティング" + +msgid "Show All" +msgstr "å…¨ã¦è¡¨ç¤º" + +msgid "Show cIOS info" +msgstr "cIOS情報を表示" + +msgid "Shutdown" +msgstr "シャットダウン" + +msgid "Side:" +msgstr "サイド:" + +msgid "Simulation" +msgstr "シミュレーション" + +msgid "Size(GB) Type Mount Used" +msgstr " サイズ(GB) å½¢å¼" + +#, c-format +msgid "Size: %d bytes" +msgstr "サイズ: %dãƒã‚¤ãƒˆ" + +msgid "Sort" +msgstr "並ã³æ›¿ãˆ" + +msgid "Sort Games" +msgstr "ゲームを並ã³æ›¿ãˆ" + +msgid "Sort Order:" +msgstr "並ã³æ›¿ãˆé †:" + +msgid "Sort Type:" +msgstr "並ã³æ›¿ãˆã‚¿ã‚¤ãƒ—:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "並ã³æ›¿ãˆ; %s~%s" + +msgid "Spanish" +msgstr "スペイン語" + +msgid "Sports" +msgstr "スãƒãƒ¼ãƒ„" + +msgid "Start" +msgstr "ã¯ã˜ã‚ã‚‹" + +msgid "Start Game" +msgstr "ゲームを始ã‚ã‚‹" + +msgid "Start this game?" +msgstr "ã“ã®ã‚²ãƒ¼ãƒ ã‚’始ã‚ã¾ã™ã‹?" + +msgid "Stopping DVD..." +msgstr "DVDã‚’åœæ­¢ä¸­..." + +msgid "Strategy" +msgstr "戦略ゲーム" + +msgid "Style" +msgstr "スタイル" + +msgid "Style:" +msgstr "スタイル:" + +msgid "Sync FAT free space info?" +msgstr "FATã®ç©ºã容é‡æƒ…報をåŒæœŸã—ã¾ã™ã‹?" + +msgid "Synchronizing, please wait." +msgstr "åŒæœŸä¸­ã§ã™ã€ãŠå¾…ã¡ãã ã•ã„" + +msgid "System" +msgstr "システム" + +msgid "System Def." +msgstr "システムã®æ¨™æº–" + +msgid "T. Chinese" +msgstr "ç¹ä½“字中国語" + +msgid "Theme" +msgstr "テーマ" + +msgid "Theme Info" +msgstr "テーマ情報" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"テーマã®ã‚µã‚¤ã‚ºãŒå¤§ãã™ãŽã¾ã™\n" +"CFGã‚’å†èµ·å‹•ã—ã¦ãã ã•ã„\n" + +msgid "Theme:" +msgstr "テーマ:" + +#, c-format +msgid "Theme: %s" +msgstr "テーマ: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "wii.spiffy360.comã«ãƒ†ãƒ¼ãƒžã¯ä¿å­˜ã•ã‚Œã¦ã„ã¾ã™" + +msgid "Themes to download" +msgstr "ダウンロードã™ã‚‹ãƒ†ãƒ¼ãƒž" + +msgid "Themes with updates" +msgstr "æ›´æ–°ã®ã‚るテーマ" + +msgid "Title" +msgstr "タイトル" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "ãƒãƒ¼ãƒˆãŒå¤šã™ãŽã¾ã™(%d)" + +msgid "Too many code lines!" +msgstr "コードã®è¡Œæ•°ãŒå¤šã™ãŽã¾ã™" + +#, c-format +msgid "Too many fragments! %d" +msgstr "フラグãŒå¤šã™ãŽã¾ã™%d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "試行中(URL#%d)…" + +msgid "UNUSED" +msgstr "ä¸ä½¿ç”¨" + +msgid "UP" +msgstr "上" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "'%s'ã®URLã¯'http://'ã§å§‹ã¾ã£ã¦ã„ã¾ã›ã‚“" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "'%s'ã®URLã¯ãƒ‘スã®ä¸€éƒ¨ã‚’æŒã£ã¦ã„ã¾ã›ã‚“" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Geckoを見ã¤ã‘ã¾ã—ãŸã€‚デãƒãƒƒã‚°ã‚’有効ã«ã—ã¾ã—ãŸ" + +msgid "USB Mass Storage Device" +msgstr "USBデãƒã‚¤ã‚¹" + +msgid "Unknown" +msgstr "ä¸æ˜Ž" + +msgid "Unknown syntax!" +msgstr "ä¸æ˜Žãªæ§‹æ–‡ã§ã™" + +msgid "Unplayed" +msgstr "未プレイ" + +msgid "Unplayed Games" +msgstr "プレイã•ã‚Œã¦ã„ãªã„ゲーム" + +msgid "Update WiiTDB Game Database" +msgstr "WiiTDBã®ã‚²ãƒ¼ãƒ ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’æ›´æ–°" + +msgid "Update themes" +msgstr "テーマを更新" + +msgid "Update titles.txt" +msgstr "titles.txtã‚’æ›´æ–°" + +msgid "Updates" +msgstr "æ›´æ–°" + +msgid "Updating database." +msgstr "データベースを更新中ã§ã™" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "使用容é‡: %.1fGB 空ã容é‡: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "ä¿å­˜ã•ã‚ŒãŸä»£æ›¿dolを使用ã—ã¦ã„ã¾ã™" + +msgid "Version" +msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³" + +msgid "Video Patch:" +msgstr "映åƒãƒ‘ッãƒ:" + +msgid "Video:" +msgstr "映åƒå‡ºåŠ›:" + +msgid "View" +msgstr "表示" + +msgid "Vitality Sensor" +msgstr "ãƒã‚¤ã‚¿ãƒªãƒ†ã‚£ã‚»ãƒ³ã‚µãƒ¼" + +msgid "WARNING:" +msgstr "警告:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "使用容é‡: %.1fGB 空ã容é‡: %.1fGB" + +msgid "Wheel" +msgstr "Wiiãƒãƒ³ãƒ‰ãƒ«" + +msgid "Wide Screen:" +msgstr "ワイドスクリーン" + +msgid "Wii Speak" +msgstr "Wiiスピーク" + +msgid "WiiTDB Game Database" +msgstr "WiiTDBゲームデータベース" + +msgid "Wiimote" +msgstr "Wiiリモコン" + +msgid "Write Playlog:" +msgstr "Wiiä¼è¨€æ¿ã«æ›¸ã:" + +#, c-format +msgid "Writing to %s" +msgstr "%sã«æ›¸ãè¾¼ã¿ä¸­" + +#, c-format +msgid "Writing: %s" +msgstr "書ãè¾¼ã¿ä¸­: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "ä¸æ­£ãªã‚µã‚¤ã‚ºã§ã™: %d (%d)" + +msgid "Yes" +msgstr "ã¯ã„" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"一度å–り外ã—ã¦ã‚‚ã†ã„ã¡ã©\n" +"デãƒã‚¤ã‚¹ã‚’å–り付ã‘ã‚‹ã‹ã€\n" +"ã‚‚ã†å°‘ã—å¾…ã£ã¦ãã ã•ã„" + +msgid "Zapper" +msgstr "Wiiザッパー" + +msgid "[ CHANGED ]" +msgstr "[ 変更ã•ã‚Œã¦ã„ã¾ã™ ]" + +msgid "[ FOUND ]" +msgstr "[ ã‚ã‚Šã¾ã™ ]" + +msgid "[ SAVED ]" +msgstr "[ ä¿å­˜æ¸ˆã¿ ]" + +msgid "[HOME]" +msgstr " " + +msgid "[No HQ]" +msgstr "[HQç„¡ã—]" + +msgid "[USED]" +msgstr "[使用中]" + +msgid "[default]" +msgstr "[デフォルト]" + +msgid "[saved]" +msgstr "[ä¿å­˜ã—ã¾ã—ãŸ]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "ä¸æ­£ãªwbfsファイルã§ã™: %s" + +msgid "calculating space, please wait..." +msgstr "領域ã®è¨ˆç®—中ã§ã™ã€ãŠå¾…ã¡ãã ã•ã„…" + +msgid "delete" +msgstr "削除" + +#, c-format +msgid "dest: %p - %p" +msgstr "目的地: %p - %p" + +msgid "discard" +msgstr "破棄" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "%sã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’解決ã§ãã¾ã›ã‚“ã§ã—ãŸ" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "読ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "1~%d人用" + +msgid "for 1 player" +msgstr "1人用" + +msgid "format" +msgstr "フォーマット" + +msgid "free" +msgstr "空ã" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "リニア読ã¿è¾¼ã¿é€Ÿåº¦: %.2f MB/秒" + +msgid "linear..." +msgstr "リニア…" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "音楽ファイルãŒå¤§ãã™ãŽã§ã™(%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr ".mp3ã‚‚ã—ãã¯.modãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +msgid "no file" +msgstr "ファイル無ã—" + +msgid "none" +msgstr "ç„¡ã—" + +msgid "or format a WBFS partition." +msgstr "ã‚‚ã—ãã¯WBFS領域ã«ã—ã¦ãã ã•ã„" + +msgid "page" +msgstr "ページ" + +msgid "parse error" +msgstr "構文解æžã‚¨ãƒ©ãƒ¼" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "ランダム読ã¿è¾¼ã¿é€Ÿåº¦: %.2f mb/秒" + +msgid "random..." +msgstr "ランダム…" + +msgid "reset" +msgstr "åˆæœŸåŒ–" + +msgid "revert" +msgstr "戻ã™" + +msgid "save" +msgstr "ä¿å­˜" + +#, c-format +msgid "split error: %s" +msgstr "分割エラー: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "Wiiディスクを開ã‘ã¾ã›ã‚“" + +#, c-format +msgid "used: %p - %p" +msgstr "使用済ã¿: %p - %p" + + +msgid "No Hooks" +msgstr "フックãªã—" + +#~ msgid "Front" +#~ msgstr "æ­£é¢" + +#~ msgid "Loading previous game list..." +#~ msgstr "以å‰ã®ã‚²ãƒ¼ãƒ ã®ãƒªã‚¹ãƒˆã‚’読ã¿è¾¼ã¿ä¸­" + +#~ msgid "Remove Game" +#~ msgstr "ゲームを削除" diff --git a/Languages/KO.lang b/Languages/KO.lang new file mode 100644 index 0000000..3259d1d --- /dev/null +++ b/Languages/KO.lang @@ -0,0 +1,1879 @@ +# CFG USB Loader language file. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2011-04-12 20:48+0100\n" +"Last-Translator: cherries4u \n" +"Language-Team: Korean Translation Team\n" +"Language: KO\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB 사용가능 ì „ì²´: %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% of %.2fGB (%c) 예ìƒì‹œê°„: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%d:%02d:%02dì— %.2fGB ê°€ 복사ë¨" + +#, c-format +msgid "%d available" +msgstr "%d 사용가능" + +#, c-format +msgid "%d more notes" +msgstr "%d 추가정보" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s 분할: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, 기다려 주세요." + +#, c-format +msgid "(%d online)" +msgstr "(%d 온ë¼ì¸)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%dì´ˆ 시간초과)" + +msgid "(This can take a couple of minutes)" +msgstr "(몇 ë¶„ì •ë„ ê±¸ë¦´ 수 있습니다.)" + +msgid ".dol too small" +msgstr ".dol 파ì¼ì´ 너무 작습니다." + +msgid "3D cover" +msgstr "3D 커버" + +msgid "< ASC >" +msgstr "<오름차순>" + +msgid "< DESC >" +msgstr "<내림차순>" + +msgid "< DOWNLOAD >" +msgstr "<다운로드>" + +msgid "About" +msgstr "ì •ë³´" + +msgid "Action" +msgstr "ì•¡ì…˜" + +msgid "Additional config:" +msgstr "추가 설정:" + +msgid "Admin Lock:" +msgstr "ê´€ë¦¬ìž ìž ê¸ˆ:" + +msgid "Admin Unlock" +msgstr "ê´€ë¦¬ìž í•´ì œ" + +msgid "Adult" +msgstr "성ì¸ìš©" + +msgid "Adventure" +msgstr "어드밴처" + +msgid "All" +msgstr "ì „ì²´" + +msgid "All Games" +msgstr "모든 게임" + +msgid "All themes up to date." +msgstr "모든 테마가 최신버전 입니다. " + +msgid "Alt dol:" +msgstr "대체 dol:" + +msgid "Alternative .dol:" +msgstr "대체 dol:" + +msgid "Anti 002 Fix:" +msgstr "002 오류 수정:" + +#, c-format +msgid "App. Path: %s" +msgstr "프로그램경로: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"ì´ íŒŒí‹°ì…˜ì— %sì„\n" +"하시겠습니까?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"ì´ íŒŒí‹°ì…˜ì„ ë³µêµ¬\n" +"하시겠습니까?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"ì´ ê²Œìž„ì„ ì •ë§ ì‚­ì œ\n" +"하시겠습니까?" + +msgid "Ascending" +msgstr "오름차순" + +msgid "Auto" +msgstr "ìžë™" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "게임 ìžë™ 실행: %.6s ê°€ 없습니다." + +msgid "Available Updates" +msgstr "가능한 ì—…ë°ì´íŠ¸" + +msgid "Back" +msgstr "뒤로" + +msgid "Balance Board" +msgstr "밸런스 ë³´ë“œ" + +msgid "Basic" +msgstr "기본" + +msgid "Block IOS Reload:" +msgstr "IOS 다시ì½ê¸° 금지:" + +msgid "Boot Disc" +msgstr "ê²Œìž„ë””ìŠ¤í¬ ì‹¤í–‰" + +msgid "Boot disc" +msgstr "ë””ìŠ¤í¬ êµ¬ë™" + +msgid "Booting Wii game, please wait..." +msgstr "ê²Œìž„ì„ ë¶ˆëŸ¬ì˜¤ëŠ”ì¤‘ ìž ì‹œ 기다려 주세요." + +#, c-format +msgid "CFG base: %s" +msgstr "CFG 기본í´ë”: %s" + +msgid "Cancelled." +msgstr "취소ë˜ì—ˆìŠµë‹ˆë‹¤." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "%sí´ë”를 만들 수 없습니다." + +msgid "Cheat Codes:" +msgstr "치트 코드:" + +msgid "Cheats: " +msgstr "치트:" + +msgid "Check For Updates" +msgstr "ì—…ë°ì´íŠ¸ 확ì¸" + +msgid "Checking for themes..." +msgstr "테마를 확ì¸í•˜ê³  있습니다." + +msgid "Checking for updates..." +msgstr "ì—…ë°ì´íŠ¸ë¥¼ 확ì¸í•˜ê³  있습니다." + +msgid "Choose a sorting method" +msgstr "ì •ë ¬ë°©ë²•ì„ ì„ íƒí•˜ì„¸ìš”." + +msgid "Classic" +msgstr "í´ëž˜ì‹" + +msgid "Classic Controller" +msgstr "í´ëž˜ì‹ 콘트롤러" + +msgid "Clear Patches:" +msgstr "패치를 삭제합니다." + +#, c-format +msgid "Complete. Size: %d" +msgstr "작업종료 í¬ê¸°:%d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "설정 오류 발견" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "ì—°ê²° 오류 발견 ì—러코드: %i" + +msgid "Console" +msgstr "콘솔" + +msgid "Console Def." +msgstr "콘솔 선명ë„" + +msgid "Controller" +msgstr "콘트롤러" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "DIP 모듈 초기화 실패! (리턴값 = %d)" + +msgid "Country Fix:" +msgstr "지역코드 수정:" + +msgid "Cover" +msgstr "커버" + +msgid "Cover Image:" +msgstr "커버 ì´ë¯¸ì§€:" + +msgid "Cover Style" +msgstr "커버 스타ì¼" + +msgid "Cover~~Back" +msgstr "커버~~ë’¤" + +msgid "Cover~~Front" +msgstr "커버~~ì•ž" + +msgid "Create" +msgstr "만들기" + +msgid "Creator" +msgstr "제작ìž" + +#, c-format +msgid "Current Version: %s" +msgstr "현재버전: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"커스텀 IOS %d를 ì°¾ì„ ìˆ˜ 없습니다.\n" +"설치해 주세요." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "cIOS %d를 불러올 수 없습니다. (리턴값 = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"커스텀 IOS %dê°€ 바뀌었습니다.\n" +"다시 설치해 주세요." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "cIOS %s를 불러왔습니다." + +msgid "DISC cover" +msgstr "ë””ìŠ¤í¬ ì»¤ë²„" + +msgid "DOWN" +msgstr "하" + +msgid "Dance Pad" +msgstr "댄스 매트" + +msgid "Database update successful." +msgstr "ë°ì´í„°ë² ì´ìŠ¤ë¥¼ ì—…ë°ì´íŠ¸ 하였습니다." + +msgid "Debug" +msgstr "디버그" + +msgid "Delete Game" +msgstr "게임 ì‚­ì œ" + +msgid "Deleting" +msgstr "삭제중" + +msgid "Descending" +msgstr "내림차순" + +msgid "Developer" +msgstr "개발ìž" + +msgid "Device is not responding!" +msgstr "장치가 ì‘답하지 않습니다." + +msgid "Device:" +msgstr "장치:" + +msgid "Disc" +msgstr "디스í¬" + +msgid "Disc (Ask)" +msgstr "ë””ìŠ¤í¬ (묻기)" + +msgid "Done." +msgstr "마침" + +msgid "Download .txt" +msgstr ".txt를 다운로드" + +msgid "Download All Covers" +msgstr "모든 커버 다운로드" + +msgid "Download All Missing Covers" +msgstr "현재 부족한 커버 다운로드" + +msgid "Download Missing Covers" +msgstr "없는 커버 다운로드" + +msgid "Download Themes" +msgstr "테마 다운로드" + +msgid "Download complete." +msgstr "다운로드 했습니다." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "게임카드 #%dì— ë‹¤ìš´ë¡œë“œ 오류" + +msgid "Download titles.txt" +msgstr "title.txt 다운로드" + +msgid "Downloadable Content" +msgstr "다운로드 가능한 콘í…츠" + +msgid "Downloading ALL MISSING covers" +msgstr "현재 부족한 커버 다운로드중" + +msgid "Downloading ALL covers" +msgstr "모든 커버 다운로드중" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "%.6s ì˜ ëª¨ë“  커버 다운로드중" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "%.6s ì˜ ë¶€ì¡±í•œ 커버 다운로드중" + +msgid "Downloading Theme previews..." +msgstr "테마 미리보기 다운로드중..." + +msgid "Downloading cheats..." +msgstr "치트 다운로드중" + +msgid "Downloading database." +msgstr "ë°ì´í„°ë² ì´ìŠ¤ 다운로드중" + +msgid "Downloading titles.txt ..." +msgstr "titles.txt 다운로드중" + +#, c-format +msgid "Downloading: %s" +msgstr "다운로딩: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "다운로딩: %s를 %së¡œ" + +msgid "Downloads" +msgstr "다운로드" + +msgid "Drums" +msgstr "드럼" + +msgid "Dutch" +msgstr "네ëœëž€ë“œì–´" + +msgid "ERROR" +msgstr "오류" + +msgid "ERROR creating file" +msgstr "íŒŒì¼ ìƒì„±í•˜ê¸° 오류" + +msgid "ERROR game opt" +msgstr "게임 옵션 오류" + +msgid "ERROR reading BCA!" +msgstr "BCA 불러오기 오류" + +#, c-format +msgid "ERROR removing %s" +msgstr "%s ì‚­ì œ 오류" + +msgid "ERROR writing BCA!" +msgstr "BCA 저장하기 오류" + +msgid "ERROR!" +msgstr "오류!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "오류! (리턴값 = %d)" + +msgid "ERROR:" +msgstr "오류:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "오류: %s ì— ì ‘ê·¼í•  수 없습니다" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "오류: ì–´í”Œë¡œë” %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "오류: BCAì—는 cIOS222/223 v4ê°€ 필요합니다" + +msgid "ERROR: Cache Close" +msgstr "오류: ìºì‰¬ 닫기" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "오류: ê²Œìž„ì„ ë¶ˆëŸ¬ì˜¬ 수 없습니다. (리턴값 = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "오류: ê²Œìž„ì´ ì´ë¯¸ 설치ë˜ì–´ 있습니다." + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "오류: ì¸ì¦ì„œ 가져오기 %d" + +msgid "ERROR: Invalid Game ID" +msgstr "오류: ìž˜ëª»ëœ ê²Œìž„ ID 입니다." + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "오류: EHC모듈 불러오기 (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"오류: NTFS 쓰기가 금지ë˜ì–´ 있습니다.\n" +"ntfs_write=1ì„ ì„¤ì •í•˜ì„¸ìš”." + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "오류: íŒŒí‹°ì…˜ì„ ì°¾ì„ ìˆ˜ 없습니다. (리턴값 = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "오류: Wii 디스í¬ê°€ 아닙니다." + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "오류: 오프셋(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "오류: 파티션 열기 %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "오류: 파티션 열기(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "오류: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "오류: WBFS 설정: %d" + +msgid "ERROR: Setting SD mode" +msgstr "오류: SD 모드 설정" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "오류: USB 초기화 (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"오류: FAT 파티션ì—ì„œ ì´ ê²Œìž„ì„ í•˜ê¸°ìœ„í•´ì„ \n" +"cIOS249rev18, cIOS222v4, cIOS223v4,\n" +"cIOS224v5 ë˜ëŠ” ìƒìœ„ë²„ì „ì´ í•„ìš”í•©ë‹ˆë‹¤.\n" +"IOS249를 ì—…ë°ì´íŠ¸ 하시거나 다른 IOS를 \n" +"ì„ íƒí•´ 주세요." + +msgid "ERROR: cache: out of memory" +msgstr "오류: ìºì‰¬ 메모리 부족" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "오류: %s 만들기" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "오류: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "오류: 메모리 중복" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"오류: 다중 wbfs 파티션 지ì›ì—는\n" +"IOS222/223-mload ê°€ 필요합니다." + +msgid "ERROR: not enough free space!!" +msgstr "오류: 여유 ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤." + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "오류: ntfs íŒŒí‹°ì…˜ì´ ì œëŒ€ë¡œ í•´ì œë˜ì§€ 않았습니다." + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "오류: %.6s 불러오기" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "오류: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "오류: 단편화 설정 %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "오류: %s 쓰기 (%d)" + +msgid "EXTEND" +msgstr "확장" + +msgid "Ejecting DVD..." +msgstr "DVD 꺼내기" + +msgid "English" +msgstr "ì˜ì–´" + +msgid "English Title" +msgstr "ì˜ì–´ 제목" + +msgid "Enter Code: " +msgstr "코드 ìž…ë ¥: " + +msgid "Error GRRLIB init" +msgstr "GRRLIB 초기화 오류" + +msgid "Error Initializing Network." +msgstr "ë„¤íŠ¸ì›Œí¬ ì´ˆê¸°í™” 오류" + +msgid "Error adding disc!" +msgstr "ë””ìŠ¤í¬ ì¶”ê°€ 오류" + +msgid "Error creating directory..." +msgstr "í´ë” 만들기 오류" + +msgid "Error discarding options!" +msgstr "옵션 취소하기 오류" + +msgid "Error downloading theme preview..." +msgstr "테마 미리보기 다운로드 오류" + +msgid "Error downloading theme..." +msgstr "테마 다운로드 오류" + +msgid "Error downloading themes..." +msgstr "테마 다운로드 오류" + +msgid "Error downloading update..." +msgstr "ì—…ë°ì´íŠ¸ 다운로드 오류" + +msgid "Error downloading updates..." +msgstr "ì—…ë°ì´íŠ¸ 다운로드 오류" + +msgid "Error downloading." +msgstr "다운로드 오류" + +msgid "Error establishing connection" +msgstr "ì—°ê²° 설정 오류" + +msgid "Error extracting theme..." +msgstr "테마 압축해제 오류" + +msgid "Error opening database, update did not complete." +msgstr "ë°ì´í„°ë² ì´ìŠ¤ ì—°ê²° 실패로 ì—…ë°ì´íŠ¸ë¥¼ í•  수 없습니다." + +#, c-format +msgid "Error opening: %s" +msgstr "불러오기 오류: %s" + +#, c-format +msgid "Error playing %s" +msgstr "%s 실행 오류" + +msgid "Error reading .dol" +msgstr ".dol 불러오기 오류" + +msgid "Error reading dol header" +msgstr "dol í—¤ë” ë¶ˆëŸ¬ì˜¤ê¸° 오류" + +#, c-format +msgid "Error saving %s" +msgstr "%s 저장 오류" + +msgid "Error saving options!" +msgstr "옵션 저장 오류" + +msgid "Error saving settings!" +msgstr "설정 저장 오류" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"게임기ë¡íŒŒì¼ 저장실패\n" +"Wii 메뉴ì—ì„œ 수정하세요." + +msgid "Error: Invalid PNG image!" +msgstr "오류: ìž˜ëª»ëœ PNG ì´ë¯¸ì§€ìž…니다." + +msgid "Error: no URL." +msgstr "오류: URL ì—†ìŒ" + +msgid "Error: no data." +msgstr "오류: ë°ì´í„° ì—†ìŒ" + +msgid "Exit" +msgstr "ë내기" + +#, c-format +msgid "Extracting: %s" +msgstr "압축해제: %s" + +msgid "FAIL" +msgstr "실패" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: 할당 오류 %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "치명ì ì˜¤ë¥˜: 그리드 할당(%d)" + +#, c-format +msgid "FILL %d" +msgstr "채우기 %d" + +msgid "FLAT cover" +msgstr "ì •ë©´ 커버" + +msgid "FULL cover" +msgstr "ì „ì²´ 커버" + +msgid "Fav" +msgstr "ì¦ê²¨ì°¾ê¸°" + +msgid "Fav: Off" +msgstr "ì¦ê²¨ì°¾ê¸°: ë”" + +msgid "Fav: On" +msgstr "ì¦ê²¨ì°¾ê¸°: 켬" + +msgid "Favorite" +msgstr "ì¦ê²¨ì°¾ê¸°" + +msgid "Favorite Games" +msgstr "ì¦ê²¨í•˜ëŠ” 게임" + +msgid "Favorite:" +msgstr "ì¦ê²¨ì°¾ê¸°:" + +msgid "Favorites:" +msgstr "ì¦ê²¨ì°¾ê¸°" + +msgid "Fighting" +msgstr "격투" + +#, c-format +msgid "File not found! %s" +msgstr "파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다. %s" + +msgid "Filter" +msgstr "분류" + +msgid "Filter Games" +msgstr "게임 분류" + +msgid "Filter by Controller" +msgstr "ì½˜íŠ¸ë¡¤ëŸ¬ì— ì˜í•œ 분류" + +msgid "Filter by Genre" +msgstr "ìŸë¥´ì— ì˜í•œ 분류" + +msgid "Filter by Online Features" +msgstr "온ë¼ì¸ ê¸°ëŠ¥ì— ì˜í•œ 분류" + +msgid "Filter:" +msgstr "분류:" + +msgid "Fixing EXTEND partition..." +msgstr "확장 파티션 수정중..." + +msgid "Force NTSC" +msgstr "ê°•ì œ NTSC" + +msgid "Force PAL50" +msgstr "ê°•ì œ PAL50" + +msgid "Force PAL60" +msgstr "ê°•ì œ PAL60" + +msgid "Formatting" +msgstr "í¬ë§·ì¤‘" + +#, c-format +msgid "Found %s" +msgstr "%s 발견ë¨" + +msgid "French" +msgstr "프랑스어" + +msgid "Full" +msgstr "ì „ì²´" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "게임 기본" + +msgid "Game Options" +msgstr "게임 옵션" + +#, c-format +msgid "Game Options: %s" +msgstr "게임 옵션: %s" + +msgid "Gamecube" +msgstr "게임í브" + +msgid "Gamer Card:" +msgstr "게ì´ë¨¸ ì¹´ë“œ:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "게임카드 #%d ë³´ê³ : %.*s" + +msgid "Genre" +msgstr "ìŸë¥´" + +msgid "German" +msgstr "ë…ì¼ì–´" + +msgid "Global Options" +msgstr "ì „ì²´ 옵션" + +msgid "Guitar" +msgstr "기타" + +msgid "HQ cover" +msgstr "고화질 커버" + +msgid "HTTP Response was without a file" +msgstr "HTTP 파ì¼ì—†ëŠ” ì‘답" + +msgid "Hide" +msgstr "숨김" + +msgid "Hide Game:" +msgstr "숨김 게임:" + +msgid "Hold button B to cancel." +msgstr "B버튼 길게 눌러 취소" + +msgid "Homebrew Channel" +msgstr "홈브류 채ë„" + +msgid "Hook Type:" +msgstr "치트 타입:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID 불ì¼ì¹˜: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS 다시ì½ê¸°: 금지" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) ì„ ì‹¤íŒ¨. ì—러코드: %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"í”„ë¡œê·¸ëž¨ì´ ì •ìƒì ìœ¼ë¡œ ë™ìž‘하지\n" +"않으면 boot.dol.bak 파ì¼ì„ boot.dolë¡œ\n" +"바꿔주신 후 다시 ì‹œë„í•´ 주세요." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "%x를 ì¸ì½”딩중 LZ77ì— ë¶ˆì¼ì¹˜ ë°œìƒ " + +msgid "Info" +msgstr "ì •ë³´" + +#, c-format +msgid "Info file: %s" +msgstr "íŒŒì¼ ì •ë³´: %s" + +msgid "Initializing Network..." +msgstr "ë„¤íŠ¸ì›Œí¬ ì´ˆê¸°í™”ì¤‘..." + +msgid "Install" +msgstr "설치" + +msgid "Install Date" +msgstr "설치 날짜" + +msgid "Install Game" +msgstr "게임 설치" + +msgid "Install game" +msgstr "게임 설치" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "설치 오류 (리턴값 = %d)" + +msgid "Installing game, please wait..." +msgstr "게임 설치중, 기다려 주세요." + +msgid "Invalid .dol" +msgstr "ìž˜ëª»ëœ .dol íŒŒì¼ ìž…ë‹ˆë‹¤." + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "파티션 오류: '%s'" + +msgid "Italian" +msgstr "ì´íƒˆë¦¬ì•„ì–´" + +msgid "Japanese" +msgstr "ì¼ë³¸ì–´" + +msgid "Japanese Title" +msgstr "ì¼ë³¸ 제목" + +msgid "Keyboard" +msgstr "키보드" + +msgid "Korean" +msgstr "한국어" + +msgid "LEFT" +msgstr "좌" + +msgid "LOCKED!" +msgstr "ìž ê¹€" + +msgid "Language:" +msgstr "언어: " + +msgid "Last Play Date" +msgstr "최종 게임한 날짜" + +msgid "Launch Methods" +msgstr "실행방법" + +msgid "Load OK!" +msgstr "불러오기 완료." + +#, c-format +msgid "Loader Version: %s" +msgstr "ë¡œë” ë²„ì „: %s" + +msgid "Loading ..." +msgstr "불러오는중" + +msgid "Main" +msgstr "ë©”ì¸" + +msgid "Main Menu" +msgstr "ë©”ì¸ ë©”ë‰´" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Wiiì˜ ë°”ê¹¥ìª½ 0번 USBì—\n" +"연결하셨는지 확ì¸í•˜ì„¸ìš”." + +msgid "Manage" +msgstr "설정" + +msgid "Manage Cheats" +msgstr "치트 설정" + +msgid "Microphone" +msgstr "마ì´í¬" + +msgid "Motion+" +msgstr "모션플러스" + +msgid "Mounting device, please wait..." +msgstr "장치연결중, 기다려 주세요." + +msgid "Music" +msgstr "ìŒì•…" + +#, c-format +msgid "Music file from dir: %s" +msgstr "ìŒì•…íŒŒì¼ í´ë”: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "ìŒì•…íŒŒì¼ í¬ê¸°: %d" + +#, c-format +msgid "Music file: %s" +msgstr "ìŒì•…파ì¼: %s" + +msgid "Music: Disabled" +msgstr "ë°°ê²½ìŒì•…: 사용안함" + +msgid "Music: Enabled" +msgstr "ë°°ê²½ìŒì•…: 사용함" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "ìŒì•…: %s 파ì¼ì„ %s í´ë”ì—ì„œ 찾는중" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "ìŒì•…: ë‹¤ìŒ ìž¬ìƒ íŒŒì¼: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "ìŒì•…: %sê°œì˜ íŒŒì¼ì„ %i ì—ì„œ 찾았습니다." + +msgid "Music: musicArray contents: " +msgstr "ìŒì•…: ìŒì•…ë°°ì—´ ë‚´ìš©: " + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"SDHCì—ì„œ ê²Œìž„ì„ í•˜ê¸° 위해서는\n" +"cIOS249 rev10 ì´ìƒì´ 필요합니다." + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"cIOS249 rev14 ì´í•˜ 버전ì€\n" +"001오류 ìˆ˜ì •ì„ í•  수 없습니다." + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"USBì—ì„œ ê²Œìž„ì„ í•˜ê¸° 위해서는\n" +"cIOS249 rev9 ì´ìƒì´ 필요합니다." + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"cIOS249 rev13ì„ ì‚¬ìš©ì¤‘:\n" +"다른 ê²Œìž„ì„ í•˜ê¸° 위해서는 ë¦¬ì…‹ì„ í•˜ë˜ê°€\n" +"Wiiì˜ ì „ì›ì„ ê»ë‹¤ê°€ 켜야만 다른 게임ì„\n" +"하실 수 있습니다." + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"cIOS249 rev14를 사용중:\n" +"rev14ë²„ì „ì€ ë“€ì–¼ë ˆì´ì–´ ê²Œìž„ì„ í•  수\n" +"없습니다. 다른 ë²„ì „ì„ ì„¤ì¹˜í•˜ì‹œê¸°\n" +"ë°”ëžë‹ˆë‹¤." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"ì—…ë°ì´íŠ¸ë¥¼ ì ìš©í•˜ë ¤ë©´ 반드시\n" +"ë¡œë”를 재시작 하셔야 합니다." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"알림: 파티션 P#%d 는 확장타입ì—\n" +"WBFS 파ì¼ì‹œìŠ¤í…œì„ 설치하셨습니다. ì´ê²ƒì€\n" +"ìž˜ëª»ëœ ì„¤ì •ì´ë¯€ë¡œ %s 를 누르셔서 파티션\n" +"íƒ€ìž…ì„ í™•ìž¥ì—ì„œ ë°ì´í„°ë¡œ 바꾸세요." + +msgid "NTFS compression not supported!" +msgstr "NTFS ì••ì¶•ì´ ì§€ì›ë˜ì§€ 않습니다." + +msgid "NTFS encryption not supported!" +msgstr "NTFS 암호화가 지ì›ë˜ì§€ 않습니다." + +msgid "Network connection established." +msgstr "네트워í¬ì— ì ‘ì†í–ˆìŠµë‹ˆë‹¤." + +msgid "Network error. Can't update gamercards." +msgstr "ë„¤íŠ¸ì›Œí¬ ì˜¤ë¥˜: 게임카드를 ì—…ë°ì´íŠ¸ í•  수 없습니다." + +msgid "New Themes" +msgstr "새로운 테마" + +msgid "Nintendo DS" +msgstr "ë‹Œí…ë„ DS" + +msgid "Nintendo DS Connectivity" +msgstr "ë‹Œí…ë„ DSì™€ì˜ í†µì‹ ê¸°ëŠ¥" + +msgid "No" +msgstr "아니오" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "'%s' ì£¼ì†Œì— ë„ë©”ì¸ì´ 없습니다." + +msgid "No games found!" +msgstr "ê²Œìž„ì´ ì—†ìŠµë‹ˆë‹¤." + +msgid "No partition selected!" +msgstr "íŒŒí‹°ì…˜ì´ ì„ íƒë˜ì§€ 않았습니다." + +msgid "No themes found." +msgstr "새로운 테마가 없습니다." + +msgid "No updates found." +msgstr "ì—…ë°ì´íŠ¸ê°€ 없습니다." + +msgid "None found on disc" +msgstr "디스í¬ì— ì•„ë¬´ê²ƒë„ ì—†ìŠµë‹ˆë‹¤." + +msgid "Not Found!" +msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ" + +msgid "Number of Online Players" +msgstr "온ë¼ì¸ 플레ì´ì–´ 수" + +msgid "Number of Players" +msgstr "플레ì´ì–´ 수" + +msgid "Nunchuk" +msgstr "눈차í¬" + +msgid "OK" +msgstr "확ì¸" + +msgid "OK!" +msgstr "확ì¸" + +msgid "Ocarina (cheats):" +msgstr "오카리나(치트): " + +msgid "Ocarina Cheat Manager" +msgstr "오카리나 치트 매니저" + +msgid "Ocarina: Code Error" +msgstr "오카리나: 코드 오류" + +msgid "Ocarina: Codes found." +msgstr "오카리나: 코드 있ìŒ" + +msgid "Ocarina: No codes found" +msgstr "오카리나: 코드 ì—†ìŒ" + +msgid "Ocarina: Out Of Memory Error" +msgstr "오카리나: 메모리 오류" + +msgid "Ocarina: Searching codes..." +msgstr "오카리나: 코드 검색중" + +msgid "Ocarina: Too many codes" +msgstr "오카리나: 코드가 너무 많습니다." + +msgid "Off" +msgstr "ë„기" + +msgid "On" +msgstr "켜기" + +msgid "Online" +msgstr "온ë¼ì¸" + +msgid "Online Content" +msgstr "온ë¼ì¸ 콘í…츠" + +msgid "Online Play" +msgstr "온ë¼ì¸ 플레ì´" + +msgid "Online Players" +msgstr "온ë¼ì¸ 플레ì´ì–´" + +msgid "Online Score List" +msgstr "온ë¼ì¸ 스코어 목ë¡" + +msgid "Online Updates" +msgstr "온ë¼ì¸ ì—…ë°ì´íŠ¸" + +msgid "Open Sort" +msgstr "ì •ë ¬ 열기" + +msgid "Open Style" +msgstr "ìŠ¤íƒ€ì¼ ì—´ê¸°" + +msgid "Opening DVD disc..." +msgstr "DVD 디스í¬ë¥¼ 여는중" + +#, c-format +msgid "Opening partition: %s" +msgstr "파티션 여는중: %s" + +msgid "Options" +msgstr "옵션" + +msgid "Options discarded for this game." +msgstr "ì´ ê²Œìž„ì—ì„œ ì·¨ì†Œëœ ì˜µì…˜" + +msgid "Options saved for this game." +msgstr "ì´ ê²Œìž„ì— ì €ìž¥ëœ ì˜µì…˜" + +msgid "Out of memory" +msgstr "메모리 오류" + +msgid "Page:" +msgstr "페ì´ì§€:" + +msgid "Partition:" +msgstr "파티션:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "파티션: %s를 ì°¾ì„ ìˆ˜ 없습니다." + +msgid "Party" +msgstr "파티" + +msgid "Paused Start" +msgstr "ì¼ì‹œì •ì§€ 시작" + +msgid "Platformer" +msgstr "플랫í¼" + +msgid "Play Count" +msgstr "게임 횟수" + +msgid "Players" +msgstr "플레ì´ì–´" + +msgid "Please insert a game disc..." +msgstr "게임 디스í¬ë¥¼ 넣어주세요." + +#, c-format +msgid "Press %s button for options." +msgstr "ì˜µì…˜ì„ ì„¤ì •í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s button to %s." +msgstr "%s를 눌러 %s를 하세요." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "코드를 ì ìš©í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s button to cancel." +msgstr "취소를 하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to change device." +msgstr "장치를 변경하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to continue." +msgstr "계ì†í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "FS를 삭제하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "BCA를 ë¤í”„하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to exit." +msgstr "종료하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "확장 WBFS문제를 수정하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "WBFSíŒŒí‹°ì…˜ì„ í¬ë§·í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s button to go back." +msgstr "ë˜ëŒì•„가려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "íŒŒí‹°ì…˜ì„ ì„ íƒí•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s button to select." +msgstr "ì„ íƒì„ 하려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "코드를 건너뛰려면 %s를 누르세요." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "FAT ë™ê¸°í™”는 %s를 누르세요." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "전체화면 미리보기는 %s를 누르세요." + +#, c-format +msgid "Press %s for game options" +msgstr "게임 ì˜µì…˜ì„ ì„¤ì •í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s for global options" +msgstr "ì „ì²´ ì˜µì…˜ì„ ì„¤ì •í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s to discard options" +msgstr "ì˜µì…˜ë³€ê²½ì„ ì·¨ì†Œí•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s to download and update" +msgstr "ì—…ë°ì´íŠ¸ë¥¼ 하려면 %s를 누르세요." + +#, c-format +msgid "Press %s to download this theme" +msgstr "ì´ í…Œë§ˆ 다운로드는 %s를 누르세요." + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "다운로드는 %s ëŒì•„가기는 %s" + +#, c-format +msgid "Press %s to return" +msgstr "ëŒì•„가려면 %s를 누르세요." + +#, c-format +msgid "Press %s to save global settings" +msgstr "ì „ì²´ì˜µì…˜ì„ ì €ìž¥í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s to save options" +msgstr "ì˜µì…˜ì„ ì €ìž¥í•˜ë ¤ë©´ %s를 누르세요." + +#, c-format +msgid "Press %s to save selection" +msgstr "ì„ íƒì„ 저장하려면 %s를 누르세요." + +#, c-format +msgid "Press %s to select filter type" +msgstr "%s를 눌러 게임분류를 ì„ íƒí•˜ì„¸ìš”." + +#, c-format +msgid "Press %s to select sorting method" +msgstr "%s를 눌러 정렬방ì‹ì„ ì„ íƒí•˜ì„¸ìš”." + +#, c-format +msgid "Press %s to start game" +msgstr "게임 ì‹œìž‘ì€ %s를 누르세요." + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "ë¡œë”만 ì—…ë°ì´íŠ¸ëŠ” %s를 누르세요." + +#, c-format +msgid "Press %s/%s to select device." +msgstr "장치선íƒì€ %s/%s를 누르세요." + +msgid "Press 2 to reload IOS" +msgstr "2를 눌러 IOS를 다시 불러오세요." + +msgid "Press A to select device" +msgstr "A를 눌러 장치를 ì„ íƒí•˜ì„¸ìš”." + +msgid "Press B to exit to HBC" +msgstr "B를 누르면 홈브류 채ë„ë¡œ 갑니다." + +msgid "Press HOME to reset" +msgstr "HOMEì„ ëˆ„ë¥´ë©´ 리셋ë©ë‹ˆë‹¤." + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "좌우 버튼으로 IOS를 ì„ íƒí•˜ì„¸ìš”." + +msgid "Press any button to continue..." +msgstr "계ì†í•˜ë ¤ë©´ 아무키나 누르세요." + +msgid "Press any button to exit..." +msgstr "종료하려면 아무키나 누르세요." + +msgid "Press any button to restart..." +msgstr "다시 시작하려면 아무키나 누르세요." + +msgid "Press any button..." +msgstr "아무키나 누르세요." + +#, c-format +msgid "Press button %s to download covers." +msgstr "ì´ë¯¸ì§€ 다운로드는 %s를 누르세요." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "DVD를 꺼내려면 %s를 누르세요." + +msgid "Profile:" +msgstr "프로파ì¼:" + +#, c-format +msgid "Profile: %s" +msgstr "프로파ì¼: %s" + +msgid "Program Updates" +msgstr "프로그램 ì—…ë°ì´íŠ¸" + +msgid "Publisher" +msgstr "발매ì›" + +msgid "Puzzle" +msgstr "í¼ì¦" + +msgid "Quit" +msgstr "마침" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "ìš°" + +msgid "RPG" +msgstr "롤플레잉" + +msgid "Racing" +msgstr "ë ˆì´ì‹±" + +#, c-format +msgid "Rated %s" +msgstr "게임등급 %s " + +msgid "Rating" +msgstr "게임등급" + +msgid "Read" +msgstr "ì½ê¸°" + +msgid "Reading BCA..." +msgstr "BCA를 불러오는중" + +msgid "Reboot" +msgstr "재시작" + +msgid "Release Date" +msgstr "ë¡œë” ìˆ˜ì •ë‚ ì§œ" + +msgid "Release Notes: (short)" +msgstr "수정내역: 요약" + +msgid "Removing game, please wait..." +msgstr "ê²Œìž„ì„ ì‚­ì œì¤‘ìž…ë‹ˆë‹¤. 기다려 주세요." + +msgid "Restarting Wii..." +msgstr "Wii를 재시작중입니다." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "ë˜ëŒì•„옴 (리턴값 = %d)" + +msgid "Rhythm" +msgstr "리듬" + +msgid "Rows:" +msgstr "ì—´:" + +msgid "Running benchmark, please wait" +msgstr "벤치마í¬ë¥¼ 실행중입니다. 기다려 주세요." + +msgid "S. Chinese" +msgstr "ê°„ì²´ 중국어" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC ì¹´ë“œ" + +msgid "SUCCESS!" +msgstr "성공" + +msgid "Save .gct" +msgstr ".gct 저장" + +msgid "Save Debug" +msgstr "디버그 저장" + +msgid "Save Settings" +msgstr "설정 저장" + +msgid "Save debug.log" +msgstr "debug.log 저장" + +msgid "Saving Settings... " +msgstr "설정 저장중" + +msgid "Saving cheats..." +msgstr "치트 저장중" + +msgid "Saving gamelist.txt ... " +msgstr "gamelist.txt 저장중" + +msgid "Saving settings..." +msgstr "설정 저장중" + +msgid "Saving:" +msgstr "저장중:" + +#, c-format +msgid "Saving: %s" +msgstr "저장중: %s" + +msgid "Scroll:" +msgstr "스í¬ë¡¤:" + +msgid "Select Alternative .dol:" +msgstr "대체 .dol ì„ íƒ:" + +msgid "Select WBFS device:" +msgstr "WBFS 장치 ì„ íƒ:" + +msgid "Select a different partition" +msgstr "다른 íŒŒí‹°ì…˜ì„ ì„ íƒí•˜ì„¸ìš”." + +msgid "Select a partition" +msgstr "íŒŒí‹°ì…˜ì„ ì„ íƒí•˜ì„¸ìš”" + +msgid "Select all" +msgstr "ì „ì²´ì„ íƒ" + +msgid "Select the game you want to boot" +msgstr "실행할 ê²Œìž„ì„ ì„ íƒí•˜ì„¸ìš”." + +msgid "Selected Game" +msgstr "ì„ íƒëœ 게임" + +msgid "Settings" +msgstr "설정" + +msgid "Shooter" +msgstr "슈팅" + +msgid "Show All" +msgstr "ëª¨ë‘ ë³´ê¸°" + +msgid "Show cIOS info" +msgstr "cIOS ì •ë³´ 보기" + +msgid "Shutdown" +msgstr "ë„기" + +msgid "Side:" +msgstr "옆:" + +msgid "Simulation" +msgstr "시뮬레ì´ì…˜" + +msgid "Size(GB) Type Mount Used" +msgstr "(GB)í¬ê¸° 타입 마운트 사용중" + +#, c-format +msgid "Size: %d bytes" +msgstr "í¬ê¸°: %dë°”ì´íŠ¸" + +msgid "Sort" +msgstr "ì •ë ¬" + +msgid "Sort Games" +msgstr "게임 ì •ë ¬" + +msgid "Sort Order:" +msgstr "ì •ë ¬ 순서:" + +msgid "Sort Type:" +msgstr "ì •ë ¬ ë°©ì‹:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "ì •ë ¬: %s-%s" + +msgid "Spanish" +msgstr "스페ì¸ì–´" + +msgid "Sports" +msgstr "스í¬ì¸ " + +msgid "Start" +msgstr "시작" + +msgid "Start Game" +msgstr "게임 시작" + +msgid "Start this game?" +msgstr "ì´ ê²Œìž„ì„ ì‹œìž‘í• ê¹Œìš”?" + +msgid "Stopping DVD..." +msgstr "DVD를 멈추는중..." + +msgid "Strategy" +msgstr "ì „ëžµ" + +msgid "Style" +msgstr "스타ì¼" + +msgid "Style:" +msgstr "스타ì¼:" + +msgid "Sync FAT free space info?" +msgstr "FATì˜ ë¹ˆê³µê°„ 정보를 ë™ê¸°í™” 할까요?" + +msgid "Synchronizing, please wait." +msgstr "ë™ê¸°í™” 하는중 기다려 주세요." + +msgid "System" +msgstr "시스템" + +msgid "System Def." +msgstr "시스템 선명ë„" + +msgid "T. Chinese" +msgstr "표준 중국어" + +msgid "Theme" +msgstr "테마" + +msgid "Theme Info" +msgstr "테마 ì •ë³´" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"테마가 너무 í½ë‹ˆë‹¤.\n" +"í”„ë¡œê·¸ëž¨ì„ ë‹¤ì‹œ 시작해 주세요." + +msgid "Theme:" +msgstr "테마:" + +#, c-format +msgid "Theme: %s" +msgstr "테마: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "테마 제공: wii.spiffy360.com" + +msgid "Themes to download" +msgstr "다운로드할 테마" + +msgid "Themes with updates" +msgstr "ì—…ë°ì´íŠ¸í•  테마" + +msgid "Title" +msgstr "타ì´í‹€" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "치트 과다 사용 (%d)" + +msgid "Too many code lines!" +msgstr "코드가 너무 많습니다." + +#, c-format +msgid "Too many fragments! %d" +msgstr "ë¶„í• ì´ ë„ˆë¬´ 많습니다. %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "ì ‘ì†ì¤‘ (url#%d)" + +msgid "UNUSED" +msgstr "사용안ë¨" + +msgid "UP" +msgstr "위" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "'%s'ì£¼ì†Œì— 'http://'ê°€ 빠졌습니다." + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "'%s'ì£¼ì†Œì— ê²½ë¡œê°€ 없습니다." + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko를 찾았습니다. ë””ë²„ê¹…ì´ ì‹œìž‘ë©ë‹ˆë‹¤." + +msgid "USB Mass Storage Device" +msgstr "USB 대용량 저장장치" + +msgid "Unknown" +msgstr "알수없ìŒ" + +msgid "Unknown syntax!" +msgstr "알수없는 구문" + +msgid "Unplayed" +msgstr "실행안ë¨" + +msgid "Unplayed Games" +msgstr "실행안한 게임" + +msgid "Update WiiTDB Game Database" +msgstr "WiiTDB ë°ì´í„°ë² ì´ìŠ¤ ì—…ë°ì´íŠ¸" + +msgid "Update themes" +msgstr "테마 ì—…ë°ì´íŠ¸" + +msgid "Update titles.txt" +msgstr "title.txt ì—…ë°ì´íŠ¸" + +msgid "Updates" +msgstr "ì—…ë°ì´íŠ¸" + +msgid "Updating database." +msgstr "ë°ì´í„°ë² ì´ìŠ¤ ì—…ë°ì´íŠ¸ì¤‘" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "사용: %.1fGB 빈공간: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "ì €ìž¥ëœ ëŒ€ì²´ .dol 사용:" + +msgid "Version" +msgstr "버전" + +msgid "Video Patch:" +msgstr "비디오 패치:" + +msgid "Video:" +msgstr "비디오:" + +msgid "View" +msgstr "보기" + +msgid "Vitality Sensor" +msgstr "ì²´ë ¥ 센서" + +msgid "WARNING:" +msgstr "경고:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB 사용 ì „ì²´: %.1fGB" + +msgid "Wheel" +msgstr "휠" + +msgid "Wii Speak" +msgstr "Wii 스피í¬" + +msgid "WiiTDB Game Database" +msgstr "WiiTDB 게임 ë°ì´í„°ë² ì´ìŠ¤" + +msgid "Wiimote" +msgstr "위모트" + +msgid "Write Playlog:" +msgstr "ê²Œìž„ê¸°ë¡ ì“°ê¸°:" + +#, c-format +msgid "Writing to %s" +msgstr "%sì— ì“°ëŠ”ì¤‘" + +#, c-format +msgid "Writing: %s" +msgstr "기ë¡: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "ìž˜ëª»ëœ í¬ê¸°: %d (%d)" + +msgid "Yes" +msgstr "예" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"장치를 다시 ì ‘ì†í•˜ì‹œê±°ë‚˜\n" +"조금 ë” ê¸°ë‹¤ë¦¬ì‹œê¸° ë°”ëžë‹ˆë‹¤." + +msgid "Zapper" +msgstr "재í¼" + +msgid "[ CHANGED ]" +msgstr "[ ë³€ê²½ë¨ ]" + +msgid "[ FOUND ]" +msgstr "[ ì°¾ìŒ ]" + +msgid "[ SAVED ]" +msgstr "[ ì €ìž¥ë¨ ]" + +msgid "[HOME]" +msgstr "[홈]" + +msgid "[No HQ]" +msgstr "[고화질 ì—†ìŒ]" + +msgid "[USED]" +msgstr "[사용]" + +msgid "[default]" +msgstr "[기본]" + +msgid "[saved]" +msgstr "[저장ë¨]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "ìž˜ëª»ëœ wbfs 파ì¼: %s" + +msgid "calculating space, please wait..." +msgstr "공간 계산중 기다려주세요." + +msgid "delete" +msgstr "ì‚­ì œ" + +#, c-format +msgid "dest: %p - %p" +msgstr "목ì ì§€: %p - %p" + +msgid "discard" +msgstr "버리기" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "%s ë„ë©”ì¸ì„ ì°¾ì„ ìˆ˜ 없습니다." + +#, c-format +msgid "error reading: %s (%d)" +msgstr "ì½ê¸° 오류: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "%d ì¸ìš©" + +msgid "for 1 player" +msgstr "1 ì¸ìš©" + +msgid "format" +msgstr "í¬ë§·" + +msgid "free" +msgstr "빈공간" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "순차 ì½ê¸° ì†ë„: %.2f mb/s" + +msgid "linear..." +msgstr "순차" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "ìŒì•…파ì¼ì´ 너무 í½ë‹ˆë‹¤. (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr ".mp3 나 .mod 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다." + +msgid "no file" +msgstr "íŒŒì¼ ì—†ìŒ" + +msgid "none" +msgstr "ì—†ìŒ" + +msgid "or format a WBFS partition." +msgstr "ë˜ëŠ” WBFS íŒŒí‹°ì…˜ì„ í¬ë§·í•˜ì„¸ìš”." + +msgid "page" +msgstr "페ì´ì§€" + +msgid "parse error" +msgstr "ë¶„ì„ ì˜¤ë¥˜" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "ëžœë¤ ì½ê¸° ì†ë„: %.2f mb/s" + +msgid "random..." +msgstr "ëžœë¤" + +msgid "reset" +msgstr "리셋" + +msgid "revert" +msgstr "ë˜ëŒë¦¬ê¸°" + +msgid "save" +msgstr "저장" + +#, c-format +msgid "split error: %s" +msgstr "분할 오류: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "Wii 디스í¬ë¥¼ 불러올 수 없습니다." + +#, c-format +msgid "used: %p - %p" +msgstr "사용중: %p - %p" + +#~ msgid "Front" +#~ msgstr "ì•ž" + +#~ msgid "Loading previous game list..." +#~ msgstr "ì´ì „ 게임목ë¡ì„ 불러오는중" + +#~ msgid "Remove Game" +#~ msgstr "게임 ì‚­ì œ" diff --git a/Languages/Makefile b/Languages/Makefile new file mode 100644 index 0000000..bacbb1a --- /dev/null +++ b/Languages/Makefile @@ -0,0 +1,14 @@ + +VERSION = $(shell sed -n '/^VERSION/{s/^.*=[\t ]*//p;Q}' ../Makefile) + +all: zip + +clean: + rm -f *miss.lang *.zip lang?*.pot + +zip: clean + echo $(VERSION) + ./msg_merge.sh lang.pot + ./msg_miss.sh + zip lang$(VERSION).zip *.lang lang.pot + diff --git a/Languages/NL.lang b/Languages/NL.lang new file mode 100644 index 0000000..4054be4 --- /dev/null +++ b/Languages/NL.lang @@ -0,0 +1,2352 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-01-31 14:18-0500\n" +"Last-Translator: wiiuser2 \n" +"Language-Team: DUTCH \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB vrij van %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% van %.2fGB (%c) Tijd: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB gekopieerd in %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d beschikbaar" + +#, c-format +msgid "%d more notes" +msgstr "%d meer opmerkingen" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Gesplitst: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, even geduld..." + +#, c-format +msgid "%s: Favorites" +msgstr "%s: Favorieten" + +#, c-format +msgid "%s: GUI " +msgstr "%s: Grafisch " + +#, c-format +msgid "%s: Options " +msgstr "%s: Opties " + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d seconden timeout)" + +msgid "(This can take a couple of minutes)" +msgstr "(Dit kan een paar minuten duren)" + +msgid ".dol too small" +msgstr ".dol te klein" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "1st-Person Shooter" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "3D hoesje" + +msgid "3rd-person Shooter" +msgstr "3rd-person Shooter" + +msgid "< ASC >" +msgstr "< Opl >" + +msgid "< DESC >" +msgstr "< Afl >" + +msgid "< DOWNLOAD >" +msgstr "< DOWNLOAD >" + +msgid "About" +msgstr "Over" + +msgid "Action" +msgstr "Actie" + +msgid "Additional config:" +msgstr "Extra instellingen:" + +msgid "Admin Lock:" +msgstr "Vergrendel Admin:" + +msgid "Admin Unlock" +msgstr "Ontgrendel Admin" + +msgid "Adult" +msgstr "18+" + +msgid "Adventure" +msgstr "Avontuur" + +msgid "All" +msgstr "Alle" + +msgid "All Channels" +msgstr "Alle Kanalen" + +msgid "All Games" +msgstr "Alle spellen" + +msgid "All themes up to date." +msgstr "Alle thema's zijn bijgewerkt" + +msgid "Alt Button Cfg:" +msgstr "Alt knoppen Cfg:" + +msgid "Alt dol:" +msgstr "Alt dol:" + +msgid "Alternative .dol:" +msgstr "Alternatieve .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix:" + +#, c-format +msgid "App. Path: %s" +msgstr "App locatie: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "Ben je zeker dat je deze partitie wilt %s?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Ben je zeker dat je deze\n" +"partitie wilt herstellen?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Weet je zeker dat je dit spel wilt\n" +"verwijderen?" + +msgid "Ascending" +msgstr "Oplopend" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Autom. starten van spel: %.6s niet gevonden!" + +msgid "Available Updates" +msgstr "Beschikbare updates" + +msgid "Back" +msgstr "Terug" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Baseball" +msgstr "Honkbal" + +msgid "Basic" +msgstr "Basic" + +msgid "Basketball" +msgstr "Basketbal" + +msgid "Bike Racing" +msgstr "Fiets racing" + +msgid "Billiards" +msgstr "Biljarten" + +msgid "Block IOS Reload:" +msgstr "Blokkeer IOS Herladen:" + +msgid "Board Game" +msgstr "Bordspel" + +msgid "Boot Disc" +msgstr "Start Disc" + +msgid "Boot disc" +msgstr "Start disc" + +msgid "Booting game, please wait..." +msgstr "Start spel, even geduld...." + +msgid "Bowling" +msgstr "Bowling" + +msgid "Boxing" +msgstr "Boxen" + +msgid "Business Sim" +msgstr "Zakelijke Sim" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG basis: %s" + +msgid "Camera" +msgstr "Camera" + +msgid "Can not dump GC savegames.\n" +msgstr "Kan geen GC savegames dumpen.\n" + +msgid "Can't install Wii games!" +msgstr "Kan geen Wii games installeren" + +msgid "Cancelled." +msgstr "Geannuleerd" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Kan dir niet aanmaken: %s" + +msgid "Cards" +msgstr "Kaarten" + +msgid "Channel" +msgstr "Kanalen" + +msgid "Cheat Codes:" +msgstr "Cheat Codes:" + +msgid "Cheats: " +msgstr "Cheats: " + +msgid "Check For Updates" +msgstr "Controleren op updates" + +msgid "Checking for themes..." +msgstr "Controleren nieuwe thema's.." + +msgid "Checking for updates..." +msgstr "Controleren op updates..." + +msgid "Chess" +msgstr "Schaken" + +msgid "Choose a sorting method" +msgstr "Kies een sorteermethode" + +msgid "Classic" +msgstr "Klassiek" + +msgid "Classic Controller" +msgstr "Klassieke Controller" + +msgid "Clear" +msgstr "Wissen" + +msgid "Clear Patches:" +msgstr "Verwijder patches:" + +msgid "Coaching" +msgstr "Coaching" + +msgid "Compare Type" +msgstr "Vergelijk Type" + +msgid "Compilation" +msgstr "Compilatie" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Totale grootte: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Instellingsfout, MAX_DNS_ENTRIES bereikt terwijl de lijst leeg is" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Connectiefout van net_read() Foutcode: %i" + +msgid "Console" +msgstr "Console" + +msgid "Construction Sim" +msgstr "Bouw simulatie" + +msgid "Contains" +msgstr "Bevat" + +msgid "Controller" +msgstr "Controller" + +msgid "Cooking" +msgstr "Koken" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Kon de DIP module niet initialiseren! (ret = %d)" + +msgid "Country Fix:" +msgstr "Land fix:" + +msgid "Cover" +msgstr "Hoes" + +msgid "Cover Image:" +msgstr "Hoesje:" + +msgid "Cover Style" +msgstr "Stijl Hoesje" + +msgid "Covers Available" +msgstr "Beschikbare Hoesjes" + +msgid "Cover~~Back" +msgstr "Hoesje~~Achterkant" + +msgid "Cover~~Front" +msgstr "Hoesje~~Voorkant" + +msgid "Create" +msgstr "Maak" + +msgid "Creator" +msgstr "Ontwerper" + +msgid "Cricket" +msgstr "Cricket" + +#, c-format +msgid "Current Version: %s" +msgstr "Huidige versie: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Custom IOS %d niet gevonden!\n" +"Eerst installeren a.u.b." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Custom IOS %d kan niet geladen worden! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d is een dummy!\n" +"Opnieuw installeren a.u.b." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "custom IOS %s geladen" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "Disc afbeelding" + +msgid "DOWN" +msgstr "OMLAAG" + +msgid "Dance" +msgstr "Dansen" + +msgid "Dance Pad" +msgstr "Dansmat" + +msgid "Darts" +msgstr "Darts" + +msgid "Database update successful." +msgstr "Database update succesvol" + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Spel verwijderen" + +msgid "Deleting" +msgstr "Bezig met verwijderen" + +msgid "Descending" +msgstr "Aflopend" + +msgid "Developer" +msgstr "Ontwikkelaar" + +msgid "Device is not responding!" +msgstr "Apparaat reageert niet!" + +msgid "Device:" +msgstr "Apparaat:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolution accepteert alleen schone dumps!\n" + +msgid "Devolution:" +msgstr "Devolution:" + +msgid "Disc" +msgstr "Disk" + +msgid "Disc (Ask)" +msgstr "Disk (Vragen)" + +msgid "Done." +msgstr "Klaar." + +msgid "Download .txt" +msgstr ".txt downloaden" + +msgid "Download All Covers" +msgstr "Alle hoesjes downloaden" + +msgid "Download All Missing Covers" +msgstr "Alle missende hoesjes downloaden" + +msgid "Download Missing Covers" +msgstr "Ontbrekende hoesjes downloaden" + +msgid "Download Plugins" +msgstr "Plugins downloaden" + +msgid "Download Themes" +msgstr "Themes downloaden" + +msgid "Download complete." +msgstr "Downloaden voltooid" + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Download fout op spelerkaart #%d" + +msgid "Download game information" +msgstr "Download spel info" + +msgid "Downloadable Content" +msgstr "Downloadbare content" + +msgid "Downloading ALL MISSING covers" +msgstr "Downloading alle ontbrekende hoesjes" + +msgid "Downloading ALL covers" +msgstr "Downloading alle hoesjes" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Downloading alle hoesjes voor %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Downloading ontbrekende hoesjes voor %.6s" + +msgid "Downloading Theme previews..." +msgstr "Downloading thema voorbeeld" + +msgid "Downloading cheats..." +msgstr "Cheats downloaden..." + +msgid "Downloading database." +msgstr "Database downloaden." + +msgid "Downloading devolution." +msgstr "Downloading devolution." + +msgid "Downloading mighty plugin." +msgstr "Downloading mighty plugin." + +msgid "Downloading neek2o plugin." +msgstr "Downloading neek2o plugin." + +msgid "Downloading titles.txt ..." +msgstr "Titles.txt downloaden ..." + +msgid "Downloading translation file." +msgstr "Vertaling downloaden" + +msgid "Downloading unifont." +msgstr "Downloaden unifont" + +#, c-format +msgid "Downloading: %s" +msgstr "Bezig met downloaden: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Downloading: %s als %s" + +msgid "Downloads" +msgstr "Downloads" + +msgid "Drawing" +msgstr "Tekenen" + +msgid "Drums" +msgstr "Drums" + +msgid "Dump savegame" +msgstr "Dump savegame" + +msgid "Duplicate ID3" +msgstr "Toon dubbele spellen" + +msgid "Dutch" +msgstr "Nederlands" + +msgid "ERROR" +msgstr "FOUT" + +msgid "ERROR creating file" +msgstr "FOUT bij het aanmaken van het bestand" + +msgid "ERROR game opt" +msgstr "FOUT in spelopties" + +msgid "ERROR reading BCA!" +msgstr "FOUT bij lezen van BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "FOUT bij verwijderen van %s" + +msgid "ERROR writing BCA!" +msgstr "FOUT bij schrijven van BCA!" + +msgid "ERROR!" +msgstr "FOUT!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "FOUT! (ret = %d)" + +msgid "ERROR:" +msgstr "FOUT:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "FOUT: %s is niet bereikbaar" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "FOUT: Applader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "FOUT: cIOS222/223 v4 benodigd voor BCA" + +msgid "ERROR: Cache Close" +msgstr "FOUT: Cache close" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "FOUT: Kon het spel niet openen! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "FOUT: Spel is al geïnstalleerd!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "FOUT: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "FOUT: Ongeldig SpelID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "FOUT: Laden van EHC module! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"FOUT: naar NTFS schrijven uitgeschakeld!\n" +"(zet ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "FOUT: Geen partities gevonden! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "FOUT: Geen Wii Disc!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "FOUT: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "FOUT: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "FOUT: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "FOUT: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "FOUT: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "FOUT: SD modus wijzigen" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "FOUT: USB init! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "FOUT: WBFS niet gekoppeld" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"FOUT: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 of hoger is nodig\n" +"om spellen vanaf een FAT partitie te kunnen starten!\n" +"Upgrade IOS249 of kies een andere IOS." + +msgid "ERROR: cache: out of memory" +msgstr "FOUT: cache: te weinig geheugen" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "FOUT bij creeeren: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "FOUT: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "FOUT: geheugen overlapt!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"FOUT: meerdere wbfs partities worden\n" +"enkel ondersteund door IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "FOUT: Niet genoeg vrije ruimte!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "FOUT: ntfs was niet netjes ontkoppeld" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "FOUT: openen van %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "FOUT: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "FOUT: opzetten van fragmenten %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "FOUT: the schijf of partitie %s/ is niet verbonden" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "FOUT: schrijven van %s (%d)" + +msgid "EXTEND" +msgstr "EXTEND" + +msgid "Educational" +msgstr "Educatief" + +msgid "Ejecting DVD..." +msgstr "DVD uitwerpen..." + +msgid "English" +msgstr "Engels" + +msgid "English Title" +msgstr "Engelse Titel" + +msgid "Enter Code: " +msgstr "Geef de code in: " + +msgid "Error GRRLIB init" +msgstr "Fout bij GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Fout bij netwerk init." + +msgid "Error adding disc!" +msgstr "Fout bij spel toevoegen" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Fout bij alloceren buffer: %d" + +msgid "Error creating directory..." +msgstr "Fout bij aanmaken folder..." + +msgid "Error discarding options!" +msgstr "Fout bij opties verwijderen!" + +msgid "Error downloading theme preview..." +msgstr "Fout bij downloaden thema voorbeeld..." + +msgid "Error downloading theme..." +msgstr "Fout bij downloaden thema..." + +msgid "Error downloading themes..." +msgstr "Fout bij downloaden themas" + +msgid "Error downloading update..." +msgstr "Fout bij downloaden van update..." + +msgid "Error downloading updates..." +msgstr "Fout bij downloaden van updates..." + +msgid "Error downloading." +msgstr "Fout bij downloaden." + +msgid "Error establishing connection" +msgstr "Fout bij opzetten van connectie" + +msgid "Error extracting theme..." +msgstr "Fout bij uitpakken thema..." + +msgid "Error opening database, update did not complete." +msgstr "Fout bij het openen van de database, update niet voltooid." + +#, c-format +msgid "Error opening: %s" +msgstr "Fout bij openen: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Fout bij spelen: %s" + +msgid "Error reading .dol" +msgstr "Fout bij laden van .dol" + +msgid "Error reading dol header" +msgstr "Fout bij lezen van dol header" + +#, c-format +msgid "Error saving %s" +msgstr "Fout bij opslaan van %s" + +msgid "Error saving options!" +msgstr "Fout bij opslaan van opties!" + +msgid "Error saving settings!" +msgstr "Fout bij opslaan van instellingen!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Kan het Wii prikbord niet bijwerken.\n" +"Werkt wel na start via het Wii Menu." + +msgid "Error: Invalid PNG image!" +msgstr "Fout: Ongeldige PNG afb." + +msgid "Error: no URL." +msgstr "Fout: geen URL." + +msgid "Error: no data." +msgstr "Fout: geen data." + +msgid "Exercise" +msgstr "Training" + +msgid "Exit" +msgstr "Verlaten" + +#, c-format +msgid "Extracting: %s" +msgstr "Uitpakken: %s" + +msgid "FAIL" +msgstr "MISLUKT" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOC FOUT %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "VUL %d" + +msgid "FLAT cover" +msgstr "2D hoesje" + +msgid "FULL cover" +msgstr "HD hoesje" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Uit" + +msgid "Fav: On" +msgstr "Fav: Aan" + +msgid "Favorite" +msgstr "Favoriet" + +msgid "Favorite Games" +msgstr "Favoriete spellen" + +msgid "Favorite:" +msgstr "Favoriet:" + +msgid "Favorites:" +msgstr "Favorieten:" + +msgid "Fighting" +msgstr "Vechten" + +#, c-format +msgid "File not found! %s" +msgstr "Bestand niet gevonden! %s" + +msgid "Filter" +msgstr "Filter" + +msgid "Filter Games" +msgstr "Filter spellen" + +msgid "Filter by Controller" +msgstr "Filter per controller" + +msgid "Filter by Game Type" +msgstr "Filter per spel type" + +msgid "Filter by Genre" +msgstr "Filter per genre" + +msgid "Filter by Online Features" +msgstr "Filter per Online functies" + +msgid "Filter:" +msgstr "Filter:" + +msgid "Fishing" +msgstr "Vissen" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "EXTEND partitie herstellen..." + +msgid "Flight Sim" +msgstr "Vluchtsimulator" + +msgid "Football" +msgstr "Voetbal" + +msgid "Force Devolution:" +msgstr "Forceer Devolution:" + +msgid "Force NTSC" +msgstr "Forceer NTSC" + +msgid "Force NTSC 480p" +msgstr "Forceer NTSC 480p" + +msgid "Force PAL" +msgstr "Forceer PAL" + +msgid "Force PAL 480p" +msgstr "Forceer PAL 480p" + +msgid "Force PAL50" +msgstr "Forceer PAL50" + +msgid "Force PAL60" +msgstr "Forceer PAL60" + +msgid "Formatting" +msgstr "Bezig met formatteren" + +#, c-format +msgid "Found %s" +msgstr "Gevonden %s" + +msgid "French" +msgstr "Frans" + +msgid "Full" +msgstr "Volledig" + +msgid "Futuristic Racing" +msgstr "Racing" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Spel standaard" + +msgid "Game ID" +msgstr "Spel ID" + +msgid "Game Options" +msgstr "Spel opties" + +#, c-format +msgid "Game Options: %s" +msgstr "Spel opties: %s" + +msgid "Game Type" +msgstr "Spel Type" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Speler kaart:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Spelerkaart #%d meldt: %.*s" + +msgid "Genre" +msgstr "Soort" + +msgid "German" +msgstr "Duits" + +msgid "Global Options" +msgstr "Globale opties" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Gitaar" + +msgid "HQ cover" +msgstr "HD cover" + +msgid "HTTP Response was without a file" +msgstr "HTTP antwoord zonder een bestand" + +msgid "Health" +msgstr "Gezondheid" + +msgid "Hidden Object" +msgstr "Verborgen Object" + +msgid "Hide" +msgstr "Verbergen" + +msgid "Hide Game:" +msgstr "Spel verbergen:" + +msgid "Hockey" +msgstr "Hockey" + +msgid "Hold button B to cancel." +msgstr "Houd B ingedrukt om te annuleren." + +msgid "Home Menu" +msgstr "Startmenu" + +msgid "Homebrew Channel" +msgstr "Homebrew Kanaal" + +msgid "Hook Type:" +msgstr "Hook Type" + +msgid "Hunting" +msgstr "Jagen" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID verschil: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS herladen: Geblokkeerd" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_open(%s) fout met code %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Als Cfg niet goed start\n" +"kopieer boot.dol.bak over\n" +"boot.dol en probeer opnieuw." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Inconsistentie in LZ77 encodering %x" + +msgid "Infinity Base" +msgstr "Infinity basis" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Bestandsinfo: %s" + +msgid "Initializing Network..." +msgstr "Netwerk initialiseren..." + +msgid "Install" +msgstr "Installeen" + +msgid "Install Date" +msgstr "Installatiedatum" + +msgid "Install Game" +msgstr "Installeer spel" + +msgid "Install game" +msgstr "Installeer spel" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Installatiefout! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Spel installeren, even geduld..." + +msgid "Invalid .dol" +msgstr "Ongeldige .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Ongeldige partitie: '%s'" + +msgid "Italian" +msgstr "Italiaans" + +msgid "Japanese" +msgstr "Japans" + +msgid "Japanese Title" +msgstr "Japanse Titel" + +msgid "Jump" +msgstr "Springen" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Karten" + +msgid "Keyboard" +msgstr "Toetsenbord" + +msgid "Korean" +msgstr "Koreaans" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "LINKS" + +msgid "LOCKED!" +msgstr "OP SLOT!" + +msgid "Language:" +msgstr "Taal:" + +msgid "Last Play Date" +msgstr "Laatst gespeeld" + +msgid "Launch Methods" +msgstr "Opstart methode" + +msgid "Life Simulation" +msgstr "Simulatie" + +msgid "Load OK!" +msgstr "Laden OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Loader versie: %s" + +msgid "Loading ..." +msgstr "Laden..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Laden..%s\n" + +msgid "Main" +msgstr "Hoofd" + +msgid "Main Menu" +msgstr "Hoofdmenu" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Gebruik USB poort 0!\n" +"(Dit is de buitenste poort)" + +msgid "Manage" +msgstr "Beheer" + +msgid "Manage Cheats" +msgstr "Beheer Cheats" + +msgid "Management Sim" +msgstr "Beheer Sim" + +msgid "Martial Arts" +msgstr "Vechtsport" + +msgid "Microphone" +msgstr "Microfoon" + +msgid "Mighty Plugin" +msgstr "Mighty Plugin" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Motor Racen" + +msgid "Mounting device, please wait..." +msgstr "Apparaat laden, even geduld..." + +msgid "Music" +msgstr "Muziek" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Muziek uit locatie: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Muziek bestandsgrootte: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Muziekbestand: %s" + +msgid "Music: Disabled" +msgstr "Muziek: Uit" + +msgid "Music: Enabled" +msgstr "Muziek: Aan" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Muziek: Kijken naar %s bestanden in: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Muziek" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Muziek: Aantal van %s bestanden gevonden: %i" + +msgid "Music: musicArray contents: " +msgstr "Muziek: inhoud musicArray: " + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"NAND Emu Path:\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "NAND Emu:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"Opmerking: cIOS249 voor rev10:\n" +"Spellen laden van SDHC niet ondersteund!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"Opmerking: cIOS249 voor rev14\n" +"Kans op fout #001!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"Opmerking: cIOS249 voor rev9:\n" +"Spellen laden van USB niet ondersteund!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"Opmerking: Je gebruikt cIOS249 rev23:\n" +"Om een spel te beëindigen moet je de\n" +"Wii afsluiten voor je een nieuw spel\n" +"start anders bevriest de lader!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"Opmerking: Je gebruikt cIOS249 rev14:\n" +"Er zijn problemen met dual-layer\n" +"spellen. Het is aangeraden om een\n" +"andere cIOS te gebruiken of een andere\n" +"versie om dit spel te installeren/gebruiken." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"Opmerking: De lader moet herstarten voor\n" +"de wijzigingen in werking treden" + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"Opmerking: De lader moet misschien herstarten\n" +"voor de wijzigingen in werking treden" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"Opmerking: partitie P#%d is van type EXTEND\n" +"maar bevat een wbfs systeem. Dit is een\n" +"ongeldige setup. Druk %s om het partitie-\n" +"type te wijzigen van EXTEND naar data." + +msgid "NTFS compression not supported!" +msgstr "NTFS compressie wordt niet ondersteund" + +msgid "NTFS encryption not supported!" +msgstr "NTFS versleuteling wordt niet ondersteund!" + +msgid "NTSC-J patch:" +msgstr "NTSC-J patch:" + +msgid "Neek2o Plugin" +msgstr "Neek2o Plugin" + +msgid "Network connection established." +msgstr "Netwerk verbinding geslaagd." + +msgid "Network error. Can't update gamercards." +msgstr "Fout in netwerkverbinding. Kan spelerkaarten niet bijwerken." + +msgid "New Themes" +msgstr "Nieuwe Thema's" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS Connectiviteit" + +msgid "No" +msgstr "Nee" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Geen domein in URL '%s'" + +msgid "No games found!" +msgstr "Geen spellen gevonden" + +msgid "No partition selected!" +msgstr "Geen partitie gekozen!" + +msgid "No themes found." +msgstr "Geen Thema's gevonden" + +msgid "No updates found." +msgstr "Geen updates gevonden" + +msgid "NoDisc:" +msgstr "Geen Disc:" + +msgid "None found on disc" +msgstr "Geen gevonden op disc" + +msgid "Not Found boot.dol!" +msgstr "Boot.dol niet gevonden!" + +msgid "Not Found!" +msgstr "Niet gevonden!" + +msgid "Number of Online Players" +msgstr "Aantal online spelers" + +msgid "Number of Players" +msgstr "Aantal spelers" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (cheats):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina Cheat Manager" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Code fout" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Codes gevonden" + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Geen codes gevonden" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: te weinig geheugen" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Codes zoeken..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: teveel codes" + +msgid "Off" +msgstr "Uit" + +msgid "Off-Road Racing" +msgstr "Terrein racing" + +msgid "On" +msgstr "Aan" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Online Content" + +msgid "Online Play" +msgstr "Online Spelen" + +msgid "Online Players" +msgstr "Online Spelers" + +msgid "Online Score List" +msgstr "Online Scorebord" + +msgid "Online Updates" +msgstr "Online Updates" + +msgid "Open Sort" +msgstr "Open Sortering" + +msgid "Open Style" +msgstr "Open Stijl" + +msgid "Opening DVD disc..." +msgstr "DVD disc openen..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Openen partitie: %s" + +msgid "Options" +msgstr "Opties" + +msgid "Options discarded for this game." +msgstr "Opties voor dit spel verwijderd." + +msgid "Options saved for this game." +msgstr "Opties voor dit spel opgeslagen" + +msgid "Out of memory" +msgstr "Geen geheugen meer" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK:" + +msgid "Page:" +msgstr "Blz:" + +msgid "Partial" +msgstr "Gedeeltelijk" + +msgid "Partition:" +msgstr "Partitie:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partitie: %s niet gevonden!" + +msgid "Party" +msgstr "Feest" + +msgid "Paused Start" +msgstr "Start gepauzeerd" + +msgid "Petanque" +msgstr "Jeu de Boules" + +msgid "Pinball" +msgstr "Flipperen" + +msgid "Platformer" +msgstr "Platform" + +msgid "Play Count" +msgstr "Spel teller" + +msgid "Players" +msgstr "Spelers" + +msgid "Please insert a game disc..." +msgstr "Plaats een DVD in de Wii..." + +msgid "Plugin:" +msgstr "PLugin:" + +msgid "Poker" +msgstr "Pokeren" + +msgid "Portal of Power" +msgstr "Portal of Power" + +#, c-format +msgid "Press %s button for options." +msgstr "Druk %s voor opties" + +#, c-format +msgid "Press %s button to %s." +msgstr "Druk %s om %s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Druk %s om de codes te activeren." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Druk %s om te annuleren" + +#, c-format +msgid "Press %s button to change device." +msgstr "Druk %s om van apparaat te wisselen." + +#, c-format +msgid "Press %s button to continue." +msgstr "Druk %s om verder te gaan." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Druk %s om het FS te verwijderen." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Druk %s om BCA te dumpen." + +#, c-format +msgid "Press %s button to exit." +msgstr "Druk %s om af te sluiten." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Druk %s om EXTEND/WBFS te herstellen." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Druk %s om naar WBFS te formatteren." + +#, c-format +msgid "Press %s button to go back." +msgstr "Druk %s om terug te gaan." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Druk %s om een partitie te kiezen" + +#, c-format +msgid "Press %s button to select." +msgstr "Druk %s om te selecteren." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Druk %s om codes over te slaan" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Druk %s knop voor synchronisatie FAT" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Druk op %s voor fullscreen preview" + +#, c-format +msgid "Press %s for game options" +msgstr "Druk %s voor spelopties" + +#, c-format +msgid "Press %s for global options" +msgstr "Druk %s voor globale opties" + +#, c-format +msgid "Press %s to discard options" +msgstr "Druk %s om de opties te verwijderen" + +#, c-format +msgid "Press %s to download and update" +msgstr "Druk %s om te downloaden en updaten" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Druk op %s om dit thema te downloaden" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Druk %s om te downloaden, %s om terug te gaan" + +#, c-format +msgid "Press %s to return" +msgstr "Druk %s om terug te gaan" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Druk %s om de globale opties op te slaan" + +#, c-format +msgid "Press %s to save options" +msgstr "Druk %s om de opties op te slaan" + +#, c-format +msgid "Press %s to save selection" +msgstr "Druk %s om de selectie op te slaan" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Druk %s om het filtertype te kiezen" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Druk %s om de sorteermethode te kiezen" + +#, c-format +msgid "Press %s to start game" +msgstr "Druk %s om het spel te starten" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Druk %s om te updaten zonder meta.xml" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "Druk LINKS/RECHTS om een apparaat te kiezen." + +msgid "Press 1 to skip WBFS mounting" +msgstr "Druk op 1 om WBFS koppeling over te slaan" + +msgid "Press 2 to reload IOS" +msgstr "Druk op 2 om IOS te herladen" + +msgid "Press A to continue without config.txt" +msgstr "Druk op A om door te gaan zonder config.txt" + +msgid "Press A to select device" +msgstr "Druk op A om apparaat te selekteren" + +msgid "Press B to exit to HBC" +msgstr "Druk op B om naar HBC terug te keren" + +msgid "Press HOME to reset" +msgstr "Druk op home voor reset" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Druk op links/rechts om een IOS te kiezen" + +msgid "Press any button to continue..." +msgstr "Druk om verder te gaan..." + +msgid "Press any button to exit..." +msgstr "Druk om af te sluiten..." + +msgid "Press any button to restart..." +msgstr "Druk om te herstarten..." + +msgid "Press any button.\n" +msgstr "Druk op een knop.\n" + +msgid "Press any button..." +msgstr "Druk op een knop..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Druk knop %s om hoesjes te downloaden." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Druk %s om de DVD uit te werpen." + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Profiel:" + +#, c-format +msgid "Profile: %s" +msgstr "Profiel: %s" + +msgid "Program Updates" +msgstr "Programma Updates" + +msgid "Publisher" +msgstr "Uitgever" + +msgid "Puzzle" +msgstr "Puzzel" + +msgid "Quit" +msgstr "Stoppen" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "RECHTS" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Racen" + +msgid "Rail Shooter" +msgstr "Rail Shooter" + +#, c-format +msgid "Rated %s" +msgstr "Leeftijd %s" + +msgid "Rating" +msgstr "Waardering" + +msgid "Read" +msgstr "Lees" + +msgid "Reading BCA..." +msgstr "BCA lezen..." + +msgid "Reboot" +msgstr "Opnieuw opstarten" + +msgid "Region" +msgstr "Regio" + +msgid "Release Date" +msgstr "Uitgebracht op" + +msgid "Release Notes: (short)" +msgstr "Versie beschrijving: (kort)" + +msgid "Removing game, please wait..." +msgstr "Spel verwijderen, even geduld..." + +msgid "Restarting Wii..." +msgstr "Wii herstarten..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Gestopt! (ret = %d)" + +msgid "Rhythm" +msgstr "Ritme" + +msgid "Rows:" +msgstr "Rijen:" + +msgid "Rugby" +msgstr "Rugby" + +msgid "Running benchmark, please wait" +msgstr "Benchmark is bezig, even geduld" + +msgid "S. Chinese" +msgstr "Chinees (simpel)" + +msgid "SD/SDHC Card" +msgstr "SD(HC) Kaart" + +msgid "SUCCESS!" +msgstr "SUCCES!" + +msgid "Save .gct" +msgstr ".gct opslaan" + +msgid "Save Debug" +msgstr "Debug opslaan" + +msgid "Save Settings" +msgstr "Settings opslaan" + +msgid "Save debug.log" +msgstr "Opslaan debug.log" + +msgid "Savegame not found.\n" +msgstr "Savegame niet gevonden.\n" + +msgid "Savegame:" +msgstr "Savegame:" + +msgid "Saving Settings... " +msgstr "Opties opslaan..." + +msgid "Saving cheats..." +msgstr "Cheats opslaan..." + +msgid "Saving gamelist.txt ... " +msgstr "gamelist.txt opslaan ..." + +msgid "Saving settings..." +msgstr "Instellingen opslaan..." + +msgid "Saving:" +msgstr "Opslaan:" + +#, c-format +msgid "Saving: %s" +msgstr "Opslaan: %s" + +msgid "Screenshot:" +msgstr "Screenshot:" + +msgid "Scroll:" +msgstr "Bladeren:" + +msgid "Search" +msgstr "Zoek" + +msgid "Search for:" +msgstr "Zoek naar:" + +msgid "Select Alternative .dol:" +msgstr "Kies een Alt .dol:" + +msgid "Select WBFS device:" +msgstr "Kies WBFS apparaat:" + +msgid "Select a different partition" +msgstr "Kies een ander partitie" + +msgid "Select a partition" +msgstr "Kies een partitie" + +msgid "Select all" +msgstr "Kies alles" + +msgid "Select device:" +msgstr "Kies apparaat:" + +msgid "Select the game you want to boot" +msgstr "Kies een spel om te laden" + +msgid "Selected Game" +msgstr "Gekozen spel" + +msgid "Settings" +msgstr "Instellingen" + +msgid "Shooter" +msgstr "Schietspel" + +msgid "Show All" +msgstr "Toon alles" + +msgid "Show cIOS info" +msgstr "Show cIOS info" + +msgid "Shutdown" +msgstr "Uitzetten" + +msgid "Side:" +msgstr "Kant:" + +msgid "Sim Racing" +msgstr "Race simulatie" + +msgid "Simulation" +msgstr "Simulatie" + +msgid "Size(GB) Type Mount Used" +msgstr "Grootte(GB) Type MOunt gebruikt" + +#, c-format +msgid "Size: %d bytes" +msgstr "Grootte: %d bytes" + +msgid "Skateboard" +msgstr "Skateboard" + +msgid "Skateboarding" +msgstr "Skateboarden" + +msgid "Skiing" +msgstr "Skien" + +msgid "Snowboarding" +msgstr "Snowboarden" + +msgid "Soccer" +msgstr "Voetbal" + +msgid "Sort" +msgstr "Sorteer" + +msgid "Sort Games" +msgstr "Spellen sorteren" + +msgid "Sort Order:" +msgstr "Sorteervolgorde:" + +msgid "Sort Type:" +msgstr "Sorteer type:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Sortering: %s-%s" + +msgid "Spanish" +msgstr "Spaans" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Start" + +msgid "Start Game" +msgstr "Start spel" + +msgid "Start this game?" +msgstr "Spel starten?" + +msgid "Stealth Action" +msgstr "Stealth aktie" + +msgid "Stopping DVD..." +msgstr "Stoppen DVD..." + +msgid "Strategy" +msgstr "Strategie" + +msgid "Style" +msgstr "Stijl" + +msgid "Style:" +msgstr "Stijl:" + +msgid "Surfing" +msgstr "Surfen" + +msgid "Survival Horror" +msgstr "Griezelen" + +msgid "Sync FAT free space info?" +msgstr "Sync FAT info vrije schijfruimte?" + +msgid "Synchronizing, please wait." +msgstr "Synchroniseren, even geduld..." + +msgid "Synopsis" +msgstr "Samenvatting" + +msgid "Synopsis Length" +msgstr "Lengte samenvatting" + +msgid "System" +msgstr "Systeem" + +msgid "System Def." +msgstr "Systeem def." + +msgid "T. Chinese" +msgstr "Chinees (trad.)" + +msgid "Table Tennis" +msgstr "Tafeltennis" + +msgid "Tennis" +msgstr "Tennis" + +msgid "Theme" +msgstr "Thema" + +msgid "Theme Info" +msgstr "Thema info" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Thema is wellicht te groot.\n" +"Start Cfg opnieuw op.\n" + +msgid "Theme:" +msgstr "Thema:" + +#, c-format +msgid "Theme: %s" +msgstr "Thema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Thema's ter beschikking gesteld door wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Te downloaden thema's" + +msgid "Themes with updates" +msgstr "Thema's met beschikbare updates" + +msgid "Title" +msgstr "Titel" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Te veel cheats! (%d)" + +msgid "Too many code lines!" +msgstr "Te veel lijnen code!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Te veel fragmenten! %d" + +msgid "Total Body Tracking" +msgstr "Total Body Tracking" + +msgid "Train Simulation" +msgstr "Trein simulatie" + +msgid "Trivia" +msgstr "Trivia" + +msgid "Truck Racing" +msgstr "Truck racen" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Proberen (url#%d) ..." + +msgid "Turntable" +msgstr "Draaitafel" + +msgid "UNUSED" +msgstr "ONGEBRUIKT" + +msgid "UP" +msgstr "OMHOOG" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' start niet met 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' heeft geen PATH deel" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko gevonden. Debuggen geactiveerd." + +msgid "USB Mass Storage Device" +msgstr "USB apparaat voor massa opslag" + +msgid "Unknown" +msgstr "Onbekend" + +msgid "Unknown syntax!" +msgstr "Onbekende syntax!" + +msgid "Unplayed" +msgstr "Niet gespeeld" + +msgid "Unplayed Games" +msgstr "Niet gespeelde spellen" + +msgid "Update themes" +msgstr "Bijwerken thema's" + +msgid "Updates" +msgstr "Updates" + +msgid "Updating database." +msgstr "Database updaten..." + +msgid "Updating devolution" +msgstr "Bijwerken devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Gebruikt: %.1fGB Vrij: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Opgeslagen Alt dol wordt gebruikt:" + +msgid "VC-Arcade" +msgstr "VC-Arcade" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-N64" + +msgid "VC-NES" +msgstr "VC-NES" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC-SNES" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "Versie" + +msgid "Video Patch:" +msgstr "Video Patch:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Bekijk" + +msgid "Virtual Pet" +msgstr "Virtueel huisdier" + +msgid "Vitality Sensor" +msgstr "Levenssensor" + +msgid "Volleyball" +msgstr "Volleybal" + +msgid "WARNING:" +msgstr "WAARSCHUWING:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB vrij van %.1fGB" + +msgid "Watercraft Racing" +msgstr "Water racen" + +msgid "Wheel" +msgstr "Wiel" + +msgid "Wide Screen:" +msgstr "Breedbeeld" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Wii kanaal" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Worstelen" + +msgid "Write Playlog:" +msgstr "Loggen op prikbord:" + +#, c-format +msgid "Writing to %s" +msgstr "Schrijven naar %s" + +#, c-format +msgid "Writing: %s" +msgstr "Schrijven: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Verkeerde grootte: %d (%d)" + +msgid "Yes" +msgstr "Ja" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Je kan proberen het apparaat af te koppelen\n" +"en weer aan te sluiten,\n" +"of iets langer te wachten" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ GEWIJZIGD ]" + +msgid "[ FOUND ]" +msgstr "[ GEVONDEN ]" + +msgid "[ SAVED ]" +msgstr "[ OPGESLAGEN ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[Geen HD]" + +msgid "[USED]" +msgstr "[GEBRUIKT]" + +msgid "[default]" +msgstr "[standaard]" + +msgid "[saved]" +msgstr "[opgeslagen]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "slecht wbfs bestand: %s" + +msgid "calculating space, please wait..." +msgstr "ruimte bereken, even geduld..." + +msgid "delete" +msgstr "verwijder" + +#, c-format +msgid "dest: %p - %p" +msgstr "doel: %p - %p" + +msgid "discard" +msgstr "weggooien" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "domein %s kon niet worden gevonden" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "fout bij lezen: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "voor %d spelers" + +msgid "for 1 player" +msgstr "voor 1 speler" + +msgid "format" +msgstr "formaat" + +msgid "free" +msgstr "vrij" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "lineaire leessnelheid: %.2f mb/s" + +msgid "linear..." +msgstr "lineair..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin grootte: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "muziekbestand te groot (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 of music.mod niet gevonden!" + +msgid "no file" +msgstr "geen bestand" + +msgid "none" +msgstr "geen" + +msgid "or format a WBFS partition." +msgstr "of formatteer een WBFS partitie" + +msgid "page" +msgstr "pag" + +msgid "parse error" +msgstr "parse fout" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "willek. leessnelheid: %.2f mb/s" + +msgid "random..." +msgstr "willekeurig..." + +msgid "reset" +msgstr "reset" + +msgid "revert" +msgstr "herstel" + +msgid "save" +msgstr "opslaan" + +#, c-format +msgid "split error: %s" +msgstr "splits fout: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "kan wii disc niet openen" + +#, c-format +msgid "used: %p - %p" +msgstr "gebruikt: %p - %p" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Spel wordt gestart: even geduld..." + +#~ msgid "Console Def." +#~ msgstr "Console Def." + +#~ msgid "Loading previous game list..." +#~ msgstr "Laden vorige spellenlijst..." + +#~ msgid "Remove Game" +#~ msgstr "Spel verwijderen" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "WiiTDB Spellendatabase updaten" + +#~ msgid "Update titles.txt" +#~ msgstr "titles.txt updaten" diff --git a/Languages/NO.lang b/Languages/NO.lang new file mode 100644 index 0000000..434da29 --- /dev/null +++ b/Languages/NO.lang @@ -0,0 +1,1880 @@ +# The awesome Norwegian translation! +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR, Stigmatic , 2011. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2011-04-12 16:10+GMT1\n" +"Last-Translator: Stigmatic \n" +"Language-Team: Norwegian \n" +"Language: Norsk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB ledig, av %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% av %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB kopiert p?d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d tilgjengelig" + +#, c-format +msgid "%d more notes" +msgstr "%d flere notater" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Oppdeling: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, vennligst vent..." + +#, c-format +msgid "(%d online)" +msgstr "(%d pÃ¥ nett)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d sekunder tidsavbrudd)" + +msgid "(This can take a couple of minutes)" +msgstr "(Dette kan ta noen minutter)" + +msgid ".dol too small" +msgstr ".dol for liten" + +msgid "3D cover" +msgstr "3D cover" + +msgid "< ASC >" +msgstr "< ASC >" + +msgid "< DESC >" +msgstr "< BESKR >" + +msgid "< DOWNLOAD >" +msgstr "< LAST NED >" + +msgid "About" +msgstr "Om" + +msgid "Action" +msgstr "Action" + +msgid "Additional config:" +msgstr "Tillegs innstillinger:" + +msgid "Admin Lock:" +msgstr "Admin lÃ¥s:" + +msgid "Admin Unlock" +msgstr "Admin lÃ¥s opp" + +msgid "Adult" +msgstr "Voksen" + +msgid "Adventure" +msgstr "Eventyr" + +msgid "All" +msgstr "Alle" + +msgid "All Games" +msgstr "Alle spill" + +msgid "All themes up to date." +msgstr "Alle temaer er oppdatert." + +msgid "Alt dol:" +msgstr "Alt dol:" + +msgid "Alternative .dol:" +msgstr "Alternativ .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 fiks:" + +#, c-format +msgid "App. Path: %s" +msgstr "App. path: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Er du sikker pÃ¥ at du vil %s\n" +"denne partisjonen?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Er du sikker pÃ¥ at du vil FIKSE\n" +"denne partisjonen?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Er du sikker pÃ¥ at du vil fjerne\n" +"dette spillet?" + +msgid "Ascending" +msgstr "Stigende" + +msgid "Auto" +msgstr "" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto-start spill: %.6s ikke funnet!" + +msgid "Available Updates" +msgstr "Tilgjengelige oppdateringer" + +msgid "Back" +msgstr "Tilbake" + +msgid "Balance Board" +msgstr "Balansebrett" + +msgid "Basic" +msgstr "Enkel" + +msgid "Block IOS Reload:" +msgstr "Stopp IOS omstart:" + +msgid "Boot Disc" +msgstr "Start disc" + +msgid "Boot disc" +msgstr "Start disc" + +msgid "Booting Wii game, please wait..." +msgstr "Starter Wii spill, vennligst vent..." + +#, c-format +msgid "CFG base: %s" +msgstr "CFG base: %s" + +msgid "Cancelled." +msgstr "Avbrutt." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Kan ikke lage: %s" + +msgid "Cheat Codes:" +msgstr "Juksekoder:" + +msgid "Cheats: " +msgstr "Juks: " + +msgid "Check For Updates" +msgstr "Se etter oppdateringer" + +msgid "Checking for themes..." +msgstr "Ser etter temaer..." + +msgid "Checking for updates..." +msgstr "Ser etter oppdateringer..." + +msgid "Choose a sorting method" +msgstr "Velg en sorterings metode" + +msgid "Classic" +msgstr "Klassisk" + +msgid "Classic Controller" +msgstr "Klassisk kontroller" + +msgid "Clear Patches:" +msgstr "Tøm patcher:" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Fullført. Str: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Konfigurasjons feil, MAX_DNS_ENTRIES nÃ¥dd mens listen er tom" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Koblingsfeil fra net_read() Feilkode: %i" + +msgid "Console" +msgstr "Konsoll" + +msgid "Console Def." +msgstr "Konsoll standard" + +msgid "Controller" +msgstr "Kontroll" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Kunne ikke initialisere DIP modul! (ret = %d)" + +msgid "Country Fix:" +msgstr "PAL til NTSC fiks:" + +msgid "Cover" +msgstr "Cover" + +msgid "Cover Image:" +msgstr "Cover bilde:" + +msgid "Cover Style" +msgstr "Cover stil" + +msgid "Cover~~Back" +msgstr "Cover~~bak" + +msgid "Cover~~Front" +msgstr "Cover~~forran" + +msgid "Create" +msgstr "Lag" + +msgid "Creator" +msgstr "Forfatter" + +#, c-format +msgid "Current Version: %s" +msgstr "NÃ¥verende versjon: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Custom IOS %d ble ikke funnet!\n" +"Vennligst installér den." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "Custom IOS %d kunne ikke lastes! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d er en stump!\n" +"Vennligst installér pÃ¥ nytt." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custom IOS %s lastet OK" + +msgid "DISC cover" +msgstr "DISC bilde" + +msgid "DOWN" +msgstr "NED" + +msgid "Dance Pad" +msgstr "Danse matte" + +msgid "Database update successful." +msgstr "Database oppdatert vellykket" + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Slett spill" + +msgid "Deleting" +msgstr "Sletter" + +msgid "Descending" +msgstr "Synkende" + +msgid "Developer" +msgstr "Utvikler" + +msgid "Device is not responding!" +msgstr "Enheten svarer ikke!" + +msgid "Device:" +msgstr "Enhet:" + +msgid "Disc" +msgstr "Disc" + +msgid "Disc (Ask)" +msgstr "Disc (Spørr)" + +msgid "Done." +msgstr "Ferdig." + +msgid "Download .txt" +msgstr "Last ned .txt" + +msgid "Download All Covers" +msgstr "Last ned alle cover" + +msgid "Download All Missing Covers" +msgstr "Last ned alle savnede cover" + +msgid "Download Missing Covers" +msgstr "Last ned SAVNEDE cover" + +msgid "Download Themes" +msgstr "Last ned temaer" + +msgid "Download complete." +msgstr "Nedlasting ferdig." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Nedlastings feil for spillerkort #%d." + +msgid "Download titles.txt" +msgstr "Last ned titles.txt (titler)" + +msgid "Downloadable Content" +msgstr "Nedlastbart innhold" + +msgid "Downloading ALL MISSING covers" +msgstr "Last ned ALLE SAVNEDE cover" + +msgid "Downloading ALL covers" +msgstr "Last ned ALLE cover" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Last ned ALLE cover for %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Last ned SAVNEDE cover for %.6s" + +msgid "Downloading Theme previews..." +msgstr "Last ned tema forhÃ¥ndsvisning..." + +msgid "Downloading cheats..." +msgstr "Last ned juks..." + +msgid "Downloading database." +msgstr "Laster ned database." + +msgid "Downloading titles.txt ..." +msgstr "Laster ned titles.txt ..." + +#, c-format +msgid "Downloading: %s" +msgstr "Laster ned: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Laster ned: %s som %s" + +msgid "Downloads" +msgstr "Nedlastinger" + +msgid "Drums" +msgstr "Trommer" + +msgid "Dutch" +msgstr "Nederlandsk" + +msgid "ERROR" +msgstr "FEIL" + +msgid "ERROR creating file" +msgstr "FEIL ved opretting av fil" + +msgid "ERROR game opt" +msgstr "FEIL spill inst" + +msgid "ERROR reading BCA!" +msgstr "FEIL ved lesing av BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "FEIL ved fjerning av %s" + +msgid "ERROR writing BCA!" +msgstr "FEIL ved skriving av BCA!" + +msgid "ERROR!" +msgstr "FEIL!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "FEIL! (ret = %d)" + +msgid "ERROR:" +msgstr "FEIL:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "FEIL: %s er ikke tilgjengelig" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "FEIL: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "FEIL: cIOS222/223 v4 kreves for BCA" + +msgid "ERROR: Cache Close" +msgstr "FEIL: Buffer lukket" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "FEIL: Kunne ikke Ã¥pne spill! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "FEIL: Spill er allerede innstallert!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "FEIL: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "FEIL: Ugyldig spill ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "FEIL: Lasting EHC modul! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERROR: NTFS skriving deaktivert!\n" +"(sets ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "FEIL: Ingen partisjon funnet! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "FEIL: Ikke et Wii spill!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "FEIL: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "FEIL: Ã…penPartisjon %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "FEIL: Ã…penPartisjon(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "FEIL: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "FEIL: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "FEIL: Angir SD modus" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "FEIL: USB init! (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"FEIL: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 eller nyere kreves\n" +"for Ã¥ starte spill fra FAT partisjon!\n" +"Oppgrader IOS249 eller velg en annen IOS." + +msgid "ERROR: cache: out of memory" +msgstr "FEIL: buffer: tomt for minne" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "FEIL: oppretting av: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "FEIL: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "FEIL: minne overlapp!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"FEIL: flere wbfs partisjoner\n" +"kun støttet med IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "FEIL: ikke nok ledig plass!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "FEIL: ntfs ble ikke riktig koblet ut" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "FEIL: ved Ã¥pning av %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "FEIL: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "FEIL: ved oppsetting av fragmenter %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "FEIL: ved skriving %s (%d)." + +msgid "EXTEND" +msgstr "FORLENG" + +msgid "Ejecting DVD..." +msgstr "Løser ut DVD..." + +msgid "English" +msgstr "Engelsk" + +msgid "English Title" +msgstr "Engelsk tittel" + +msgid "Enter Code: " +msgstr "Skriv inn kode: " + +msgid "Error GRRLIB init" +msgstr "Feil GRRLIB init" + +msgid "Error Initializing Network." +msgstr "Feil ved Ã¥ initialisere nettverk." + +msgid "Error adding disc!" +msgstr "Feil ved tillegging av disc" + +msgid "Error creating directory..." +msgstr "Feil ved oppretting av sti..." + +msgid "Error discarding options!" +msgstr "Feil ved bortkast av innstillinger" + +msgid "Error downloading theme preview..." +msgstr "Feil ved nedlasting av temaforhÃ¥ndsvisning..." + +msgid "Error downloading theme..." +msgstr "Feil ved nedlasting av tema..." + +msgid "Error downloading themes..." +msgstr "Feil ved nedlasting av temaer..." + +msgid "Error downloading update..." +msgstr "Feil ved nedlasting av oppdatering..." + +msgid "Error downloading updates..." +msgstr "Feil ved nedlasting av oppdateringer..." + +msgid "Error downloading." +msgstr "Feil ved nedlasting." + +msgid "Error establishing connection" +msgstr "Feil ved oppretting av kobling" + +msgid "Error extracting theme..." +msgstr "Feil ved utpakking av tema..." + +msgid "Error opening database, update did not complete." +msgstr "Feil ved Ã¥pning av database, oppdatering ufullført." + +#, c-format +msgid "Error opening: %s" +msgstr "Feil ved Ã¥pning: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Feil ved avspillin av %s" + +msgid "Error reading .dol" +msgstr "Feil ved lesing av .dol" + +msgid "Error reading dol header" +msgstr "Feil ved lesing av dol header" + +#, c-format +msgid "Error saving %s" +msgstr "Feil ved lagring av %s" + +msgid "Error saving options!" +msgstr "Feil ved lagring av alternativer!" + +msgid "Error saving settings!" +msgstr "Feil ved lagring av innstillinger!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Feil ved lagring av spillerlogg fil.\n" +"Start fra Wii menyen for Ã¥ fikse." + +msgid "Error: Invalid PNG image!" +msgstr "Feil: Ugyldig PNG bilde!" + +msgid "Error: no URL." +msgstr "Feil: ingen URL." + +msgid "Error: no data." +msgstr "Feil: ingen data." + +msgid "Exit" +msgstr "Avslutt" + +#, c-format +msgid "Extracting: %s" +msgstr "Pakker ut: %s" + +msgid "FAIL" +msgstr "FEILET" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ALLOK FEIL %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "FYLL %d" + +msgid "FLAT cover" +msgstr "FLATT cover" + +msgid "FULL cover" +msgstr "FULLT cover" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Av" + +msgid "Fav: On" +msgstr "Fav: PÃ¥" + +msgid "Favorite" +msgstr "Favoritt" + +msgid "Favorite Games" +msgstr "Favoritt spill" + +msgid "Favorite:" +msgstr "Favoritt:" + +msgid "Favorites:" +msgstr "Favoritter:" + +msgid "Fighting" +msgstr "SlÃ¥ssing" + +#, c-format +msgid "File not found! %s" +msgstr "Fil ikke funnet! %s" + +msgid "Filter" +msgstr "Filtrer" + +msgid "Filter Games" +msgstr "Filtrer spill" + +msgid "Filter by Controller" +msgstr "Filtrer etter kontroller" + +msgid "Filter by Genre" +msgstr "Filtrer etter sjanger" + +msgid "Filter by Online Features" +msgstr "Filtrer etter " + +msgid "Filter:" +msgstr "Filtrer:" + +msgid "Fixing EXTEND partition..." +msgstr "Fikser EXTEND partisjon..." + +msgid "Force NTSC" +msgstr "Tving NTSC" + +msgid "Force PAL50" +msgstr "Tving PAL50hz" + +msgid "Force PAL60" +msgstr "Tving PAL60hz" + +msgid "Formatting" +msgstr "Formattering" + +#, c-format +msgid "Found %s" +msgstr "Fant %s" + +msgid "French" +msgstr "Fransk" + +msgid "Full" +msgstr "Full" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Spill standard" + +msgid "Game Options" +msgstr "Spill alternativer" + +#, c-format +msgid "Game Options: %s" +msgstr "Spill alternativer: %s" + +msgid "Gamecube" +msgstr "GameCube" + +msgid "Gamer Card:" +msgstr "Spillerkort:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Spillerkort #%d raporterte: %.*s" + +msgid "Genre" +msgstr "Sjanger" + +msgid "German" +msgstr "Tysk" + +msgid "Global Options" +msgstr "Globale alternativer" + +msgid "Guitar" +msgstr "Gitar" + +msgid "HQ cover" +msgstr "HK cover" + +msgid "HTTP Response was without a file" +msgstr "HTTP svar var uten en fil" + +msgid "Hide" +msgstr "Skjul" + +msgid "Hide Game:" +msgstr "Skjul spill:" + +msgid "Hold button B to cancel." +msgstr "Hold B for Ã¥ avbryte" + +msgid "Homebrew Channel" +msgstr "Homebrew kanalen" + +msgid "Hook Type:" +msgstr "Krok type:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID samsvarer ikke: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS gjenlasting: Blokkert" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) failet med kode %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Om Cfg ikke starter riktig\n" +"neste gang, kopier boot.dol.bak over\n" +"boot.dol og prøv igjen." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Uoverenstemmelse i LZ77 koding %x" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Info fil: %s" + +msgid "Initializing Network..." +msgstr "Initialiserer nettverk..." + +msgid "Install" +msgstr "Installér" + +msgid "Install Date" +msgstr "Installérings dato" + +msgid "Install Game" +msgstr "Installér spill" + +msgid "Install game" +msgstr "Installér spill" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Installérings FEIL! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Installérer spill, vennligst vent..." + +msgid "Invalid .dol" +msgstr "Ugyldig .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Ugyldig partisjon: '%s'" + +msgid "Italian" +msgstr "Italiensk" + +msgid "Japanese" +msgstr "Japansk" + +msgid "Japanese Title" +msgstr "Japansk tittel" + +msgid "Keyboard" +msgstr "Tastatur" + +msgid "Korean" +msgstr "Koreansk" + +msgid "LEFT" +msgstr "VENSTRE" + +msgid "LOCKED!" +msgstr "LÃ…ST!" + +msgid "Language:" +msgstr "SprÃ¥k:" + +msgid "Last Play Date" +msgstr "Sist startet" + +msgid "Launch Methods" +msgstr "Start metoder" + +msgid "Load OK!" +msgstr "Lasting OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Laster versjon: %s" + +msgid "Loading ..." +msgstr "Laster ..." + +msgid "Main" +msgstr "Hoved" + +msgid "Main Menu" +msgstr "Hoved meny" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Vær sikker pÃ¥ at USB port 0 blir brukt!\n" +"(Den nærmest kanten)" + +msgid "Manage" +msgstr "Administrer" + +msgid "Manage Cheats" +msgstr "Administrer juks" + +msgid "Microphone" +msgstr "Mikrofon" + +msgid "Motion+" +msgstr "MotionPlus" + +msgid "Mounting device, please wait..." +msgstr "Monterer enhet, vennligst vent..." + +msgid "Music" +msgstr "Musikk" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Musikk fil fra kat: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Musikk filstørrelse: #d" + +#, c-format +msgid "Music file: %s" +msgstr "Musikk fil: %s" + +msgid "Music: Disabled" +msgstr "Musikk: Av" + +msgid "Music: Enabled" +msgstr "Musikk: PÃ¥" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Musikkk: Ser etter %s filer i: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Musikk: Neste fil indeks Ã¥ spille: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Musikk: Antall %s filer funnet: %i" + +msgid "Music: musicArray contents: " +msgstr "Musikk: musicArray innhold: " + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTAT: CIOS249 før rev10:\n" +"Lasting av spill fra SDHC ikke støttet!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTAT: CIOS249 før rev14:\n" +"Mulig feil #001 blir ikke hÃ¥ndtert!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTAT: CIOS249 før rev9:\n" +"Lasting av spill fra USB ikke støttet!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTAT: Du bruker CIOS249 rev13:\n" +"For Ã¥ avslutte spillet mÃ¥ du starte pÃ¥\n" +"nytt eller slÃ¥ av Wiien før du starter\n" +"et nytt spill, ellers vil det krasje her!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTAT: Du bruker CIOS249 rev14:\n" +"Den har kjente problemere med 'dual-layer'\n" +"spill. Det er høyst anbefalt at du\n" +"bruker en annen cIOS er version for\n" +"installasjon/spilling av dette spillet." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTAT: loader omstart er pÃ¥krevd\n" +"for at oppdateringen skal bli brukt." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTAT: Partisjon P#%d er type EXTEND men\n" +"inneholder WBFS filsystem. Dette er et\n" +"ugyldig oppsett. Trykk %s for Ã¥ endre\n" +"partisjons type fra EXTEND til data." + +msgid "NTFS compression not supported!" +msgstr "NTFS komprimering ikke støttet!" + +msgid "NTFS encryption not supported!" +msgstr "NTFS kryptering ikke støttet!" + +msgid "Network connection established." +msgstr "Nettverkskobling etablert." + +msgid "Network error. Can't update gamercards." +msgstr "Nettverks feil. Kan ikke oppdatere spillerkort." + +msgid "New Themes" +msgstr "Nye temaer" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS Connectivity" + +msgid "No" +msgstr "Nei" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Ingen domene del i URL '%s'" + +msgid "No games found!" +msgstr "Ingen spill funnet!" + +msgid "No partition selected!" +msgstr "Ingen partisjon valgt!" + +msgid "No themes found." +msgstr "Ingen temaer funnet." + +msgid "No updates found." +msgstr "Ingen oppdateringer funent" + +msgid "None found on disc" +msgstr "Ingen funnet pÃ¥ disc" + +msgid "Not Found!" +msgstr "Ikke funnet!" + +msgid "Number of Online Players" +msgstr "Antall spillere pÃ¥ nett" + +msgid "Number of Players" +msgstr "Antall spillere" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (juks):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina jukse manager" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Kode feil" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Koder funnet" + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Ingen koder funnet" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Tomt for minne feil" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Søker koder..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: For mange koder" + +msgid "Off" +msgstr "Nei" + +msgid "On" +msgstr "Ja" + +msgid "Online" +msgstr "PÃ¥ nett" + +msgid "Online Content" +msgstr "Innhold pÃ¥ nett" + +msgid "Online Play" +msgstr "Spill pÃ¥ nett" + +msgid "Online Players" +msgstr "Spillere pÃ¥ nett" + +msgid "Online Score List" +msgstr "Poengliste pÃ¥ nett" + +msgid "Online Updates" +msgstr "Oppdateringer pÃ¥ nett" + +msgid "Open Sort" +msgstr "Ã…pne sortering" + +msgid "Open Style" +msgstr "Ã…pner stil" + +msgid "Opening DVD disc..." +msgstr "Ã…pner DVD disc..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Ã…pner partisjon: %s" + +msgid "Options" +msgstr "Innstillinger" + +msgid "Options discarded for this game." +msgstr "Innstillinger forkastet for spillet." + +msgid "Options saved for this game." +msgstr "Innstillinger lagret for spillet." + +msgid "Out of memory" +msgstr "Tomt for minne" + +msgid "Page:" +msgstr "Side:" + +msgid "Partition:" +msgstr "Partisjon:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partisjon: %s ikke funnet!" + +msgid "Party" +msgstr "Fest" + +msgid "Paused Start" +msgstr "Start er pauset" + +msgid "Platformer" +msgstr "Plattformspill" + +msgid "Play Count" +msgstr "Ganger spillt" + +msgid "Players" +msgstr "Spillere" + +msgid "Please insert a game disc..." +msgstr "Vennligst sett inn en spilldisc..." + +#, c-format +msgid "Press %s button for options." +msgstr "Trykk %s for alternativer." + +#, c-format +msgid "Press %s button to %s." +msgstr "Trykk %s for Ã¥ %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Trykk %s for Ã¥ bruke koder" + +#, c-format +msgid "Press %s button to cancel." +msgstr "Trykk %s for Ã¥ avbryte." + +#, c-format +msgid "Press %s button to change device." +msgstr "Trykk %s for Ã¥ endre enhet." + +#, c-format +msgid "Press %s button to continue." +msgstr "Trykk %s for Ã¥ fortsette" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Trykk %s for Ã¥ slette FilSyst.." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Trykk %s for Ã¥ vise BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Trykk %s for Ã¥ avslutte." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "Trykk %s for Ã¥ fikse EXTEND/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Trykk %s for Ã¥ formatere WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Trykk %s for Ã¥ gÃ¥ tilbake." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Trykk %s for Ã¥ velge en partisjon." + +#, c-format +msgid "Press %s button to select." +msgstr "Trykk %s for Ã¥ velge." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Trykk %s for Ã¥ hoppe over koder" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Trykk %s for Ã¥ synkronisere FAT" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Trykk %s for full skjerm forhÃ¥ndsvisning" + +#, c-format +msgid "Press %s for game options" +msgstr "Trykk %s for spill innstillinger" + +#, c-format +msgid "Press %s for global options" +msgstr "Trykk %s for globale innstillinger" + +#, c-format +msgid "Press %s to discard options" +msgstr "Trykk %s for Ã¥ forkaste alternativer" + +#, c-format +msgid "Press %s to download and update" +msgstr "Trykk %s for Ã¥ laste ned og oppdatere" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Trykk %s for Ã¥ laste ned dette temaet" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Trykk %s for Ã¥ laste ned, %s for Ã¥ gÃ¥ tilbake" + +#, c-format +msgid "Press %s to return" +msgstr "Trykk %s for Ã¥ gÃ¥ tilbake" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Trykk %s for Ã¥ lagre globale innstillinger" + +#, c-format +msgid "Press %s to save options" +msgstr "Trykk %s for Ã¥ lagre instillinger" + +#, c-format +msgid "Press %s to save selection" +msgstr "Trykk %s for Ã¥ lagre valg" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Trykk %s for Ã¥ velge filter" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Trykk %s for Ã¥ velge sorterings metode" + +#, c-format +msgid "Press %s to start game" +msgstr "Trykk %s for Ã¥ starte spillet" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Trykk %s for Ã¥ oppdatere uten meta.xml" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "Trykk %s/%s for Ã¥ velge enhet." + +msgid "Press 2 to reload IOS" +msgstr "Trykk 2 for Ã¥ laste IOS pÃ¥ nytt" + +msgid "Press A to select device" +msgstr "Trykk A for Ã¥ velge enhet" + +msgid "Press B to exit to HBC" +msgstr "Trykk B for Ã¥ avslutte til HBC" + +msgid "Press HOME to reset" +msgstr "Trykk HJEM for Ã¥ starte pÃ¥ nytt" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Trykk pÃ¥ VENSTRE/HØYRE for Ã¥ velge IOS" + +msgid "Press any button to continue..." +msgstr "Trykk pÃ¥ en knapp for Ã¥ fortsette..." + +msgid "Press any button to exit..." +msgstr "Trykk pÃ¥ en knapp for Ã¥ avslutte..." + +msgid "Press any button to restart..." +msgstr "Trykk pÃ¥ en knapp for Ã¥ starte pÃ¥ nytt..." + +msgid "Press any button..." +msgstr "Trykk pÃ¥ hvilken som helst knapp..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Trykk pÃ¥ %s for Ã¥ laste ned covers" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Trykk pÃ¥ %s for Ã¥ løse ut DVD." + +msgid "Profile:" +msgstr "Profil:" + +#, c-format +msgid "Profile: %s" +msgstr "Profil: %s" + +msgid "Program Updates" +msgstr "Program oppdateringer" + +msgid "Publisher" +msgstr "Utgiver" + +msgid "Puzzle" +msgstr "Pusslespill" + +msgid "Quit" +msgstr "Utgangsdøra" + +msgid "RAW" +msgstr "RÃ…" + +msgid "RIGHT" +msgstr "HØYRE" + +msgid "RPG" +msgstr "Rollespill" + +msgid "Racing" +msgstr "Racing" + +#, c-format +msgid "Rated %s" +msgstr "Vurdert til %s" + +msgid "Rating" +msgstr "Aldersgrense" + +msgid "Read" +msgstr "Les" + +msgid "Reading BCA..." +msgstr "Leser BCA..." + +msgid "Reboot" +msgstr "Omstart" + +msgid "Release Date" +msgstr "Utgivelsesdato" + +msgid "Release Notes: (short)" +msgstr "Utgivelses notater: (kort)" + +msgid "Removing game, please wait..." +msgstr "Fjerner spill, venligst vent..." + +msgid "Restarting Wii..." +msgstr "Starter Wii pÃ¥ nytt..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Returnerte! (ret = %d)" + +msgid "Rhythm" +msgstr "Rytme" + +msgid "Rows:" +msgstr "Rader:" + +msgid "Running benchmark, please wait" +msgstr "Kjører benchmark, vennligst vent" + +msgid "S. Chinese" +msgstr "Smpl. Kinesisk" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC kort" + +msgid "SUCCESS!" +msgstr "SUKSESS!" + +msgid "Save .gct" +msgstr "Lagre .gct" + +msgid "Save Debug" +msgstr "Lagre debug" + +msgid "Save Settings" +msgstr "Lagre instillinger" + +msgid "Save debug.log" +msgstr "Lagre debug.log" + +msgid "Saving Settings... " +msgstr "Lagrer instillinger... " + +msgid "Saving cheats..." +msgstr "Lagrer juksekoder..." + +msgid "Saving gamelist.txt ... " +msgstr "Lagrer gamelist.txt ... " + +msgid "Saving settings..." +msgstr "Lagrer instillinger..." + +msgid "Saving:" +msgstr "Lagrer:" + +#, c-format +msgid "Saving: %s" +msgstr "Lagrer: %s" + +msgid "Scroll:" +msgstr "Bla:" + +msgid "Select Alternative .dol:" +msgstr "velg alternativ .dol" + +msgid "Select WBFS device:" +msgstr "Velg WBFS enhet:" + +msgid "Select a different partition" +msgstr "Velg en annen partisjon" + +msgid "Select a partition" +msgstr "Velg en partisjon" + +msgid "Select all" +msgstr "Velg alle" + +msgid "Select the game you want to boot" +msgstr "Velg spillet du vil starte" + +msgid "Selected Game" +msgstr "Valgt spill" + +msgid "Settings" +msgstr "Instillinger" + +msgid "Shooter" +msgstr "Skyting" + +msgid "Show All" +msgstr "Vis alle" + +msgid "Show cIOS info" +msgstr "Vis cIOS info" + +msgid "Shutdown" +msgstr "SlÃ¥ av" + +msgid "Side:" +msgstr "Side:" + +msgid "Simulation" +msgstr "Simulering" + +msgid "Size(GB) Type Mount Used" +msgstr "Størrelse(GB) Type Montert Brukt" + +#, c-format +msgid "Size: %d bytes" +msgstr "Størrelse: %d bytes" + +msgid "Sort" +msgstr "Sorter" + +msgid "Sort Games" +msgstr "Sorter spill" + +msgid "Sort Order:" +msgstr "Sort. rekkefølge" + +msgid "Sort Type:" +msgstr "Sort. type" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Sorter: %s-%s" + +msgid "Spanish" +msgstr "Spansk" + +msgid "Sports" +msgstr "Sport" + +msgid "Start" +msgstr "Start" + +msgid "Start Game" +msgstr "Start spillet" + +msgid "Start this game?" +msgstr "Start dette spillet?" + +msgid "Stopping DVD..." +msgstr "Stopper DVD..." + +msgid "Strategy" +msgstr "Strategi" + +msgid "Style" +msgstr "Stil" + +msgid "Style:" +msgstr "Stil" + +msgid "Sync FAT free space info?" +msgstr "Synkroniser FAT ledig plass informasjon?" + +msgid "Synchronizing, please wait." +msgstr "Synkroniserer, vennligst vent." + +msgid "System" +msgstr "System" + +msgid "System Def." +msgstr "Wii standard" + +msgid "T. Chinese" +msgstr "Tra. Kinesisk" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Tema informasjon" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Tema kan være for stort.\n" +"Det er anbefalt Ã¥ starte Cfg pÃ¥ nytt.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temaer levert av wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Temaer Ã¥ laste ned" + +msgid "Themes with updates" +msgstr "Temaer med oppdateringer" + +msgid "Title" +msgstr "Tittel" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "For mange juksekoder! (%d)" + +msgid "Too many code lines!" +msgstr "For mange kode linjer!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "For mange fragmenter! %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Prøver (url#%d) ..." + +msgid "UNUSED" +msgstr "UBRUKT" + +msgid "UP" +msgstr "OPP" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' starter ikke med 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' har ingen STI del" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko funnet. Debugging er aktivert." + +msgid "USB Mass Storage Device" +msgstr "USB lagringsenhet" + +msgid "Unknown" +msgstr "Ukjent" + +msgid "Unknown syntax!" +msgstr "Ukjent syntax!" + +msgid "Unplayed" +msgstr "Uprøvd" + +msgid "Unplayed Games" +msgstr "Uprøvde spill" + +msgid "Update WiiTDB Game Database" +msgstr "Oppdater WiiTDB spill database" + +msgid "Update themes" +msgstr "Oppdater temaer" + +msgid "Update titles.txt" +msgstr "Oppdater titler tekstfil (titles.txt)" + +msgid "Updates" +msgstr "Oppdateringer" + +msgid "Updating database." +msgstr "Oppdaterer database." + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Brukt: %.1fGB Ledig: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Bruker lagret alternativ .dol:" + +msgid "Version" +msgstr "Versjon" + +msgid "Video Patch:" +msgstr "Bildepatch:" + +msgid "Video:" +msgstr "Bilde:" + +msgid "View" +msgstr "Visning" + +msgid "Vitality Sensor" +msgstr "Vitalitet føler" + +msgid "WARNING:" +msgstr "ADVARSEL:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB av %.1fGB ledig" + +msgid "Wheel" +msgstr "Ratt" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiTDB Game Database" +msgstr "WiiTDB spill database" + +msgid "Wiimote" +msgstr "Wiikontroll" + +msgid "Write Playlog:" +msgstr "Lagre spillelogg:" + +#, c-format +msgid "Writing to %s" +msgstr "Skriver til %s" + +#, c-format +msgid "Writing: %s" +msgstr "Skriver: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Feil størrelse: %d (%d)" + +msgid "Yes" +msgstr "Ja" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Du kan ogsÃ¥ prøve Ã¥ koble fra\n" +"og sette i enheten igjen,\n" +"eller bare vente litt til" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ ENDRET ]" + +msgid "[ FOUND ]" +msgstr "[ FUNNET ]" + +msgid "[ SAVED ]" +msgstr "[ LAGRET ]" + +msgid "[HOME]" +msgstr "[HJEM]" + +msgid "[No HQ]" +msgstr "[Ingen HK]" + +msgid "[USED]" +msgstr "[BRUKT]" + +msgid "[default]" +msgstr "[standard]" + +msgid "[saved]" +msgstr "[lagret]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "skadet/dÃ¥rlig wbfs fil: %s" + +msgid "calculating space, please wait..." +msgstr "beregner plass, vennligst vent" + +msgid "delete" +msgstr "slett" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "forkast" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "domene %s kunne ikke løses" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "feil ved lesing av: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "for %d spillere" + +msgid "for 1 player" +msgstr "for 1 spiller" + +msgid "format" +msgstr "formater" + +msgid "free" +msgstr "ledig" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "lineærlesehastighet: %.2f mb/s" + +msgid "linear..." +msgstr "lineær?.." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "musikk fil for stor (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 eller music.mod ikke funnet!" + +msgid "no file" +msgstr "ingen fil" + +msgid "none" +msgstr "ingen" + +msgid "or format a WBFS partition." +msgstr "eller formater en WBFS partisjon" + +msgid "page" +msgstr "side" + +msgid "parse error" +msgstr "parse error" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "tilfedlig lese hastighet: %.2f mb/s" + +msgid "random..." +msgstr "tilfeldig..." + +msgid "reset" +msgstr "nullstill" + +msgid "revert" +msgstr "nullstill" + +msgid "save" +msgstr "lagre" + +#, c-format +msgid "split error: %s" +msgstr "oppdelings feil: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "kan ikke lese wii discen" + +#, c-format +msgid "used: %p - %p" +msgstr "brukt: %p - %p" + +#~ msgid "Front" +#~ msgstr "Front" + +#~ msgid "Loading previous game list..." +#~ msgstr "Leser inn forige spill liste..." + +#~ msgid "Remove Game" +#~ msgstr "Fjern spill" diff --git a/Languages/PT_BR.lang b/Languages/PT_BR.lang new file mode 100644 index 0000000..723129a --- /dev/null +++ b/Languages/PT_BR.lang @@ -0,0 +1,1911 @@ +# Brazilian Portuguese translation for CFG USB Loader. +# Copyright (C) 2010 LeonLeao +# This file is distributed under the same license as the PACKAGE package. +# Original: mangojambo , 2010 +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PT-BR translation for CFG USB loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2010-09-23 21:40-0300\n" +"Last-Translator: LeonLeao \n" +"Language-Team: Brazilian Portuguese\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB livre de %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% de %.2fGB (%c) TE: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB copiado em %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d disponível" + +#, c-format +msgid "%d more notes" +msgstr "%d mais notas" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Dividir: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, por favor, aguarde..." + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d segundos de espera)" + +msgid "(This can take a couple of minutes)" +msgstr "(Isso pode levar alguns minutos)" + +msgid ".dol too small" +msgstr ".dol muito pequeno" + +msgid "3D cover" +msgstr "CAPA 3D" + +msgid "< ASC >" +msgstr "< CRESC >" + +msgid "< DESC >" +msgstr "< DECRESC >" + +msgid "< DOWNLOAD >" +msgstr "< BAIXAR >" + +msgid "About" +msgstr "Sobre" + +msgid "Action" +msgstr "Ação" + +msgid "Additional config:" +msgstr "Config. Adicional:" + +msgid "Admin Lock:" +msgstr "Travar configs." + +msgid "Admin Unlock" +msgstr "Destravar configs" + +msgid "Adult" +msgstr "Adulto" + +msgid "Adventure" +msgstr "Aventura" + +msgid "All" +msgstr "Todos" + +msgid "All Games" +msgstr "Todos os Jogos" + +msgid "All themes up to date." +msgstr "Todos os temas em dia." + +msgid "Alt dol:" +msgstr "dol Alt:" + +msgid "Alternative .dol:" +msgstr ".dol Alternativo:" + +msgid "Anti 002 Fix:" +msgstr "Correção erro 002:" + +#, c-format +msgid "App. Path: %s" +msgstr "Caminho do Aplicativo: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Você tem certeza que quer\n" +"esta partição?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Você tem certeza que quer CORRIGIR\n" +"esta partição?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Você tem certeza que quer remover este\n" +"jogo?" + +msgid "Ascending" +msgstr "Crescente" + +msgid "Auto" +msgstr "Automático" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Auto início do jogo: %.6s não encontrado!" + +msgid "Available Updates" +msgstr "Atualizações Disponíveis" + +msgid "Back" +msgstr "Voltar" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Basic" +msgstr "Básico" + +msgid "Block IOS Reload:" +msgstr "Bloq. Reload do IOS:" + +msgid "Boot Disc" +msgstr "Iniciar Disco" + +msgid "Boot disc" +msgstr "Iniciar disco" + +msgid "Booting Wii game, please wait..." +msgstr "" +"Iniciando jogo do wii, aguarde...Iniciando jogo de Wii\n" +"Por favor, aguarde..." + +#, c-format +msgid "CFG base: %s" +msgstr "Base de CFG: %s" + +msgid "Cancelled." +msgstr "Cancelado" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Não é possível criar diretório: %s" + +msgid "Cheat Codes:" +msgstr "Cód. de Cheat:" + +msgid "Cheats: " +msgstr "Cheats" + +msgid "Check For Updates" +msgstr "Busca por Atualizações" + +msgid "Checking for themes..." +msgstr "Busca por temas..." + +msgid "Checking for updates..." +msgstr "Busca por atualizações..." + +msgid "Choose a sorting method" +msgstr "Escolha um critério de organização" + +msgid "Classic" +msgstr "Clássico" + +msgid "Classic Controller" +msgstr "Controle Clássico" + +msgid "Clear Patches:" +msgstr "Cancelar Patches:" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Completo. Tamanho: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Erro de configuração, MAX_DNS_ENTRIES alcançado enquanto a lista está vazia" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Erro de conexão de net_read() Erro: %i" + +msgid "Console" +msgstr "Console" + +msgid "Console Def." +msgstr "Def. Console" + +msgid "Controller" +msgstr "Controle" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Não é possível inicializar o módulo DIP! (ret = %d)" + +msgid "Country Fix:" +msgstr "Fixar Região:" + +msgid "Cover" +msgstr "Capa" + +msgid "Cover Image:" +msgstr "Imagem da Capa:" + +msgid "Cover Style" +msgstr "Estilo de capa" + +msgid "Cover~~Back" +msgstr "Capa~~traseira" + +msgid "Cover~~Front" +msgstr "Capa~~Frontal" + +msgid "Create" +msgstr "Criar" + +msgid "Creator" +msgstr "Criador" + +#, c-format +msgid "Current Version: %s" +msgstr "Versão Atual: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"O Custom IOS %d não pôde ser encontrado!\n" +"Favor instalá-lo." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "O Custom IOS %d não pôde ser carregado! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"O Custom IOS %d é um stub!\n" +"Favor reinstalá-lo." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "O Custon IOS %s Carregado Corretamente" + +msgid "DISC cover" +msgstr "Capa do DISCO" + +msgid "DOWN" +msgstr "BAIXO" + +msgid "Dance Pad" +msgstr "Tapete de Dança" + +msgid "Database update successful." +msgstr "Banco de Dados atualizado com êxito." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Remover Jogo" + +msgid "Deleting" +msgstr "Apagando" + +msgid "Descending" +msgstr "Descrescente" + +msgid "Developer" +msgstr "Desenvolvedor" + +msgid "Device is not responding!" +msgstr "Dispositivo não está respondendo" + +msgid "Device:" +msgstr "Dispositivo" + +msgid "Disc" +msgstr "Disco" + +msgid "Disc (Ask)" +msgstr "Disco (pedido)" + +msgid "Done." +msgstr "Concluído." + +msgid "Download .txt" +msgstr "Baixar .txt" + +msgid "Download All Covers" +msgstr "Baixar todas as capas" + +msgid "Download All Missing Covers" +msgstr "Baixar TODAS as Capas faltando" + +msgid "Download Missing Covers" +msgstr "Baixar capas faltantes" + +msgid "Download Themes" +msgstr "Baixar Temas" + +msgid "Download complete." +msgstr "Download completo." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Erro ao baixar no gamercard #%d." + +msgid "Download titles.txt" +msgstr "Baixar Títulos" + +msgid "Downloadable Content" +msgstr "Conteúdo Disponível" + +msgid "Downloading ALL MISSING covers" +msgstr "Baixando as capas FALTANDO" + +msgid "Downloading ALL covers" +msgstr "Baixando TODAS as capas" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "Baixando TODAS as capas para %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "Baixando capas FALTANDO para %.6s" + +msgid "Downloading Theme previews..." +msgstr "Baixando prévia do Tema..." + +msgid "Downloading cheats..." +msgstr "Baixando trapaças..." + +msgid "Downloading database." +msgstr "Baixando banco de dados." + +msgid "Downloading titles.txt ..." +msgstr "Baixando titles.txt ..." + +#, c-format +msgid "Downloading: %s" +msgstr "Baixando: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Baixando: %s de %s" + +msgid "Downloads" +msgstr "Downloads" + +msgid "Drums" +msgstr "Bateria (instrumento)" + +msgid "Dutch" +msgstr "Holandês" + +msgid "ERROR" +msgstr "ERRO" + +msgid "ERROR creating file" +msgstr "ERRO ao criar arquivo" + +msgid "ERROR game opt" +msgstr "ERRO do jogo escolhido" + +msgid "ERROR reading BCA!" +msgstr "ERRO ao ler BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "ERRO ao remover %s" + +msgid "ERROR writing BCA!" +msgstr "ERRO ao escrever BCA!" + +msgid "ERROR!" +msgstr "ERRO!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ERRO! (ret = %d)" + +msgid "ERROR:" +msgstr "ERRO:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ERRO: %s não é acessível" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ERRO: Carregando Aplicativo %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ERRO: CIOS222/223 v4 requerido para BCA" + +msgid "ERROR: Cache Close" +msgstr "ERRO: Cache Fechado" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ERRO: Não é possível abrir o jogo! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ERRO: O jogo já está instalado!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ERRO: Obter Certif. %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ERRO: ID do Jogo Inválida" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ERRO: Carregando módulo EHC! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERRO: Escrita NTFS desabilitada!\n" +"(colocar ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ERRO: Partições não encontradas! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ERRO: Não é um disco de Wii!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ERRO: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ERRO: AbrirPartição %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ERRO: AbrirPartição(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ERRO: DefinirListaFrag(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ERRO: DefinirWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ERRO: Configurando modo SD" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ERRO: Inicializando USB! (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"ERRO: cIOS249rev18, cIOS222v4, \n" +"cIOS223v4, cIOS224v5 ou maior requerido\n" +"para iniciar jogos de partição FAT!\n" +"Atualize o IOS249 ou escolha um IOS diferente." + +msgid "ERROR: cache: out of memory" +msgstr "ERRO: cache: sem memória" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ERRO: criando: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ERRO: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "ERRO: memória sobreposta!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERRO: multiplas partições wbfs\n" +"suportadas somente com IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ERRO: não há espaço livre suficiente!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ERRO: ntfs não foi desmontado asseadamente" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ERRO: abrindo %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ERRO: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ERRO: configurando fragmentos %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ERRO: escrevendo %s (%d)." + +msgid "EXTEND" +msgstr "EXTENDIDO" + +msgid "Ejecting DVD..." +msgstr "Ejetando DVD..." + +msgid "English" +msgstr "Inglês" + +msgid "English Title" +msgstr "Título Inglês" + +msgid "Enter Code: " +msgstr "Digite o Código:" + +msgid "Error GRRLIB init" +msgstr "Erro ao inicializar GRRLIB" + +msgid "Error Initializing Network." +msgstr "Erro ao inicializar a Rede." + +msgid "Error adding disc!" +msgstr "Erro ao adicionar disco!" + +msgid "Error creating directory..." +msgstr "Erro ao criar diretório..." + +msgid "Error discarding options!" +msgstr "Erro ao descartar opções!" + +msgid "Error downloading theme preview..." +msgstr "Erro ao baixar prévia do tema..." + +msgid "Error downloading theme..." +msgstr "Erro ao baixar tema..." + +msgid "Error downloading themes..." +msgstr "Erro ao baixar temas..." + +msgid "Error downloading update..." +msgstr "Erro ao baixar atualização..." + +msgid "Error downloading updates..." +msgstr "Erro ao baixar atualizações..." + +msgid "Error downloading." +msgstr "Erro ao baixar." + +msgid "Error establishing connection" +msgstr "Erro ao estabelecer conexão" + +msgid "Error extracting theme..." +msgstr "Erro ao extrair tema..." + +msgid "Error opening database, update did not complete." +msgstr "" +"Erro ao abrir banco de dados\n" +"atualização não foi completada." + +#, c-format +msgid "Error opening: %s" +msgstr "Erro ao abrir: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Erro ao jogar %s" + +msgid "Error reading .dol" +msgstr "Erro ao ler .dol" + +msgid "Error reading dol header" +msgstr "Erro ao ler dol header" + +#, c-format +msgid "Error saving %s" +msgstr "Erro ao salvar %s" + +msgid "Error saving options!" +msgstr "Erro ao salvar opções!" + +msgid "Error saving settings!" +msgstr "Erro ao salvar configurações!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Erro ao armazenar registro do jogo.\n" +"Inicie pelo menu do Wii para corrigir." + +msgid "Error: Invalid PNG image!" +msgstr "Erro: Imagem PNG inválida!" + +msgid "Error: no URL." +msgstr "Erro: não há URL" + +msgid "Error: no data." +msgstr "Erro: não há dados." + +msgid "Exit" +msgstr "Sair" + +#, c-format +msgid "Extracting: %s" +msgstr "Extraindo: %s" + +msgid "FAIL" +msgstr "FALHA" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ERRO DE ALOCAÇÃO %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: grade de alocação(%d)" + +#, c-format +msgid "FILL %d" +msgstr "PREENCHER %d" + +msgid "FLAT cover" +msgstr "Capa chata" + +msgid "FULL cover" +msgstr "Capa completa" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Desl" + +msgid "Fav: On" +msgstr "Fav: Lig" + +msgid "Favorite" +msgstr "Favorito" + +msgid "Favorite Games" +msgstr "Jogos Favoritos" + +msgid "Favorite:" +msgstr "Favorito:" + +msgid "Favorites:" +msgstr "Favoritos:" + +msgid "Fighting" +msgstr "Luta" + +#, c-format +msgid "File not found! %s" +msgstr "Arquivo não encontrado! %s" + +msgid "Filter" +msgstr "Filtro" + +msgid "Filter Games" +msgstr "Filtrar Jogos" + +msgid "Filter by Controller" +msgstr "Filtrar por Controle" + +msgid "Filter by Genre" +msgstr "Filtrar por gênero" + +msgid "Filter by Online Features" +msgstr "Filtrar por Características Online" + +msgid "Filter:" +msgstr "Filtro:" + +msgid "Fixing EXTEND partition..." +msgstr "Corrigindo partições EXTENDIDAS..." + +msgid "Force NTSC" +msgstr "Forçar NTSC" + +msgid "Force PAL50" +msgstr "Forçar PAL50" + +msgid "Force PAL60" +msgstr "Forçar PAL60" + +msgid "Formatting" +msgstr "Formatando" + +#, c-format +msgid "Found %s" +msgstr "Encontrado %s" + +msgid "French" +msgstr "Francês" + +msgid "Full" +msgstr "Completo" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Padrões do jogo" + +msgid "Game Options" +msgstr "Opções de Jogo" + +#, c-format +msgid "Game Options: %s" +msgstr "Opções de Jogo: %s" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d reportada: %.*s" + +msgid "Genre" +msgstr "Sexo" + +msgid "German" +msgstr "Alemão" + +msgid "Global Options" +msgstr "Opções Globais" + +msgid "Guitar" +msgstr "Guitarra" + +msgid "HQ cover" +msgstr "Capa HQ" + +msgid "HTTP Response was without a file" +msgstr "Resposta HTTP foi sem um arquivo" + +msgid "Hide" +msgstr "Esconder" + +msgid "Hide Game:" +msgstr "Ocultar Jogo:" + +msgid "Hold button B to cancel." +msgstr "Segure o botão B para cancelar." + +msgid "Homebrew Channel" +msgstr "Homebrew Channel" + +msgid "Hook Type:" +msgstr "Tipo de Hook:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID desajustada: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "Recargarregar IOS: Bloqueado" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) falhou com código %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Se o cfg não iniciar corretamente\n" +"na próxima vez, copie o boot.dol.bak\n" +"por cima do boot.dol e tente de novo." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Incoerência na codificação LZ77 %x" + +msgid "Info" +msgstr "Informações" + +#, c-format +msgid "Info file: %s" +msgstr "Informação do arquivo: %s" + +msgid "Initializing Network..." +msgstr "Inicializando a Rede..." + +msgid "Install" +msgstr "Instalar" + +msgid "Install Date" +msgstr "Data de Instalação" + +msgid "Install Game" +msgstr "Instalar Jogo" + +msgid "Install game" +msgstr "Instalar jogo" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "ERRO de Instalação! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Instalando jogo,, por favor aguarde..." + +msgid "Invalid .dol" +msgstr ".dol inválido" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Partição inválida: '%s'" + +msgid "Italian" +msgstr "Italiano" + +msgid "Japanese" +msgstr "Japonês" + +msgid "Japanese Title" +msgstr "Título Japonês" + +msgid "Keyboard" +msgstr "Teclado" + +msgid "Korean" +msgstr "Koreano" + +msgid "LEFT" +msgstr "ESQUERDO" + +msgid "LOCKED!" +msgstr "TRANCADO" + +msgid "Language:" +msgstr "Idioma" + +msgid "Last Play Date" +msgstr "Data do último jogo" + +msgid "Launch Methods" +msgstr "Método de inicialização" + +msgid "Load OK!" +msgstr "Carregado corretamente!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Versão do Loader: %s" + +msgid "Loading ..." +msgstr "Carregando..." + +msgid "Main" +msgstr "Principal" + +msgid "Main Menu" +msgstr "Menu Principal" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Assegure-se que a porta USB 0 seja\n" +"usada! (A mais próxima da borda)" + +msgid "Manage" +msgstr "Gerenciar" + +msgid "Manage Cheats" +msgstr "Gerenciar Trapaças" + +msgid "Microphone" +msgstr "Microfone" + +msgid "Motion+" +msgstr "Motion Plus" + +msgid "Mounting device, please wait..." +msgstr "" +"Abrindo dispositivo, aguarde...Montando dispositivo\n" +"por favor, aguarde..." + +msgid "Music" +msgstr "Música" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Arquivo de música do dir: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Tamanho do arquivo de música: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Arquivo de música: %s" + +msgid "Music: Disabled" +msgstr "Música: Desativada" + +msgid "Music: Enabled" +msgstr "Música: Ativada" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Música: Buscando %s arquivos em: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Música: Próximo arquivo indexado para tocar: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Música: Número de %s arquivos encontrados: %i" + +msgid "Music: musicArray contents: " +msgstr "Música: conteúdos de musicArray: " + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTA: CIOS249 antes do rev10:\n" +"Não suporta carregar jogos de um SDHC!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTA: CIOS249 antes de rev14:\n" +"Possível erro #001 não é manipulável!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTA: CIOS249 antes de rev9:\n" +"Não suporta carregar jogos por USB!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTA: Você está usando CIOS249 rev13:\n" +"Para sair do jogo deve resetar ou\n" +"desligar o Wii antes que inicie\n" +"outro jogo ou a tela se congelará aqui!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTA: Você está usando CIOS249 rev14:\n" +"Sabe-se que ele tem problemas com jogos\n" +"de dupla camada. É recomendável que você\n" +"use uma revisão ou um IOS diferente para\n" +"instalar/reproduzir este jogo." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTA: É necessário reiniciar o loader\n" +"para que a atualização tenha efeito." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTA: A partição P#%d é do tipo EXTENDIDA, mas\n" +"contém um sistema de arquivos WBFS. Isto é uma\n" +"configuração inválida. Pressione %s para trocar o\n" +"tipo de partição de EXTENDIDA para de dados." + +msgid "NTFS compression not supported!" +msgstr "Compressão NTFS não suportada!" + +msgid "NTFS encryption not supported!" +msgstr "Encriptação NTFS não suportada!" + +msgid "Network connection established." +msgstr "Conexão com a rede estabelecida." + +msgid "Network error. Can't update gamercards." +msgstr "Erro na Rede. Não é possível atualizar os gamercards." + +msgid "New Themes" +msgstr "Novos Temas" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Conectividade com Nintendo DS" + +msgid "No" +msgstr "Não" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Nenhuma parte de domínio na URL '%s'" + +msgid "No games found!" +msgstr "Jogos não encontrados!" + +msgid "No partition selected!" +msgstr "Não há partições selecionadas!" + +msgid "No themes found." +msgstr "Nenhum tema encontrados." + +msgid "No updates found." +msgstr "Nenhuma atualização encontrada." + +msgid "None found on disc" +msgstr "Nada encontrado no disco" + +msgid "Not Found!" +msgstr "Não encontrado!" + +msgid "Number of Online Players" +msgstr "Nº de Jogadores Online" + +msgid "Number of Players" +msgstr "Nº de Jogadores" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (cheats):" + +msgid "Ocarina Cheat Manager" +msgstr "Gerenciador de cheats Ocarina" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Erro no Código" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Códigos encontrados" + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Nenhum código encontrado" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Erro por Falta de memória" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Procurando códigos..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Há muitos códigos" + +msgid "Off" +msgstr "Desligado" + +msgid "On" +msgstr "Ligado" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Conteúdo Online" + +msgid "Online Play" +msgstr "Jogo Online" + +msgid "Online Players" +msgstr "Jogadores Online" + +msgid "Online Score List" +msgstr "Lista de Pontuação Online" + +msgid "Online Updates" +msgstr "Atualizações" + +msgid "Open Sort" +msgstr "Ordenar" + +msgid "Open Style" +msgstr "Estilo" + +msgid "Opening DVD disc..." +msgstr "Abrindo disco DVD..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Abrindo partição: %s" + +msgid "Options" +msgstr "Opções" + +msgid "Options discarded for this game." +msgstr "Opções descartadas para este jogo." + +msgid "Options saved for this game." +msgstr "Opções salvas para este jogo." + +msgid "Out of memory" +msgstr "Sem memória" + +msgid "Page:" +msgstr "Pag." + +msgid "Partition:" +msgstr "Partição:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partição: %s não encontrada!" + +msgid "Party" +msgstr "Festa" + +msgid "Paused Start" +msgstr "Início pausado" + +msgid "Platformer" +msgstr "Plataforma" + +msgid "Play Count" +msgstr "Contador de Jogo" + +msgid "Players" +msgstr "Jogadores" + +msgid "Please insert a game disc..." +msgstr "Por favor, insira um disco de jogo..." + +#, c-format +msgid "Press %s button for options." +msgstr "Pressione o botão %s para as opções." + +#, c-format +msgid "Press %s button to %s." +msgstr "Pressione o botão %s para %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "" +"Pressione o botão %s para\n" +"aplicar códigos." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Pressione o botão %s para cancelar." + +#, c-format +msgid "Press %s button to change device." +msgstr "" +"Pressione o botão %s para mudar\n" +"de dispositivo." + +#, c-format +msgid "Press %s button to continue." +msgstr "Pressione o botão %s para continuar." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Pressione o botão %s para apagar FS." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Pressione o botão %s para extrair BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Pressione o botão %s para sair." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "" +"Pressione o botão %s para corrigir\n" +"partição EXTENDIDA/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "" +"Pressione o botão %s para formatar\n" +"para WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Pressione o botão %s para voltar." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "" +"Pressione o botão %s para selecionar\n" +"uma partição." + +#, c-format +msgid "Press %s button to select." +msgstr "Pressione o botão %s para selecionar." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Pressione o botão %s para pular códigos." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Pressione %s para sincronizar FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Pressione %s para prévia em tela cheia" + +#, c-format +msgid "Press %s for game options" +msgstr "Pressione %s para as opções de jogo" + +#, c-format +msgid "Press %s for global options" +msgstr "Pressione %s para as opções globais" + +#, c-format +msgid "Press %s to discard options" +msgstr "" +"Pressione o botão %s para descartar\n" +"opções" + +#, c-format +msgid "Press %s to download and update" +msgstr "Pressione %s para baixar e atualizar" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Pressione %s para baixar esse tema" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "" +"Pressione %s para baixar, %s para\n" +"retornar" + +#, c-format +msgid "Press %s to return" +msgstr "Pressione o botão %s para retornar" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Pressione %s para salvar config. globais" + +#, c-format +msgid "Press %s to save options" +msgstr "Pressione %s para salvar opções" + +#, c-format +msgid "Press %s to save selection" +msgstr "Pressione %s para salvar seleção" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Pressione %s para selecionar filtro" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "" +"Pressione %s para selecionar\n" +"critério de organização" + +#, c-format +msgid "Press %s to start game" +msgstr "Pressione %s para iniciar o jogo" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "" +"Pressione %s para atualizar sem\n" +"o meta.xml" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "" +"Pressione ESQUERDA/DIREITA para\n" +"selecionar dispositivo." + +msgid "Press 2 to reload IOS" +msgstr "Pressione 2 para recarregar IOS" + +msgid "Press A to select device" +msgstr "Presione A para selecionar" + +msgid "Press B to exit to HBC" +msgstr "Pressione B para ir ao HBC" + +msgid "Press HOME to reset" +msgstr "Pressione HOME para resetar" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Pressione ESQUERDA/DIREITA para selecionar IOS" + +msgid "Press any button to continue..." +msgstr "" +"Pressione qualquer botão para\n" +"continuar..." + +msgid "Press any button to exit..." +msgstr "" +"Pressione quaquer botão para\n" +"sair..." + +msgid "Press any button to restart..." +msgstr "" +"Pressione quaquer botão para\n" +"reiniciar..." + +msgid "Press any button..." +msgstr "Pressione quaquer botão..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Pressione o botão %s para baixar capas." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Pressione o botão %s para ejetar o DVD." + +msgid "Profile:" +msgstr "Perfil:" + +#, c-format +msgid "Profile: %s" +msgstr "Perfil: %s" + +msgid "Program Updates" +msgstr "Atualizações do programa" + +msgid "Publisher" +msgstr "Editor" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Sair" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "DIREITA" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Corrida" + +#, c-format +msgid "Rated %s" +msgstr "Classificação %s" + +msgid "Rating" +msgstr "Avaliação" + +msgid "Read" +msgstr "Ler" + +msgid "Reading BCA..." +msgstr "Lendo BCA..." + +msgid "Reboot" +msgstr "Reiniciar" + +msgid "Release Date" +msgstr "Data da Versão" + +msgid "Release Notes: (short)" +msgstr "Notas da Versão: (curto)" + +msgid "Removing game, please wait..." +msgstr "Removendo jogo, por favor aguarde..." + +msgid "Restarting Wii..." +msgstr "Reiniciando Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Retornado! (ret = %d)" + +msgid "Rhythm" +msgstr "Ritmo" + +msgid "Rows:" +msgstr "Linhas" + +msgid "Running benchmark, please wait" +msgstr "Rodando benchmark, por favor, aguarde" + +msgid "S. Chinese" +msgstr "S. Chinês" + +msgid "SD/SDHC Card" +msgstr "Cartão SD/SDHC" + +msgid "SUCCESS!" +msgstr "SUCESSO!" + +msgid "Save .gct" +msgstr "Salvar .gct" + +msgid "Save Debug" +msgstr "Salvar Debug" + +msgid "Save Settings" +msgstr "Salvar configs." + +msgid "Save debug.log" +msgstr "Salvar debug.log" + +msgid "Saving Settings... " +msgstr "Salvando configurações..." + +msgid "Saving cheats..." +msgstr "Salvando Trapaças..." + +msgid "Saving gamelist.txt ... " +msgstr "Salvando gamelist.txt... " + +msgid "Saving settings..." +msgstr "Salvando configurações..." + +msgid "Saving:" +msgstr "Salvando:" + +#, c-format +msgid "Saving: %s" +msgstr "Salvando: %s" + +msgid "Scroll:" +msgstr "Rolar:" + +msgid "Select Alternative .dol:" +msgstr "Selecione .dol Alternativo:" + +msgid "Select WBFS device:" +msgstr "Selecione dispositivo WBFS:" + +msgid "Select a different partition" +msgstr "Selecionar uma partição diferente" + +msgid "Select a partition" +msgstr "Selecionar uma partição" + +msgid "Select all" +msgstr "Selecionar tudo" + +msgid "Select the game you want to boot" +msgstr "Selecionar o jogo que deseja iniciar" + +msgid "Selected Game" +msgstr "Jogo Selecionado" + +msgid "Settings" +msgstr "Configurações" + +msgid "Shooter" +msgstr "Tiro" + +msgid "Show All" +msgstr "Mostrar tudo" + +msgid "Show cIOS info" +msgstr "Info da cIOS" + +msgid "Shutdown" +msgstr "Desligar" + +msgid "Side:" +msgstr "Lado:" + +msgid "Simulation" +msgstr "Simulação" + +msgid "Size(GB) Type Mount Used" +msgstr "Tam.(GB) Tipo Part. Usado" + +#, c-format +msgid "Size: %d bytes" +msgstr "Tamanho: %d bytes" + +msgid "Sort" +msgstr "Ordenar" + +msgid "Sort Games" +msgstr "Ordenar Jogos" + +msgid "Sort Order:" +msgstr "Ordem:" + +msgid "Sort Type:" +msgstr "Tipo:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Ordem: %s-%s" + +msgid "Spanish" +msgstr "Espanhol" + +msgid "Sports" +msgstr "Esportes" + +msgid "Start" +msgstr "Jogar" + +msgid "Start Game" +msgstr "Iniciar Jogo" + +msgid "Start this game?" +msgstr "Iniciar este jogo?" + +msgid "Stopping DVD..." +msgstr "Parando DVD..." + +msgid "Strategy" +msgstr "Estratégia" + +msgid "Style" +msgstr "Estilo" + +msgid "Style:" +msgstr "Estilo:" + +msgid "Sync FAT free space info?" +msgstr "Sincronizar espaço livre?" + +msgid "Synchronizing, please wait." +msgstr "Sincronizando, aguarde." + +msgid "System" +msgstr "Sistema" + +msgid "System Def." +msgstr "Def. Sistema" + +msgid "T. Chinese" +msgstr "Chinês. T" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Informação do Tema" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"O tema pode ser muito grande.\n" +"Recomenda-se reiniciar o CFG.\n" + +msgid "Theme:" +msgstr "Tema" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temas fornecidos por wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Temas para download" + +msgid "Themes with updates" +msgstr "Temas com atualizações" + +msgid "Title" +msgstr "Título" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Há muitas Trapaças! (%d)" + +msgid "Too many code lines!" +msgstr "Há muitas linhas de códigos!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Há muitos fragmentos! %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Tentando (url#%d) ..." + +msgid "UNUSED" +msgstr "Não usado" + +msgid "UP" +msgstr "CIMA" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' não inicia com 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' não tem parte do endereço" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "" +"USB Gecko encontrado. Debug ligado.USB Gecko encontrado. A depuração\n" +"está ativada." + +msgid "USB Mass Storage Device" +msgstr "Dispositivo de Armazenamento USB" + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Unknown syntax!" +msgstr "Sintaxe desconhecida!" + +msgid "Unplayed" +msgstr "Não jogado" + +msgid "Unplayed Games" +msgstr "Jogos não jogados" + +msgid "Update WiiTDB Game Database" +msgstr "Atualizar WiiDB" + +msgid "Update themes" +msgstr "Atualizar temas" + +msgid "Update titles.txt" +msgstr "Atualizar titles.txt" + +msgid "Updates" +msgstr "Atualizações" + +msgid "Updating database." +msgstr "Atualizando banco de dados." + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Usado: %.1fGB Livre: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "Usando .dol Alternativo salvo:" + +msgid "Version" +msgstr "Versão" + +msgid "Video Patch:" +msgstr "Vídeo Patch:" + +msgid "Video:" +msgstr "Vídeo" + +msgid "View" +msgstr "Visualizar" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "WARNING:" +msgstr "ATENÇÃO:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB livre de %.1fGB" + +msgid "Wheel" +msgstr "Volante Wii" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiTDB Game Database" +msgstr "Base de dados WiiTDB" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Write Playlog:" +msgstr "Escrever Registro:" + +#, c-format +msgid "Writing to %s" +msgstr "Escrevendo para %s" + +#, c-format +msgid "Writing: %s" +msgstr "Escrevendo: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Tamanho Errado %d (%d)" + +msgid "Yes" +msgstr "Sim" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Você também pode tentar desconectar\n" +"e reconectar o dispositivo,\n" +"ou espere mais um pouco" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ TROCADO ]" + +msgid "[ FOUND ]" +msgstr "[ ENCONTRADO ]" + +msgid "[ SAVED ]" +msgstr "[ SALVO ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[Sem HQ]" + +msgid "[USED]" +msgstr "[USADO]" + +msgid "[default]" +msgstr "[padrão]" + +msgid "[saved]" +msgstr "[salvo]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "arquivo wbfs ruim: %s" + +msgid "calculating space, please wait..." +msgstr "calculando espaço, por favor, aguarde..." + +msgid "delete" +msgstr "apagar" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "descartar" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "domínio %s não pôde ser resolvido" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "erro ao ler: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "para %d jogadores" + +msgid "for 1 player" +msgstr "para 1 jogador" + +msgid "format" +msgstr "formato" + +msgid "free" +msgstr "livre" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "velocidade linear de leitura: %.2f mb/s" + +msgid "linear..." +msgstr "linear..." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "arquivo de música é muito grande (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 ou music.mod não encontrados!" + +msgid "no file" +msgstr "nenhum arquivo" + +msgid "none" +msgstr "nenhum" + +msgid "or format a WBFS partition." +msgstr "ou formatar uma partição WBFS" + +msgid "page" +msgstr "página" + +msgid "parse error" +msgstr "erro de análise" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "velocidade de leitura aleatória: %.2f mb/s" + +msgid "random..." +msgstr "aleatório..." + +msgid "reset" +msgstr "reset" + +msgid "revert" +msgstr "reverter" + +msgid "save" +msgstr "salvar" + +#, c-format +msgid "split error: %s" +msgstr "erro de divisão: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "não é possível abrir o disco de wii" + +#, c-format +msgid "used: %p - %p" +msgstr "usado: %p - %p" + +#~ msgid "Loading previous game list..." +#~ msgstr "Carregando lista anterior de jogos..." + +#~ msgid "Remove Game" +#~ msgstr "Remover Jogo" diff --git a/Languages/PT_PT.lang b/Languages/PT_PT.lang new file mode 100644 index 0000000..d7a1866 --- /dev/null +++ b/Languages/PT_PT.lang @@ -0,0 +1,1893 @@ +# European PT translation for CFG USB loader 65b (beta) +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Plucky Duck , 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PT-PT translation for CFG USB loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"Last-Translator: pplucky \n" +"Language-Team: European Portuguese\n" +"Language: European Portuguese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB livres de %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% de %.2fGB (%c) TE: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB copiado em %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d disponível" + +#, c-format +msgid "%d more notes" +msgstr "%d mais notas" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Dividir: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, por favor, aguarde..." + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d segundos de espera)" + +msgid "(This can take a couple of minutes)" +msgstr "(Isto pode levar uns minutos)" + +msgid ".dol too small" +msgstr ".dol muito pequeno" + +msgid "3D cover" +msgstr "Capa 3D" + +msgid "< ASC >" +msgstr "< CRESC >" + +msgid "< DESC >" +msgstr "< DECRESC >" + +msgid "< DOWNLOAD >" +msgstr "< TRANSFERIR >" + +msgid "About" +msgstr "Sobre" + +msgid "Action" +msgstr "Acção" + +msgid "Additional config:" +msgstr "Config. Adicional:" + +msgid "Admin Lock:" +msgstr "Bloqueio Admin.:" + +msgid "Admin Unlock" +msgstr "Desbloqueio Admin." + +msgid "Adult" +msgstr "Adulto" + +msgid "Adventure" +msgstr "Aventura" + +msgid "All" +msgstr "Todos" + +msgid "All Games" +msgstr "Todos os Jogos" + +msgid "All themes up to date." +msgstr "Todos os temas actualizados." + +msgid "Alt dol:" +msgstr "Dol Alt:" + +msgid "Alternative .dol:" +msgstr ".dol Alternativo:" + +msgid "Anti 002 Fix:" +msgstr "Corr. erro 002:" + +#, c-format +msgid "App. Path: %s" +msgstr "Caminho. Apl.: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Tem a certeza que quer %s\n" +"esta partição?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Tem a certeza que quer CORRIGIR\n" +"esta partição?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Tem a certeza que quer remover este\n" +"jogo?" + +msgid "Ascending" +msgstr "Ascendente" + +msgid "Auto" +msgstr "Auto" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "Início automático do jogo: %.6s não encontrado!" + +msgid "Available Updates" +msgstr "Actualizações Disponíveis" + +msgid "Back" +msgstr "Para trás" + +msgid "Balance Board" +msgstr "Balance Board" + +msgid "Basic" +msgstr "Básica" + +msgid "Block IOS Reload:" +msgstr "Bloquear Rec.IOS:" + +msgid "Boot Disc" +msgstr "Iniciar Disco" + +msgid "Boot disc" +msgstr "Iniciar Disco" + +msgid "Booting Wii game, please wait..." +msgstr "" +"A iniciar jogo Wii\n" +"Por favor, aguarde..." + +#, c-format +msgid "CFG base: %s" +msgstr "Base do CFG: %s" + +msgid "Cancelled." +msgstr "Cancelado." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Impossível criar pasta: %s" + +msgid "Cheat Codes:" +msgstr "Códigos batota:" + +msgid "Cheats: " +msgstr "Batotas: " + +msgid "Check For Updates" +msgstr "Verificar actualizações" + +msgid "Checking for themes..." +msgstr "A verificar temas..." + +msgid "Checking for updates..." +msgstr "A verificar actualizações..." + +msgid "Choose a sorting method" +msgstr "Escolha um critério de ordenação" + +msgid "Classic" +msgstr "Clássico" + +msgid "Classic Controller" +msgstr "Comando Clássico" + +msgid "Clear Patches:" +msgstr "Cancelar Patches" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Completo. Tamanho: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Erro de configuração, MAX_DNS_ENTRIES alcançado enquanto a lista está vazia" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "Erro de ligação de net_read() Erro: %i" + +msgid "Console" +msgstr "Consola" + +msgid "Console Def." +msgstr "Def. Consola" + +msgid "Controller" +msgstr "Comando" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "Não é possível inicializar o módulo DIP! (ret = %d)" + +msgid "Country Fix:" +msgstr "Correcção Região:" + +msgid "Cover" +msgstr "Capa" + +msgid "Cover Image:" +msgstr "Imagem de Capa:" + +msgid "Cover Style" +msgstr "Estilo de Capa" + +msgid "Cover~~Back" +msgstr "Capa~~Traseira" + +msgid "Cover~~Front" +msgstr "Capa~~Frente" + +msgid "Create" +msgstr "Criar" + +msgid "Creator" +msgstr "Autor" + +#, c-format +msgid "Current Version: %s" +msgstr "Versão Actual: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"O Custom IOS %d não foi encontrado!\n" +"Por favor instale-o." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "O Custom IOS %d não pôde ser carregado! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"O Custom IOS %d é um stub!\n" +"Por favor reinstale-o." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "Custon IOS %s Carregado OK" + +msgid "DISC cover" +msgstr "Capa do DISCO" + +msgid "DOWN" +msgstr "BAIXO" + +msgid "Dance Pad" +msgstr "Tapete de Dança" + +msgid "Database update successful." +msgstr "Base de dados actualizada com sucesso." + +msgid "Debug" +msgstr "Debug" + +msgid "Delete Game" +msgstr "Apagar Jogo" + +msgid "Deleting" +msgstr "A apagar" + +msgid "Descending" +msgstr "Descendente" + +msgid "Developer" +msgstr "Programador" + +msgid "Device is not responding!" +msgstr "Dispositivo não está a responder!" + +msgid "Device:" +msgstr "Dispositivo:" + +msgid "Disc" +msgstr "Disco" + +msgid "Disc (Ask)" +msgstr "Disco (Pedido)" + +msgid "Done." +msgstr "Concluído." + +msgid "Download .txt" +msgstr "Transferir .txt" + +msgid "Download All Covers" +msgstr "Transferir Todas as Capas" + +msgid "Download All Missing Covers" +msgstr "Transferir Todas as Capas em Falta" + +msgid "Download Missing Covers" +msgstr "Transferir Capas em Falta" + +msgid "Download Themes" +msgstr "Transferir Temas" + +msgid "Download complete." +msgstr "Transferência completa." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Erro de transferência no gamercard #%d." + +msgid "Download titles.txt" +msgstr "Transferir titles.txt" + +msgid "Downloadable Content" +msgstr "Conteúdo Transferível" + +msgid "Downloading ALL MISSING covers" +msgstr "A Transferir TODAS AS CAPAS EM FALTA" + +msgid "Downloading ALL covers" +msgstr "A Transferir TODAS AS CAPAS" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "A Transferir TODAS as capas para %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "A Transferir capas EM FALTA para %.6s" + +msgid "Downloading Theme previews..." +msgstr "A Transferir previews dos Temas..." + +msgid "Downloading cheats..." +msgstr "A Transferir batotas..." + +msgid "Downloading database." +msgstr "A Transferir base de dados." + +msgid "Downloading titles.txt ..." +msgstr "A Transferir titles.txt..." + +#, c-format +msgid "Downloading: %s" +msgstr "A Transferir: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "A Transferir: %s as %s" + +msgid "Downloads" +msgstr "Transferências" + +msgid "Drums" +msgstr "Bateria" + +msgid "Dutch" +msgstr "Holandês" + +msgid "ERROR" +msgstr "ERRO" + +msgid "ERROR creating file" +msgstr "ERRO a criar ficheiro" + +msgid "ERROR game opt" +msgstr "ERRO do jogo escolhido" + +msgid "ERROR reading BCA!" +msgstr "ERRO ao ler BCA!" + +#, c-format +msgid "ERROR removing %s" +msgstr "ERRO ao remover %s" + +msgid "ERROR writing BCA!" +msgstr "ERRO ao escrever BCA!" + +msgid "ERROR!" +msgstr "ERRO!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "ERRO! (ret = %d)" + +msgid "ERROR:" +msgstr "ERRO:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "ERRO: %s não está acessível" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "ERRO: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "ERRO: CIOS222/223 v4 necessário para BCA" + +msgid "ERROR: Cache Close" +msgstr "ERRO: Cache Fechado" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "ERRO: Não foi possível abrir o jogo! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "ERRO: Jogo já instalado!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "ERRO: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "ERRO: ID do Jogo Inválida" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "ERRO: A Carregar módulo EHC! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"ERRO: Escrita NTFS desactivada!\n" +"(colocar ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "ERRO: Nenhuma partição encontrada! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "ERRO: Não é um disco da Wii!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "ERRO: Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "ERRO: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "ERRO: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "ERRO: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "ERRO: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "ERRO: A configurar modo SD" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "ERRO: Inicialização USB! (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"ERRO: cIOS249rev18, cIOS222v4, \n" +"cIOS223v4, cIOS224v5 ou superior necessário\n" +"para iniciar jogos a partir duma partição FAT!\n" +"Actualize o IOS249 ou escolha um IOS diferente." + +msgid "ERROR: cache: out of memory" +msgstr "ERRO: cache: sem memória" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "ERROR: a criar: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "ERRO: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "ERROR: memória sobreposta!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"ERRO: múltiplas partições wbfs\n" +"apenas suportadas com IOS222/223-mload" + +msgid "ERROR: not enough free space!!" +msgstr "ERRO: não há espaço livre suficiente!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "ERRO: ntfs não foi desmontado correctamente" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "ERRO: a abrir %.6s" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "ERRO: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "ERRO: a configurar fragmentos %d %d" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "ERRO: a escrever %s (%d)." + +msgid "EXTEND" +msgstr "EXTENDIDO" + +msgid "Ejecting DVD..." +msgstr "A Ejectar o DVD..." + +msgid "English" +msgstr "Inglês" + +msgid "English Title" +msgstr "Título em Inglês" + +msgid "Enter Code: " +msgstr "Entrar Código: " + +msgid "Error GRRLIB init" +msgstr "Erro de inicialização GRRLIB" + +msgid "Error Initializing Network." +msgstr "Erro de inicialização Rede." + +msgid "Error adding disc!" +msgstr "Erro a adicionar disco!" + +msgid "Error creating directory..." +msgstr "Erro ao criar pasta..." + +msgid "Error discarding options!" +msgstr "Erro ao descartar opções!" + +msgid "Error downloading theme preview..." +msgstr "Erro ao transferir preview de tema..." + +msgid "Error downloading theme..." +msgstr "Erro ao transferir tema..." + +msgid "Error downloading themes..." +msgstr "Erro ao transferir temas..." + +msgid "Error downloading update..." +msgstr "Erro ao transferir actualização..." + +msgid "Error downloading updates..." +msgstr "Erro ao transferir actualizações..." + +msgid "Error downloading." +msgstr "Erro ao transferir." + +msgid "Error establishing connection" +msgstr "Erro ao estabelecer ligação" + +msgid "Error extracting theme..." +msgstr "Erro ao extrair tema..." + +msgid "Error opening database, update did not complete." +msgstr "" +"Erro ao abrir base de dados, \n" +"actualização não foi completada." + +#, c-format +msgid "Error opening: %s" +msgstr "Erro ao abrir: %s" + +#, c-format +msgid "Error playing %s" +msgstr "Erro ao jogar %s" + +msgid "Error reading .dol" +msgstr "Erro ao ler .dol" + +msgid "Error reading dol header" +msgstr "Erro ao ler o cabeçalho do dol" + +#, c-format +msgid "Error saving %s" +msgstr "Erro ao gravar %s" + +msgid "Error saving options!" +msgstr "Erro ao gravar opções!" + +msgid "Error saving settings!" +msgstr "Erro ao gravar configurações!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"Erro ao armazenar registo do jogo.\n" +"Inicie menu da Wii para corrigir." + +msgid "Error: Invalid PNG image!" +msgstr "Erro: Imagem PNG inválida!" + +msgid "Error: no URL." +msgstr "Erro: não há URL." + +msgid "Error: no data." +msgstr "Erro: não há dados." + +msgid "Exit" +msgstr "Sair" + +#, c-format +msgid "Extracting: %s" +msgstr "A extrair: %s" + +msgid "FAIL" +msgstr "FALHA" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: ERRO DE ALOCAÇÃO %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: alloc grid(%d)" + +#, c-format +msgid "FILL %d" +msgstr "PREENCHER %d" + +msgid "FLAT cover" +msgstr "Capa PLANA" + +msgid "FULL cover" +msgstr "Capa COMPLETA" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Off" + +msgid "Fav: On" +msgstr "Fav: On" + +msgid "Favorite" +msgstr "Favorito" + +msgid "Favorite Games" +msgstr "Jogos Favoritos" + +msgid "Favorite:" +msgstr "Favorito:" + +msgid "Favorites:" +msgstr "Favoritos:" + +msgid "Fighting" +msgstr "Luta" + +#, c-format +msgid "File not found! %s" +msgstr "Ficheiro não encontrado! %s" + +msgid "Filter" +msgstr "Filtro" + +msgid "Filter Games" +msgstr "Filtrar Jogos" + +msgid "Filter by Controller" +msgstr "Filtrar por Comando" + +msgid "Filter by Genre" +msgstr "Filtrar por Género" + +msgid "Filter by Online Features" +msgstr "Filtrar por funcionalidades Online" + +msgid "Filter:" +msgstr "Filtro:" + +msgid "Fixing EXTEND partition..." +msgstr "A corrigir partição EXTENDIDA..." + +msgid "Force NTSC" +msgstr "Forçar NTSC" + +msgid "Force PAL50" +msgstr "Forçar PAL50" + +msgid "Force PAL60" +msgstr "Forçar PAL60" + +msgid "Formatting" +msgstr "A formatar" + +#, c-format +msgid "Found %s" +msgstr "Encontrada %s" + +msgid "French" +msgstr "Francês" + +msgid "Full" +msgstr "Completa" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Jogo por Defeito" + +msgid "Game Options" +msgstr "Opções de Jogo" + +#, c-format +msgid "Game Options: %s" +msgstr "Opções de Jogo: %s" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Gamer Card:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d reportado: %.*s" + +msgid "Genre" +msgstr "Género" + +msgid "German" +msgstr "Alemão" + +msgid "Global Options" +msgstr "Opções Globais" + +msgid "Guitar" +msgstr "Guitarra" + +msgid "HQ cover" +msgstr "Capa HQ" + +msgid "HTTP Response was without a file" +msgstr "Resposta HTTP foi sem um ficheiro" + +msgid "Hide" +msgstr "Esconder" + +msgid "Hide Game:" +msgstr "Ocultar Jogo:" + +msgid "Hold button B to cancel." +msgstr "Carregue no botão B para cancelar." + +msgid "Homebrew Channel" +msgstr "Homebrew Channel" + +msgid "Hook Type:" +msgstr "Tipo de Hook:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID desajustada: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "Recarregamento IOS: Bloqueado" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) falhou com código %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Se o cfg não iniciar correctamente\n" +"da próxima vez, copie o boot.dol.bak\n" +"por cima do boot.dol e tente novamente." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "Inconsistência na codificação LZ77 %x" + +msgid "Info" +msgstr "Info" + +#, c-format +msgid "Info file: %s" +msgstr "Informação ficheiro: %s" + +msgid "Initializing Network..." +msgstr "A inicializar Rede..." + +msgid "Install" +msgstr "Instalar" + +msgid "Install Date" +msgstr "Data de Instalação" + +msgid "Install Game" +msgstr "Instalar Jogo" + +msgid "Install game" +msgstr "Instalar jogo" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "ERRO de Instalação! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "A instalar jogo, por favor aguarde..." + +msgid "Invalid .dol" +msgstr ".dol inválido" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Partição inválida: '%s'" + +msgid "Italian" +msgstr "Italiano" + +msgid "Japanese" +msgstr "Japonês" + +msgid "Japanese Title" +msgstr "Título Japonês" + +msgid "Keyboard" +msgstr "Teclado" + +msgid "Korean" +msgstr "Coreano" + +msgid "LEFT" +msgstr "ESQUERDA" + +msgid "LOCKED!" +msgstr "BLOQUEADO!" + +msgid "Language:" +msgstr "Idioma:" + +msgid "Last Play Date" +msgstr "Data do Último Jogo" + +msgid "Launch Methods" +msgstr "Métodos de inicialização" + +msgid "Load OK!" +msgstr "Carregamento OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Versão do Loader: %s" + +msgid "Loading ..." +msgstr "A carregar..." + +msgid "Main" +msgstr "Principal" + +msgid "Main Menu" +msgstr "Menu Principal" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"Garanta que a porta USB 0 é a\n" +"usada! (A mais próxima da extremidade)" + +msgid "Manage" +msgstr "Gerir" + +msgid "Manage Cheats" +msgstr "Gerir Batotas" + +msgid "Microphone" +msgstr "Microfone" + +msgid "Motion+" +msgstr "MotionPlus" + +msgid "Mounting device, please wait..." +msgstr "" +"A montar dispositivo.\n" +"Por favor, aguarde..." + +msgid "Music" +msgstr "Música" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Ficheiro de música da pasta: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Tamanho do ficheiro de música: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Ficheiro de música: %s" + +msgid "Music: Disabled" +msgstr "Música: Desactivada" + +msgid "Music: Enabled" +msgstr "Música: Activada" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Música: A pesquisar por %s ficheiros em: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Música: Próximo ficheiro indexado para tocar: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Música: Número de %s ficheiros encontrados: %i" + +msgid "Music: musicArray contents: " +msgstr "Música: conteúdos de musicArray: " + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOTA: CIOS249 antes da rev10:\n" +"Não suporta carregar jogos de SDHC!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOTA: CIOS249 antes da rev14:\n" +"Possível erro #001 não é tratado!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOTA: CIOS249 antes da rev9:\n" +"Não suporta carregar jogos por USB!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOTA: Você está a usar o CIOS249 rev13:\n" +"Para sair do jogo deve fazer reset ou\n" +"desligar a Wii antes de começar\n" +"outro jogo ou vai bloquear aqui!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"NOTA: Você está a usar o CIOS249 rev14:\n" +"Sabe-se que este tem problemas com jogos\n" +"de dupla camada. É altamente recomendável\n" +"que use IOS diferente ou uma versão diferente\n" +"para instalação/reprodução deste jogo." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOTA: É necessário reiniciar o loader\n" +"para que a actualização tenha efeito." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOTA: A partição P#%d é do tipo EXTENDIDA, mas\n" +"contém um sistema de ficheiros WBFS. Isto é uma\n" +"configuração inválida. Pressione %s para mudar o\n" +"tipo de partição de EXTENDIDA para Dados." + +msgid "NTFS compression not supported!" +msgstr "Compressão NTFS não suportada!" + +msgid "NTFS encryption not supported!" +msgstr "Encriptação NTFS não suportada!" + +msgid "Network connection established." +msgstr "Ligação de rede estabelecida." + +msgid "Network error. Can't update gamercards." +msgstr "Erro de rede. Não é possível actualizar os gamercards." + +msgid "New Themes" +msgstr "Temas Novos" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Conectividade com Nintendo DS" + +msgid "No" +msgstr "Não" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Nenhuma parte do domínio no URL '%s'" + +msgid "No games found!" +msgstr "Nenhum jogo encontrado!" + +msgid "No partition selected!" +msgstr "Não há partições selecionadas!" + +msgid "No themes found." +msgstr "Nenhum tema encontrado." + +msgid "No updates found." +msgstr "Nenhuma actualização encontrada." + +msgid "None found on disc" +msgstr "Nada encontrado no disco" + +msgid "Not Found!" +msgstr "Não encontrado!" + +msgid "Number of Online Players" +msgstr "Nº de Jogadores Online" + +msgid "Number of Players" +msgstr "Nº de Jogadores" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (batota):" + +msgid "Ocarina Cheat Manager" +msgstr "Gestor Batota Ocarina" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Erro no Código" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Códigos encontrados." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Nenhum código encontrado" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Erro por Falta de memória" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: A pesquisar códigos..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Demasiados códigos" + +msgid "Off" +msgstr "Off" + +msgid "On" +msgstr "On" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Conteúdo Online" + +msgid "Online Play" +msgstr "Jogo Online" + +msgid "Online Players" +msgstr "Jogadores Online" + +msgid "Online Score List" +msgstr "Lista de Pontuação Online" + +msgid "Online Updates" +msgstr "Actualizações online" + +msgid "Open Sort" +msgstr "Ordenação inicial" + +msgid "Open Style" +msgstr "Estilo Ordenação" + +msgid "Opening DVD disc..." +msgstr "A abrir disco DVD..." + +#, c-format +msgid "Opening partition: %s" +msgstr "A abrir partição: %s" + +msgid "Options" +msgstr "Opções" + +msgid "Options discarded for this game." +msgstr "Opções descartadas para este jogo." + +msgid "Options saved for this game." +msgstr "Opções gravadas para este jogo." + +msgid "Out of memory" +msgstr "Sem memória" + +msgid "Page:" +msgstr "Pág.:" + +msgid "Partition:" +msgstr "Partição:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Partição: %s não encontrada!" + +msgid "Party" +msgstr "Festa" + +msgid "Paused Start" +msgstr "Início pausado" + +msgid "Platformer" +msgstr "Plataformas" + +msgid "Play Count" +msgstr "Contador de Jogo" + +msgid "Players" +msgstr "Jogadores" + +msgid "Please insert a game disc..." +msgstr "Por favor insira um disco de jogo..." + +#, c-format +msgid "Press %s button for options." +msgstr "Prima o botão %s para opções." + +#, c-format +msgid "Press %s button to %s." +msgstr "Prima o botão %s para %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Prima o botão %s para aplicar códigos." + +#, c-format +msgid "Press %s button to cancel." +msgstr "Prima o botão %s para cancelar." + +#, c-format +msgid "Press %s button to change device." +msgstr "" +"Prima o botão %s para mudar\n" +"de dispositivo." + +#, c-format +msgid "Press %s button to continue." +msgstr "Prima o botão %s para continuar." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Prima o botão %s para apagar FS." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "Prima o botão %s para extrair BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "Prima o botão %s para sair." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "" +"Prima o botão %s para\n" +"corrigir EXTEND/WBFS." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "Prima o botão %s para formatar WBFS." + +#, c-format +msgid "Press %s button to go back." +msgstr "Prima o botão %s para voltar atrás." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "" +"Prima o botão %s para\n" +"seleccionar uma partição." + +#, c-format +msgid "Press %s button to select." +msgstr "Prima o botão %s para seleccionar." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Prima o botão %s para saltar códigos." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "Prima o botão %s para sincronizar FAT." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Prima %s para preview de ecrã completo" + +#, c-format +msgid "Press %s for game options" +msgstr "Prima %s para opções de jogo" + +#, c-format +msgid "Press %s for global options" +msgstr "Prima %s para opções globais" + +#, c-format +msgid "Press %s to discard options" +msgstr "Prima %s para descartar opções" + +#, c-format +msgid "Press %s to download and update" +msgstr "Prima %s para transferir e actualizar" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Prima %s para transferir este tema" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "Prima %s para transferir, %s para voltar" + +#, c-format +msgid "Press %s to return" +msgstr "Prima %s para voltar" + +#, c-format +msgid "Press %s to save global settings" +msgstr "Prima %s para gravar config. globais" + +#, c-format +msgid "Press %s to save options" +msgstr "Prima %s para gravar opções" + +#, c-format +msgid "Press %s to save selection" +msgstr "Prima %s para gravar selecção" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Prima %s para seleccionar filtro" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "" +"Prima %s para seleccionar\n" +"critério de ordenação" + +#, c-format +msgid "Press %s to start game" +msgstr "Prima %s para iniciar o jogo" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "Prima %s para actualizar sem meta.xml" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "Prima %s/%s para selecionar dispositivo." + +msgid "Press 2 to reload IOS" +msgstr "Prima 2 para recarregar IOS" + +msgid "Press A to select device" +msgstr "Prima A para seleccionar dispositivo" + +msgid "Press B to exit to HBC" +msgstr "Prima B para sair para o HBC" + +msgid "Press HOME to reset" +msgstr "Prima HOME para reset" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "Prima DIR./ESQ. para seleccionar IOS" + +msgid "Press any button to continue..." +msgstr "Prima qualquer botão para continuar..." + +msgid "Press any button to exit..." +msgstr "Prima qualquer botão para sair..." + +msgid "Press any button to restart..." +msgstr "Prima qualquer botão para reiniciar..." + +msgid "Press any button..." +msgstr "Prima qualquer botão..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Prima botão %s para transferir capas." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "Prima o botão %s para ejectar o DVD." + +msgid "Profile:" +msgstr "Perfil:" + +#, c-format +msgid "Profile: %s" +msgstr "Perfil: %s" + +msgid "Program Updates" +msgstr "Actualizações Programa" + +msgid "Publisher" +msgstr "Editor" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Abandonar" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "DIREITA" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Corridas" + +#, c-format +msgid "Rated %s" +msgstr "Classificação %s" + +msgid "Rating" +msgstr "Aval." + +msgid "Read" +msgstr "Ler" + +msgid "Reading BCA..." +msgstr "A ler BCA..." + +msgid "Reboot" +msgstr "Reiniciar" + +msgid "Release Date" +msgstr "Data de Publicação" + +msgid "Release Notes: (short)" +msgstr "Notas de Publicação: (curtas)" + +msgid "Removing game, please wait..." +msgstr "A remover jogo, por favor aguarde..." + +msgid "Restarting Wii..." +msgstr "A reiniciar a Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Returned! (ret = %d)" + +msgid "Rhythm" +msgstr "Rhythm" + +msgid "Rows:" +msgstr "Linhas:" + +msgid "Running benchmark, please wait" +msgstr "A correr benchmark, por favor aguarde" + +msgid "S. Chinese" +msgstr "Chinês S." + +msgid "SD/SDHC Card" +msgstr "Cartão SD/SDHC" + +msgid "SUCCESS!" +msgstr "SUCESSO!" + +msgid "Save .gct" +msgstr "Gravar .gct" + +msgid "Save Debug" +msgstr "Gravar Debug" + +msgid "Save Settings" +msgstr "Gravar Config." + +msgid "Save debug.log" +msgstr "Gravar debug.log" + +msgid "Saving Settings... " +msgstr "A gravar Configurações..." + +msgid "Saving cheats..." +msgstr "A gravar batotas..." + +msgid "Saving gamelist.txt ... " +msgstr "A gravar gamelist.txt..." + +msgid "Saving settings..." +msgstr "A gravar configurações..." + +msgid "Saving:" +msgstr "A gravar:" + +#, c-format +msgid "Saving: %s" +msgstr "A gravar: %s" + +msgid "Scroll:" +msgstr "Scroll:" + +msgid "Select Alternative .dol:" +msgstr "Seleccionar .dol Alternativo:" + +msgid "Select WBFS device:" +msgstr "Seleccionar dispositivo WBFS:" + +msgid "Select a different partition" +msgstr "Selecionar uma partição diferente" + +msgid "Select a partition" +msgstr "Selecionar uma partição" + +msgid "Select all" +msgstr "Seleccionar tudo" + +msgid "Select the game you want to boot" +msgstr "Seleccionar o jogo que deseja iniciar" + +msgid "Selected Game" +msgstr "Jogo Seleccionado" + +msgid "Settings" +msgstr "Configurações" + +msgid "Shooter" +msgstr "Tiros" + +msgid "Show All" +msgstr "Mostrar tudo" + +msgid "Show cIOS info" +msgstr "Mostrar info cIOS" + +msgid "Shutdown" +msgstr "Encerrar" + +msgid "Side:" +msgstr "Lado:" + +msgid "Simulation" +msgstr "Simulação" + +msgid "Size(GB) Type Mount Used" +msgstr "Tam.(GB) Tipo Em uso" + +#, c-format +msgid "Size: %d bytes" +msgstr "Tamanho: %d bytes" + +msgid "Sort" +msgstr "Ordenação" + +msgid "Sort Games" +msgstr "Ordenar Jogos" + +msgid "Sort Order:" +msgstr "Ordenação:" + +msgid "Sort Type:" +msgstr "Tipo Ordenação:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Ordenação: %s-%s" + +msgid "Spanish" +msgstr "Espanhol" + +msgid "Sports" +msgstr "Desportos" + +msgid "Start" +msgstr "Iniciar" + +msgid "Start Game" +msgstr "Iniciar Jogo" + +msgid "Start this game?" +msgstr "Iniciar este jogo?" + +msgid "Stopping DVD..." +msgstr "A parar DVD..." + +msgid "Strategy" +msgstr "Estratégia" + +msgid "Style" +msgstr "Estilo" + +msgid "Style:" +msgstr "Estilo:" + +msgid "Sync FAT free space info?" +msgstr "Sincronizar info de espaço livre FAT?" + +msgid "Synchronizing, please wait." +msgstr "A sincronizar, por favor aguarde." + +msgid "System" +msgstr "Sistema" + +msgid "System Def." +msgstr "Def. Sistema" + +msgid "T. Chinese" +msgstr "Chinês T." + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Info Tema" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Tema pode ser demasiado grande.\n" +"Recomendado reiniciar o Cfg.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "Temas fornecidos por wii.spiffy360.com" + +msgid "Themes to download" +msgstr "Temas para transferir" + +msgid "Themes with updates" +msgstr "Temas com actualizações" + +msgid "Title" +msgstr "Título" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Demasiadas batotas! (%d)" + +msgid "Too many code lines!" +msgstr "Demasiadas linhas de código!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Demasiados fragmentos! %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "A tentar (url#%d)..." + +msgid "UNUSED" +msgstr "NÃO EM USO" + +msgid "UP" +msgstr "CIMA" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' não começa por 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' não tem parte do endereço" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko encontrado. Depuração activada." + +msgid "USB Mass Storage Device" +msgstr "Dispositivo de Armazenamento USB" + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Unknown syntax!" +msgstr "Síntaxe desconhecida!" + +msgid "Unplayed" +msgstr "Não jogado" + +msgid "Unplayed Games" +msgstr "Jogos não jogados" + +msgid "Update WiiTDB Game Database" +msgstr "Atualizar Base Dados de Jogos WiiTDB" + +msgid "Update themes" +msgstr "Actualizar temas" + +msgid "Update titles.txt" +msgstr "Actualizar titles.txt" + +msgid "Updates" +msgstr "Actualizações" + +msgid "Updating database." +msgstr "A actualizar base de dados." + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Usado: %.1fGB Livre: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "A usar .dol Alternativo Gravado:" + +msgid "Version" +msgstr "Versão" + +msgid "Video Patch:" +msgstr "Patch de Vídeo:" + +msgid "Video:" +msgstr "Vídeo:" + +msgid "View" +msgstr "Vista" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor" + +msgid "WARNING:" +msgstr "AVISO:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB livre de %.1fGB" + +msgid "Wheel" +msgstr "Volante" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiTDB Game Database" +msgstr "Base Dados de Jogos WiiTDB" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Write Playlog:" +msgstr "Escrever Reg.Jogo:" + +#, c-format +msgid "Writing to %s" +msgstr "A escrever para %s" + +#, c-format +msgid "Writing: %s" +msgstr "A escrever: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Tamanho Errado: %d (%d)" + +msgid "Yes" +msgstr "Sim" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Pode tentar desligar e voltar\n" +"a ligar o dispositivo, ou\n" +"apenas esperar mais um pouco" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ ALTERADAS ]" + +msgid "[ FOUND ]" +msgstr "[ ENCONTRADA ]" + +msgid "[ SAVED ]" +msgstr "[ GRAVADAS ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[SEM HQ]" + +msgid "[USED]" +msgstr "[EM USO]" + +msgid "[default]" +msgstr "[inicial]" + +msgid "[saved]" +msgstr "[gravado/a]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "ficheiro wbfs mau: %s" + +msgid "calculating space, please wait..." +msgstr "A calcular espaço, por favor aguarde..." + +msgid "delete" +msgstr "apagar" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "descartar" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "domínio %s não pôde ser resolvido" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "erro a ler: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "p/ %d jog." + +msgid "for 1 player" +msgstr "p/ 1 jog." + +msgid "format" +msgstr "formato" + +msgid "free" +msgstr "livre" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "velocidade linear de leitura: %.2f mb/s" + +msgid "linear..." +msgstr "linear..." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "ficheiro de música muito grande (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 ou music.mod não encontrados!" + +msgid "no file" +msgstr "nenhum ficheiro" + +msgid "none" +msgstr "nenhum" + +msgid "or format a WBFS partition." +msgstr "ou formatar uma partição WBFS" + +msgid "page" +msgstr "página" + +msgid "parse error" +msgstr "erro de análise" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "velocidade de leitura aleatória: %.2f mb/s" + +msgid "random..." +msgstr "aleatório..." + +msgid "reset" +msgstr "reset" + +msgid "revert" +msgstr "reverter" + +msgid "save" +msgstr "gravar" + +#, c-format +msgid "split error: %s" +msgstr "erro de divisão: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "impossível abrir o disco da wii" + +#, c-format +msgid "used: %p - %p" +msgstr "usado: %p - %p" + +#~ msgid "Front" +#~ msgstr "Frente" + +#~ msgid "Loading previous game list..." +#~ msgstr "A carregar lista anterior de jogos..." + +#~ msgid "Remove Game" +#~ msgstr "Remover Jogo" diff --git a/Languages/TR.lang b/Languages/TR.lang new file mode 100644 index 0000000..12f626c --- /dev/null +++ b/Languages/TR.lang @@ -0,0 +1,2451 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-03-01 22:57+0100\n" +"Last-Translator: Wiidewii\n" +"Language-Team: TURKISH \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB boÅŸ %.1fGB tamamı" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% aktarıldı, tamamı %.2fGB (%c) TKZ: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB kopyalandı %d:%02d:%02d sürdü" + +#, c-format +msgid "%d available" +msgstr "%d var" + +#, c-format +msgid "%d more notes" +msgstr "%d daha fazla not" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s Ayrım: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, Lütfen bekleyin..." + +#, c-format +msgid "%s: Favorites" +msgstr "%s: Favoriler" + +#, c-format +msgid "%s: GUI " +msgstr "%s: GUI " + +#, c-format +msgid "%s: Options " +msgstr "%s: Seçenekler " + +#, c-format +msgid "(%d online)" +msgstr "(%d online)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d saniye zaman aşımı)" + +msgid "(This can take a couple of minutes)" +msgstr "Bu iÅŸlem birkaç dakika alabilir" + +msgid ".dol too small" +msgstr ".dol çok küçük" + +msgid "1.2+" +msgstr "1.2+" + +msgid "1st-Person Shooter" +msgstr "1st-Person Shooter" + +msgid "2.0+" +msgstr "2.0+" + +msgid "2.1+" +msgstr "2.1+" + +msgid "2.2+" +msgstr "2.2+" + +msgid "3D cover" +msgstr "3D Kapak" + +msgid "3rd-person Shooter" +msgstr "3rd-person Shooter" + +msgid "< ASC >" +msgstr "< ARTAN >" + +msgid "< DESC >" +msgstr "" + +msgid "< DOWNLOAD >" +msgstr "< Ä°NDÄ°R >" + +msgid "About" +msgstr "Hakkında" + +msgid "Action" +msgstr "Ä°ÅŸlem" + +msgid "Additional config:" +msgstr "Ek Yapılandırma:" + +msgid "Admin Lock:" +msgstr "Yönetici Kilidi" + +msgid "Admin Unlock" +msgstr "Yönetici Kilidi Aç" + +msgid "Adult" +msgstr "YetiÅŸkin" + +msgid "Adventure" +msgstr "Macera" + +msgid "All" +msgstr "Hepsi" + +msgid "All Channels" +msgstr "Bütün Kanallar" + +msgid "All Games" +msgstr "Tüm Oyunlar" + +msgid "All themes up to date." +msgstr "Bugüne kadar bütün temalar" + +msgid "Alt Button Cfg:" +msgstr "Alt TuÅŸ Ayarı" + +msgid "Alt dol:" +msgstr "Alt dol:" + +msgid "Alternative .dol:" +msgstr "Alternatif .dol:" + +msgid "Anti 002 Fix:" +msgstr "Anti 002 Fix:" + +#, c-format +msgid "App. Path: %s" +msgstr "Uygulama Yolu: %s" + +msgid "Arcade" +msgstr "Arcade" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"Bu bölümü %s istediÄŸinden\n" +" emin misin?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"Bu bölümü FIX etmek istediÄŸinden\n" +" emin misin?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" +"Bu oyunu silmek istediÄŸinden \n" +" emin misin?" + +msgid "Ascending" +msgstr "Artan" + +msgid "Auto" +msgstr "Otomatik" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "DoÄŸrudan oyun: %.6s bulunamadı!" + +msgid "Available Updates" +msgstr "Kullanılabilir güncelleÅŸtirmeler" + +msgid "Back" +msgstr "Geri" + +msgid "Balance Board" +msgstr "Denge Tahtası" + +msgid "Baseball" +msgstr "Beysbol" + +msgid "Basic" +msgstr "Basit" + +msgid "Basketball" +msgstr "Basketbol" + +msgid "Bike Racing" +msgstr "Bisiklet Yarışı" + +msgid "Billiards" +msgstr "Bilardo" + +msgid "Block IOS Reload:" +msgstr "Block IOS Reload:" + +msgid "Board Game" +msgstr "Board Game" + +msgid "Boot Disc" +msgstr "Diski çalıştır" + +msgid "Boot disc" +msgstr "Boot Disk" + +msgid "Booting game, please wait..." +msgstr "Oyun yükleniyor, lütfen beleyin" + +msgid "Bowling" +msgstr "Bovling" + +msgid "Boxing" +msgstr "Boks" + +msgid "Business Sim" +msgstr "Ä°ÅŸ Simülasyonu" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG kök dizini: %s" + +msgid "Camera" +msgstr "kamera" + +msgid "Can not dump GC savegames.\n" +msgstr "GC oyun savelerini ekleyemiyorum" + +msgid "Can't install Wii games!" +msgstr "Wii oyunları kurulamıyor!" + +msgid "Cancelled." +msgstr "Ä°ptal." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "Dizin oluÅŸturulamıyor: %s" + +msgid "Cards" +msgstr "Kart" + +msgid "Channel" +msgstr "Kanal" + +msgid "Cheat Codes:" +msgstr "Hile Kodları:" + +msgid "Cheats: " +msgstr "Hileler: " + +msgid "Check For Updates" +msgstr "Güncellemeler için kontrol" + +msgid "Checking for themes..." +msgstr "" + +msgid "Checking for updates..." +msgstr "Güncellemeler kontrol ediliyor..." + +msgid "Chess" +msgstr "Satranç" + +msgid "Choose a sorting method" +msgstr "Sıralama ÅŸekli seçin" + +msgid "Classic" +msgstr "Klasik" + +msgid "Classic Controller" +msgstr "Klasik kumanda" + +msgid "Clear" +msgstr "Temiz" + +msgid "Clear Patches:" +msgstr "Temiz Yamalar" + +msgid "Coaching" +msgstr "Antrenörlük" + +msgid "Compare Type" +msgstr "Tip KarşılaÅŸtırın" + +msgid "Compilation" +msgstr "Derleme" + +#, c-format +msgid "Complete. Size: %d" +msgstr "Tam boyut: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "Yapılandırma hatası, liste boÅŸ iken MAX_DNS_ENTRIES deÄŸerine ulaşıldı ." + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "net_read() Hata kodu: %i yüzünden baÄŸlantı hatası" + +msgid "Console" +msgstr "Konsol" + +msgid "Construction Sim" +msgstr "Ä°nÅŸaat Simülasyonu" + +msgid "Contains" +msgstr "İçeriyor" + +msgid "Controller" +msgstr "Kontroller" + +msgid "Cooking" +msgstr "Yemek PiÅŸirme" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "DIP Modülü (ret= %d) baÅŸlatılamadı!" + +msgid "Country Fix:" +msgstr "Ãœlke ayarı:" + +msgid "Cover" +msgstr "Kapak" + +msgid "Cover Image:" +msgstr "Kapak Resmi:" + +msgid "Cover Style" +msgstr "kapak Sitili" + +msgid "Covers Available" +msgstr "Mevcut Kapaklar" + +msgid "Cover~~Back" +msgstr "Arka~~Kapak" + +msgid "Cover~~Front" +msgstr "Ön~~Kapak" + +msgid "Create" +msgstr "OluÅŸtur" + +msgid "Creator" +msgstr "yaratıcı" + +msgid "Cricket" +msgstr "Kriket" + +#, c-format +msgid "Current Version: %s" +msgstr "Geçerli sürüm: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"Custom IOS %d bulunamadı!\n" +"Lütfen kurun." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "CIOS %d yüklenemedi (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"Custom IOS %d çöp!\n" +"Lütfen tekrar kurun." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "CIOS %s yüklemesi OK" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "DISC Kapak" + +msgid "DOWN" +msgstr "AÅžAÄžI" + +msgid "Dance" +msgstr "Dans" + +msgid "Dance Pad" +msgstr "Dans bezi" + +msgid "Darts" +msgstr "Dart" + +msgid "Database update successful." +msgstr "Veritabanı baÅŸarıyla güncellendi." + +msgid "Debug" +msgstr "Hata Ayıklama" + +msgid "Delete Game" +msgstr "Oyunu Sil" + +msgid "Deleting" +msgstr "Siliniyor" + +msgid "Descending" +msgstr "Ä°nme" + +msgid "Developer" +msgstr "GeliÅŸtirici" + +msgid "Device is not responding!" +msgstr "Aygıt cevap vermiyor" + +msgid "Device:" +msgstr "Aygıt:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Sadece temiz dökümler nakil ediliyor!" + +msgid "Devolution:" +msgstr "Nakil" + +msgid "Disc" +msgstr "Disk" + +msgid "Disc (Ask)" +msgstr "Disk (Sor)" + +msgid "Done." +msgstr "Tamam." + +msgid "Download .txt" +msgstr ".txt Ä°ndir" + +msgid "Download All Covers" +msgstr "Bütün Kapakları Ä°ndir" + +msgid "Download All Missing Covers" +msgstr "Bütün eksik kapakları indir" + +msgid "Download Missing Covers" +msgstr "Eksik Kapakları Ä°ndir" + +msgid "Download Plugins" +msgstr "Eklenti Yükle" + +msgid "Download Themes" +msgstr "Temaları Yükle" + +msgid "Download complete." +msgstr "Ä°ndirme tamamlandı." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "Gamercard #%d. indirme hatası" + +msgid "Download game information" +msgstr "Oyun bilgilerini indir" + +msgid "Downloadable Content" +msgstr "Ä°ndirilebilir içerik" + +msgid "Downloading ALL MISSING covers" +msgstr "BÃœTÃœN EKSÄ°K kapaklar indiriliyor" + +msgid "Downloading ALL covers" +msgstr "BÃœTÃœN kapaklar indiriliyor" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "BÃœTÃœN kapakların indirilmesi %.6s" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "EKSÄ°K kapakların indirilmesi %.6s" + +msgid "Downloading Theme previews..." +msgstr "Tema önzilemeleri indiriliyor..." + +msgid "Downloading cheats..." +msgstr "Hileler indiriliyor..." + +msgid "Downloading database." +msgstr "Veritabanı indiriliyor." + +msgid "Downloading devolution." +msgstr "" + +msgid "Downloading mighty plugin." +msgstr "Eklenti indiriliyor" + +msgid "Downloading neek2o plugin." +msgstr "neek2o eklentisi indiriliyor" + +msgid "Downloading titles.txt ..." +msgstr " titles.txt indiriliyor..." + +msgid "Downloading translation file." +msgstr "Dil dosyası indiriliyor" + +msgid "Downloading unifont." +msgstr "unifont indiriliyor" + +#, c-format +msgid "Downloading: %s" +msgstr "Ä°ndirme: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "Ä°ndiriliyor: %s as %s" + +msgid "Downloads" +msgstr "Ä°ndirmeler" + +msgid "Drawing" +msgstr "Çizim" + +msgid "Drums" +msgstr "Bateri" + +msgid "Dump savegame" +msgstr "Oyun kayıtlarını dök" + +msgid "Duplicate ID3" +msgstr "ID3 ÇoÄŸalt" + +msgid "Dutch" +msgstr "Hollandaca" + +msgid "ERROR" +msgstr "HATA" + +msgid "ERROR creating file" +msgstr "HATA : dosya oluÅŸturmada" + +msgid "ERROR game opt" +msgstr "HATA : oyun ayarları uygulanırken" + +msgid "ERROR reading BCA!" +msgstr "HATA : BCA okunamadı!" + +#, c-format +msgid "ERROR removing %s" +msgstr "HATA : silereken %s" + +msgid "ERROR writing BCA!" +msgstr "HATA : BCA yazılamadı!" + +msgid "ERROR!" +msgstr "HATA!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "HATA! (ret = %d)" + +msgid "ERROR:" +msgstr "HATA:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "HATA: %s eriÅŸilemez" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "HATA: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "HATA: BCA için CIOS222/223 v4 gerekli " + +msgid "ERROR: Cache Close" +msgstr "HATA: Önbellek kapatılırken" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "HATA: Oyun açılamıyor! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "HATA: Oyun zaten yüklenmiÅŸ!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "HATA: GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "HATA: Geçersiz Oyun-ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "HATA: EHC modülü yüklenirken! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"HATA: NTFS yazımı kapalı!\n" +"(set ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "HATA : Bölüm/Partition bulunamadı! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "HATA: Wii Diski deÄŸil!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "HATA: Ofset (0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "HATA: OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "HATA: OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "HATA: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "HATA: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "HATA: SD moduna geçmede" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "HATA: USB baÅŸlatmada! (%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "HATA: WBFS mount edilemedi" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"cIOS223v4, cIOS224v5 veya daha üstü gerekiyor\n" +"IOS249'u güncelleyin veya farklı bir IOS seçin." + +msgid "ERROR: cache: out of memory" +msgstr "HATA: Önbellek: Bellek yetersiz" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "HATA: oluÅŸturuluyor: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "HATA: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "HATA: Hafıza örtüşmesi!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"HATA: Çoklu WBFS bölümü\n" +"sadece CIOS222/223-mload ile destekleniyor" + +msgid "ERROR: not enough free space!!" +msgstr "HATA: Yeterli yer yok!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "HATAÄ° ntfs unmount edilemedi" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "HATA: %.6s açılırken" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "HATA: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "HATA: parçalar ayarlanıyor %d %d" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "HATA: %s/ Sürücü veya bölüm baÄŸlanamadı!!" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "HATA: yazma esnasında %s (%d)." + +msgid "EXTEND" +msgstr "GENÄ°ÅžLET" + +msgid "Educational" +msgstr "EÄŸitici" + +msgid "Ejecting DVD..." +msgstr "DVD çıkarılıyor..." + +msgid "English" +msgstr "Ä°ngilizce" + +msgid "English Title" +msgstr "Ä°ngilizce BaÅŸlık" + +msgid "Enter Code: " +msgstr "Kod’u girin: " + +msgid "Error GRRLIB init" +msgstr "GRRLIB baÅŸlatılamadı" + +msgid "Error Initializing Network." +msgstr "AÄŸ baÅŸlatma hatası." + +msgid "Error adding disc!" +msgstr "Disk ekleme hatası!" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "Tampon ayırma hatası: %d" + +msgid "Error creating directory..." +msgstr "Dizin oluÅŸturma hatası" + +msgid "Error discarding options!" +msgstr "Ayar iptali hatası!" + +msgid "Error downloading theme preview..." +msgstr "Tema önizlemelerini indirme hatası..." + +msgid "Error downloading theme..." +msgstr "Tema indirme hatası..." + +msgid "Error downloading themes..." +msgstr "Temaları indirme hatası..." + +msgid "Error downloading update..." +msgstr "Güncelleme indirme hatası..." + +msgid "Error downloading updates..." +msgstr "Güncellemeleri indirme hatası..." + +msgid "Error downloading." +msgstr "Ä°ndirme hatası." + +msgid "Error establishing connection" +msgstr "BaÄŸlantı kurma hatası" + +msgid "Error extracting theme..." +msgstr "Tema ayıklama hatası..." + +msgid "Error opening database, update did not complete." +msgstr "Veritabanı açılırken hata ile karşılaşıldı, güncelleme tamamlanamadı." + +#, c-format +msgid "Error opening: %s" +msgstr "%s açılırken hata alındı" + +#, c-format +msgid "Error playing %s" +msgstr "%s oynatırken hata" + +msgid "Error reading .dol" +msgstr ".dol okunamadı" + +msgid "Error reading dol header" +msgstr "dol baÅŸlığı okunamadı!" + +#, c-format +msgid "Error saving %s" +msgstr "Kaydederken hata alındı %s" + +msgid "Error saving options!" +msgstr "Ayarlar kaydedilirken hata alındı!" + +msgid "Error saving settings!" +msgstr "Seçenekler kaydedilirken hata alındı!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"HATA, oyun günlüğü kaydedilemedi \n" +" Düzeltmek için Wii Menüye dönüp tekrar baÅŸlatın.." + +msgid "Error: Invalid PNG image!" +msgstr "HATA: Geçersiz PNG Dosyası!" + +msgid "Error: no URL." +msgstr "HATA: URL yok." + +msgid "Error: no data." +msgstr "HATA: Veri yok." + +msgid "Exercise" +msgstr "Egzersiz" + +msgid "Exit" +msgstr "Çıkış" + +#, c-format +msgid "Extracting: %s" +msgstr "Ayıklanıyor: %s" + +msgid "FAIL" +msgstr "BaÅŸarısız" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: Yer ayırma hatası %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "FATAL: tahsis ızgarası(%d)" + +#, c-format +msgid "FILL %d" +msgstr "Dolduruluyor %d" + +msgid "FLAT cover" +msgstr "FLAT Kapak" + +msgid "FULL cover" +msgstr "FULL Kapak" + +msgid "Fav" +msgstr "Fav" + +msgid "Fav: Off" +msgstr "Fav: Off" + +msgid "Fav: On" +msgstr "Fav: On" + +msgid "Favorite" +msgstr "Favori" + +msgid "Favorite Games" +msgstr "Favori oyunlar" + +msgid "Favorite:" +msgstr "Favori:" + +msgid "Favorites:" +msgstr "Favoriler" + +msgid "Fighting" +msgstr "Dövüş" + +#, c-format +msgid "File not found! %s" +msgstr "Dosya bulunamadı! %s" + +msgid "Filter" +msgstr "Filtre" + +msgid "Filter Games" +msgstr "Oyunları Filtrele" + +msgid "Filter by Controller" +msgstr "Kumanda Filtresi" + +msgid "Filter by Game Type" +msgstr "Oyun Tipine göre Filtrele" + +msgid "Filter by Genre" +msgstr "Tür/çeÅŸit filtresi" + +msgid "Filter by Online Features" +msgstr "Online Özellikler'e göre Filtre" + +msgid "Filter:" +msgstr "Filtre" + +msgid "Fishing" +msgstr "Balık Tutma" + +msgid "Fitness" +msgstr "Fitness" + +msgid "Fixing EXTEND partition..." +msgstr "GeniÅŸletilmiÅŸ bölüm/partition düzeltiliyor..." + +msgid "Flight Sim" +msgstr "UçuÅŸ Simülasyonu" + +msgid "Football" +msgstr "Futbol" + +msgid "Force Devolution:" +msgstr "Force Devolution:" + +msgid "Force NTSC" +msgstr "NTSC zorla" + +msgid "Force NTSC 480p" +msgstr "NTSC 480p Zorla" + +msgid "Force PAL" +msgstr "PAL zorla" + +msgid "Force PAL 480p" +msgstr "PAL 480p zorla" + +msgid "Force PAL50" +msgstr "PAL50 zorla" + +msgid "Force PAL60" +msgstr "PAL60 zorla" + +msgid "Formatting" +msgstr "Biçimlendiriliyor" + +#, c-format +msgid "Found %s" +msgstr "Bulundu %s" + +msgid "French" +msgstr "Fransızca" + +msgid "Full" +msgstr "Full" + +msgid "Futuristic Racing" +msgstr "Futuristik Yarış" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "Oyun Varsayılanları" + +msgid "Game ID" +msgstr "Oyun ID" + +msgid "Game Options" +msgstr "Oyun Ayarları" + +#, c-format +msgid "Game Options: %s" +msgstr "Oyun Ayarları: %s" + +msgid "Game Type" +msgstr "Oyun Tipi" + +msgid "GameCube" +msgstr "GameCube" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "Oyuncu Kartı" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "OyuncuKartı #%d bildirildi: %.*s" + +msgid "Genre" +msgstr "Tür" + +msgid "German" +msgstr "Alman" + +msgid "Global Options" +msgstr "Genel Ayarlar" + +msgid "Golf" +msgstr "Golf" + +msgid "Guitar" +msgstr "Gitar" + +msgid "HQ cover" +msgstr "HQ Kapak" + +msgid "HTTP Response was without a file" +msgstr "HTTP karşılığı olan dosya yok" + +msgid "Health" +msgstr "SaÄŸlık" + +msgid "Hidden Object" +msgstr "Gizli Nesne" + +msgid "Hide" +msgstr "Gizli" + +msgid "Hide Game:" +msgstr "Oyun Gizle:" + +msgid "Hockey" +msgstr "Hokey" + +msgid "Hold button B to cancel." +msgstr "Ä°ptal için B tuÅŸuna basılı tutun." + +msgid "Home Menu" +msgstr "Home Menü" + +msgid "Homebrew Channel" +msgstr "Homebrew Channel" + +msgid "Hook Type:" +msgstr "Hook Tipi:" + +msgid "Hunting" +msgstr "Avcılık" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID UyumsuzluÄŸu: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS Reload: Engellendi" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS-Open(%s) %d koduyla baÅŸarısız oldu" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"Cfg düzgün baÅŸlatılamazsa\n" +"gelecek sefer, boot.dol.bak dosyasını\n" +"boot.dol olarak deÄŸiÅŸtirip tekrar deneyin" + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr " LZ77 kodlamasında tutarsızlık %x" + +msgid "Infinity Base" +msgstr "Sınırsız" + +msgid "Info" +msgstr "Bilgi" + +#, c-format +msgid "Info file: %s" +msgstr "Info dosyası: %s" + +msgid "Initializing Network..." +msgstr "AÄŸ baÅŸlatılıyor..." + +msgid "Install" +msgstr "Yükleme" + +msgid "Install Date" +msgstr "Yükleme Tarihi" + +msgid "Install Game" +msgstr "Oyun Yükleme" + +msgid "Install game" +msgstr "Oyun yükleme" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "Yükleme HATASI! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "Oyun kuruluyor, lütfen bekleyin..." + +msgid "Invalid .dol" +msgstr "Geçersiz .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "Geçersiz bölüm/partition '%s'" + +msgid "Italian" +msgstr "Ä°talyan" + +msgid "Japanese" +msgstr "Japon" + +msgid "Japanese Title" +msgstr "Japonca BaÅŸlık" + +msgid "Jump" +msgstr "Zıplama" + +msgid "Karaoke" +msgstr "Karaoke" + +msgid "Kart Racing" +msgstr "Kart Yarışı" + +msgid "Keyboard" +msgstr "Klavye" + +msgid "Korean" +msgstr "Kore" + +msgid "LED:" +msgstr "LED:" + +msgid "LEFT" +msgstr "SOL" + +msgid "LOCKED!" +msgstr "KÄ°LÄ°TLÄ°!" + +msgid "Language:" +msgstr "Dil: " + +msgid "Last Play Date" +msgstr "Son oynama tarihi" + +msgid "Launch Methods" +msgstr "Çalıştırma Yöntemleri" + +msgid "Life Simulation" +msgstr "YaÅŸam Simülasyonu" + +msgid "Load OK!" +msgstr "Yükleme OK!" + +#, c-format +msgid "Loader Version: %s" +msgstr "Yükleyici sürümü: %s" + +msgid "Loading ..." +msgstr "Yükleniyor ..." + +#, c-format +msgid "Loading..%s\n" +msgstr "Yükleniyor..%s\n" + +msgid "Main" +msgstr "Ana" + +msgid "Main Menu" +msgstr "Ana Menü" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"USB Port 0’ın kullanıldığından! emin olun\n" +" (Kenara yakın olan)" + +msgid "Manage" +msgstr "Yönet" + +msgid "Manage Cheats" +msgstr "Hileleri Yönet" + +msgid "Management Sim" +msgstr "Sim Yönetim" + +msgid "Martial Arts" +msgstr "Dövüş Sanatları" + +msgid "Microphone" +msgstr "Mikrofon" + +msgid "Mighty Plugin" +msgstr "Mighty Plugin" + +msgid "Motion+" +msgstr "Motion+" + +msgid "Motorcycle Racing" +msgstr "Motorsiklet Yarışı" + +msgid "Mounting device, please wait..." +msgstr "Aygıt baÄŸlanıyor, lütfen bekleyin.." + +msgid "Music" +msgstr "Müzik" + +#, c-format +msgid "Music file from dir: %s" +msgstr "Müzik dosyaları için klasör: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "Müzik dosyası boyutu: %d" + +#, c-format +msgid "Music file: %s" +msgstr "Müzik dosyası: %s" + +msgid "Music: Disabled" +msgstr "Müzik kapalı" + +msgid "Music: Enabled" +msgstr "Müzik açık" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "Müzik: %s içeriÄŸindeki dosyalara bakmak için: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "Müzik: Bir sonra çalacak parça: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "Müzik: %s sayısında müzik dosyası bulundu: %i" + +msgid "Music: musicArray contents: " +msgstr "Müzik: Kütüphane içeriÄŸi:" + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" +"NAND Emu Yolu:\n" +"%s\n" + +msgid "NAND Emu:" +msgstr "NAND Emu:" + +msgid "NMM:" +msgstr "NMM:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"NOT: SDHC’den oyun yüklenmesi \n" +" CIOS249 rev10: öncesinde desteklenmiyor!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"NOT: CIOS249 rev14: öncesinde\n" +" olası #001 HATASI!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"NOT: USB’den oyun yüklenmesi \n" +" CIOS249 rev9: öncesinde desteklenmiyor!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"NOT: CIOS249 Rev13 kullanıyorsanız:\n" +"BaÅŸka bir oyunu baÅŸlatmak için\n" +"Wii'yi resetlemeniz veya kapatıp açmalısınız!\n" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"CIOS249 rev14 kullanıyorsanız: \n" +" Dual layer oyunlarda problem olduÄŸu biliniyor.\n" +" Bu tür oyunlarda kurulum/çalıştırmak için\n" +"farklı IOS veya sürüm kullanılması önerilir" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"NOT: Güncellemelerin geçerli\n" +"olabilmesi için Restart etmeniz gerekmektedir." + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"Ayarların etkin olabilmesi için\n" +"Loader'in tekrar baÅŸlatılması gerekiyor" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"NOT: P#%d bölümü WBFS dosya sistemini\n" +"içeren GENÄ°ÅžLETÄ°LMÄ°Åž bölümdür.\n" +"Bu geçersiz bir kurulumdur. GENÄ°ÅžLETÄ°LMÄ°Åž alanın\n" +"türünü/partition deÄŸiÅŸtirmek için %s tuÅŸuna basın." + +msgid "NTFS compression not supported!" +msgstr "NTFS sıkıştırma desteklenmiyor!" + +msgid "NTFS encryption not supported!" +msgstr "NTFS ÅŸifreleme desteklenmiyor!" + +msgid "NTSC-J patch:" +msgstr "NTSC-J patch:" + +msgid "Neek2o Plugin" +msgstr "Neek2o Eklentisi" + +msgid "Network connection established." +msgstr "AÄŸ baÄŸlantısı kuruldu." + +msgid "Network error. Can't update gamercards." +msgstr "Network hatası. Gamecard'lar güncellenemiyor." + +msgid "New Themes" +msgstr "Yeni Temalar" + +msgid "Nintendo DS" +msgstr "Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "Nintendo DS BaÄŸlantısı" + +msgid "No" +msgstr "No" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "Etki alanı olmayan URL '%s'" + +msgid "No games found!" +msgstr "Oyun bulunamadı!" + +msgid "No partition selected!" +msgstr "Bölüm/Partition seçilmedi!" + +msgid "No themes found." +msgstr "Tema bulunamadı." + +msgid "No updates found." +msgstr "Güncelleme bulunamadı." + +msgid "NoDisc:" +msgstr "Disk Yok:" + +msgid "None found on disc" +msgstr "Disk’te bir ÅŸey bulunamadı" + +msgid "Not Found boot.dol!" +msgstr "boot.dol bulunamadı!" + +msgid "Not Found!" +msgstr "Bulunamadı!" + +msgid "Number of Online Players" +msgstr "Online Oyuncu Sayısı" + +msgid "Number of Players" +msgstr "Oyuncu Sayısı" + +msgid "Nunchuk" +msgstr "Nunchuk" + +msgid "OK" +msgstr "OK" + +msgid "OK!" +msgstr "OK!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (hileler):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina Hile Yöneticisi" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: Kod Hatası" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: Kodlar bulundu." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: Kod bulunamadı" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: Yetersiz Bellek Hatası" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: Kodlar aranıyor..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: Çok fazla kod" + +msgid "Off" +msgstr "Off" + +msgid "Off-Road Racing" +msgstr "Off-Road Racing" + +msgid "On" +msgstr "On" + +msgid "Online" +msgstr "Online" + +msgid "Online Content" +msgstr "Online İçerik " + +msgid "Online Play" +msgstr "Online Oynama" + +msgid "Online Players" +msgstr "Online Players" + +msgid "Online Score List" +msgstr "Online Skor Listesi " + +msgid "Online Updates" +msgstr "Online Güncellemeler" + +msgid "Open Sort" +msgstr "Sıralı Aç" + +msgid "Open Style" +msgstr "Stil Aç" + +msgid "Opening DVD disc..." +msgstr "DVD Disk açılıyor..." + +#, c-format +msgid "Opening partition: %s" +msgstr "Bölüm açılıyor: %s" + +msgid "Options" +msgstr "Seçenekler" + +msgid "Options discarded for this game." +msgstr "Bu oyun için seçenekler iptal edildi." + +msgid "Options saved for this game." +msgstr "Bu oyun için seçenekler kaydedildi." + +msgid "Out of memory" +msgstr "Yetersiz Bellek" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PAD HOOK" + +msgid "PAD HOOK:" +msgstr "PAD HOOK:" + +msgid "Page:" +msgstr "Sayfa:" + +msgid "Partial" +msgstr "Partial" + +msgid "Partition:" +msgstr "Bölüm:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "Bölüm: %s bulunamadı!" + +msgid "Party" +msgstr "Parti" + +msgid "Paused Start" +msgstr "BaÅŸlatma Durduruldu" + +msgid "Petanque" +msgstr "Petank" + +msgid "Pinball" +msgstr "Tilt" + +msgid "Platformer" +msgstr "Platform" + +msgid "Play Count" +msgstr "Oynanma" + +msgid "Players" +msgstr "Oyuncular" + +msgid "Please insert a game disc..." +msgstr "Lütfen bir oyun diski yerleÅŸtirin..." + +msgid "Plugin:" +msgstr "Eklenti" + +msgid "Poker" +msgstr "Poker" + +msgid "Portal of Power" +msgstr "Güç Geçidi" + +#, c-format +msgid "Press %s button for options." +msgstr "Seçenekler için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to %s." +msgstr "%s için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "Kodların geçerli olması için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to cancel." +msgstr "Ä°ptal etmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to change device." +msgstr "Aygıt deÄŸiÅŸtirmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to continue." +msgstr "Devam etmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "Dosya sistemini silme %s tuÅŸu" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "BCA dump için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to exit." +msgstr "Çıkmak için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "EXTEND/WBFS düzeltilmesi için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "WBFS biçimlendirmesi için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to go back." +msgstr "Geri gitmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "Bölüm/partition seçmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to select." +msgstr "Seçmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "Kodları atlamak için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "FAT senkronizasyonu için %s tuÅŸuna basın." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "Tam ekran önizleme için %s basın" + +#, c-format +msgid "Press %s for game options" +msgstr "Oyun ayarları için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s for global options" +msgstr "Genel ayarlar için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to discard options" +msgstr "Seçeneklerin iptali için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to download and update" +msgstr "%s tuÅŸuna basarak indirebilir ve güncelleyebilirsiniz" + +#, c-format +msgid "Press %s to download this theme" +msgstr "Bu temayı indirmek için %s basın" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "%s tuÅŸu ile indirebilir, %s tuÅŸu ile geri dönebilirsiniz" + +#, c-format +msgid "Press %s to return" +msgstr "Dönmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to save global settings" +msgstr "" +"Genel ayarları kaydetmek için %s\n" +"tuÅŸuna basın" + +#, c-format +msgid "Press %s to save options" +msgstr "Ayarları kaydetmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to save selection" +msgstr "Seçimi kaydetmek için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to select filter type" +msgstr "Filtre türü için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "Sıralama türü için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to start game" +msgstr "Oyunu baÅŸlatmak için %s tuÅŸuna basın" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "" +"meta.xml olmadan güncellemek için\n" +"%s tuÅŸuna basın" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "SOL/SAÄž tuÅŸlarına basarak aygıtı seçin" + +msgid "Press 1 to skip WBFS mounting" +msgstr "WBFS mount etmeyi iptal etmek için 1 tuÅŸuna basın" + +msgid "Press 2 to reload IOS" +msgstr "IOS'u tekrar yüklemek için 2 tuÅŸuna basın" + +msgid "Press A to continue without config.txt" +msgstr "config.txt olmadan devam etmek için A tuÅŸuna basın" + +msgid "Press A to select device" +msgstr "Aygıt seçmek için A tuÅŸuna basın" + +msgid "Press B to exit to HBC" +msgstr "HBC'den çıkmak için B tuÅŸuna basın" + +msgid "Press HOME to reset" +msgstr "Resetleme için HOME tuÅŸuna basın" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "IOS seçmek için SOL/SAÄž tuÅŸlarına basın" + +msgid "Press any button to continue..." +msgstr "Devam etmek için herhangi bir tuÅŸa basın..." + +msgid "Press any button to exit..." +msgstr "Çıkmak için herhangi bir tuÅŸa basın..." + +msgid "Press any button to restart..." +msgstr "Tekrar baÅŸlatmak için herhangi bir tuÅŸa basın..." + +msgid "Press any button.\n" +msgstr "Herhangi bir tuÅŸa basın.\n" + +msgid "Press any button..." +msgstr "Herhangi bir tuÅŸa basın..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "Kapakları indirmek için %S tuÅŸuna basın." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "DVD’yi çıkarmak için %s tuÅŸuna basın" + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "Profil:" + +#, c-format +msgid "Profile: %s" +msgstr "Profil: %s" + +msgid "Program Updates" +msgstr "Program Güncellemeleri" + +msgid "Publisher" +msgstr "Yayınlayan" + +msgid "Puzzle" +msgstr "Puzzle" + +msgid "Quit" +msgstr "Çık" + +msgid "RAW" +msgstr "RAW" + +msgid "RIGHT" +msgstr "SAÄž" + +msgid "RPG" +msgstr "RPG" + +msgid "Racing" +msgstr "Yarış" + +msgid "Rail Shooter" +msgstr "Rail Shhoter" + +#, c-format +msgid "Rated %s" +msgstr "DeÄŸerlendirme %s" + +msgid "Rating" +msgstr "Derecelendirme" + +msgid "Read" +msgstr "Oku" + +msgid "Reading BCA..." +msgstr "BCA okunuyor..." + +msgid "Reboot" +msgstr "Tekrar BaÅŸlat" + +msgid "Region" +msgstr "Bölge" + +msgid "Release Date" +msgstr "Yayın Tarihi" + +msgid "Release Notes: (short)" +msgstr "Yayın Notları: (kısa)" + +msgid "Removing game, please wait..." +msgstr "Oyun siliniyor, lütfen bekleyin!" + +msgid "Restarting Wii..." +msgstr "Wii yeniden baÅŸlatılıyor..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "Dönüş! (ret = %d)" + +msgid "Rhythm" +msgstr "Ritim" + +msgid "Rows:" +msgstr "Sıralar:" + +msgid "Rugby" +msgstr "Ragbi" + +msgid "Running benchmark, please wait" +msgstr "KarşılaÅŸtırma deÄŸerleri yükleniyor, lütfen bekleyin" + +msgid "S. Chinese" +msgstr "Basit Çince" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC kart" + +msgid "SUCCESS!" +msgstr "BAÅžARILI!" + +msgid "Save .gct" +msgstr ".gct kaydet" + +msgid "Save Debug" +msgstr "Hata ayıklama kaydet" + +msgid "Save Settings" +msgstr "Ayarları kaydet" + +msgid "Save debug.log" +msgstr "debug.log kaydet" + +msgid "Savegame not found.\n" +msgstr "Save dosyası bulunamadı.\n" + +msgid "Savegame:" +msgstr "Save Dosyası:" + +msgid "Saving Settings... " +msgstr "Ayarlar kaydediliyor..." + +msgid "Saving cheats..." +msgstr "Hileler kaydediliyor..." + +msgid "Saving gamelist.txt ... " +msgstr "gamelist.txt kaydediliyor..." + +msgid "Saving settings..." +msgstr "Ayarlar kaydediliyor..." + +msgid "Saving:" +msgstr "Kaydediliyor:" + +#, c-format +msgid "Saving: %s" +msgstr "%s kaydediliyor" + +msgid "Screenshot:" +msgstr "Ekran görüntüsü:" + +msgid "Scroll:" +msgstr "Scroll:" + +msgid "Search" +msgstr "Arama" + +msgid "Search for:" +msgstr "Aramak:" + +msgid "Select Alternative .dol:" +msgstr "Alternatif .dol seçimi:" + +msgid "Select WBFS device:" +msgstr "WBFS aygıt seçimi:" + +msgid "Select a different partition" +msgstr "Farklı bir bölüm/partition seç" + +msgid "Select a partition" +msgstr "Bölüm/Partition seç" + +msgid "Select all" +msgstr "Tümünü seç" + +msgid "Select device:" +msgstr "Aygıt seç:" + +msgid "Select the game you want to boot" +msgstr "Yüklenmesini istediÄŸiniz oyunu seçin" + +msgid "Selected Game" +msgstr "Seçilen Oyun" + +msgid "Settings" +msgstr "Ayarlar" + +msgid "Shooter" +msgstr "Shooter" + +msgid "Show All" +msgstr "Hepsini Göster" + +msgid "Show cIOS info" +msgstr "cIOS bilgisini göster" + +msgid "Shutdown" +msgstr "Kapat" + +msgid "Side:" +msgstr "Bölüm:" + +msgid "Sim Racing" +msgstr "Yarış Simülasyonu" + +msgid "Simulation" +msgstr "Simulasyon" + +msgid "Size(GB) Type Mount Used" +msgstr "Size(GB) Type Mount Used" + +#, c-format +msgid "Size: %d bytes" +msgstr "Boyut: %d bytes" + +msgid "Skateboard" +msgstr "Kaykay" + +msgid "Skateboarding" +msgstr "Kaykay" + +msgid "Skiing" +msgstr "Kayakçılık" + +msgid "Snowboarding" +msgstr "Snowboard" + +msgid "Soccer" +msgstr "Futbol" + +msgid "Sort" +msgstr "Sırala" + +msgid "Sort Games" +msgstr "Oyunları Sırala" + +msgid "Sort Order:" +msgstr "Basamakla:" + +msgid "Sort Type:" +msgstr "Tipe göre sırala" + +#, c-format +msgid "Sort: %s-%s" +msgstr "Sırala: %s-%s" + +msgid "Spanish" +msgstr "Ä°spanyolca" + +msgid "Sports" +msgstr "Spor" + +msgid "Start" +msgstr "BaÅŸlat" + +msgid "Start Game" +msgstr "Oyunu BaÅŸlat" + +msgid "Start this game?" +msgstr "Bu oyunu çalıştırayım mı?" + +msgid "Stealth Action" +msgstr "Stealth Action" + +msgid "Stopping DVD..." +msgstr "DVD Durduruluyor..." + +msgid "Strategy" +msgstr "Strateji" + +msgid "Style" +msgstr "Stil" + +msgid "Style:" +msgstr "Stil:" + +msgid "Surfing" +msgstr "Sörf" + +msgid "Survival Horror" +msgstr "Survival Horror" + +msgid "Sync FAT free space info?" +msgstr "FAT boÅŸ alan bilgisi senkronize ediliyor?" + +msgid "Synchronizing, please wait." +msgstr "Senkronize ediliyor, lütfen bekleyin" + +msgid "Synopsis" +msgstr "Özet" + +msgid "Synopsis Length" +msgstr "Sür özeti" + +msgid "System" +msgstr "Sistem" + +msgid "System Def." +msgstr "Sistem Ayarları" + +msgid "T. Chinese" +msgstr "T. Çince" + +msgid "Table Tennis" +msgstr "Masa Tenisi" + +msgid "Tennis" +msgstr "Tenis" + +msgid "Theme" +msgstr "Tema" + +msgid "Theme Info" +msgstr "Tema Bilgisi" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"Tema çok büyük olabilir.\n" +"CFG'nin yeniden baÅŸlatılması öneriliyor.\n" + +msgid "Theme:" +msgstr "Tema:" + +#, c-format +msgid "Theme: %s" +msgstr "Tema: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "wii.spiffy360.com tarafından saÄŸlanan temalar" + +msgid "Themes to download" +msgstr "Temalar indirildi" + +msgid "Themes with updates" +msgstr "Tema güncellemeleri" + +msgid "Title" +msgstr "BaÅŸlık" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "Çok fazla hile! (%d)" + +msgid "Too many code lines!" +msgstr "Çok fazla kod satırı!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "Çok fazla parça! %d" + +msgid "Total Body Tracking" +msgstr "Total Body Tracking" + +msgid "Train Simulation" +msgstr "Tren Simülasyonu" + +msgid "Trivia" +msgstr "Trivia" + +msgid "Truck Racing" +msgstr "Kamyon Yarışı" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "Deneniyor (url#%d)..." + +msgid "Turntable" +msgstr "Turntable" + +msgid "UNUSED" +msgstr "KULLANILMAYAN" + +msgid "UP" +msgstr "YUKARI" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' 'http://' ile baÅŸlamıyor!" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' içinde yol yok" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko bulundu. Hata ayıklama etkinleÅŸtirildi." + +msgid "USB Mass Storage Device" +msgstr "USB Yığın Depolama Aygıtı" + +msgid "Unknown" +msgstr "Bilinmeyen" + +msgid "Unknown syntax!" +msgstr "Bilinmeyen sözdizimi!" + +msgid "Unplayed" +msgstr "Oynanmayan" + +msgid "Unplayed Games" +msgstr "Oynanmamış Oyunlar" + +msgid "Update themes" +msgstr "Temaları güncelleÅŸtir" + +msgid "Updates" +msgstr "GüncelleÅŸtirmeler" + +msgid "Updating database." +msgstr "Veritabanı güncelleÅŸtiriliyor..." + +msgid "Updating devolution" +msgstr "Devolution güncelleniyor" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "Kullanılan: %.1fGB BoÅŸ: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "KaydedilmiÅŸ Alternatif .dol ile:" + +msgid "VC-Arcade" +msgstr "VC-Arcade" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-N64" + +msgid "VC-NES" +msgstr "VC-NES" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC-SNES" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "Versiyon" + +msgid "Video Patch:" +msgstr "Video Yaması:" + +msgid "Video:" +msgstr "Video:" + +msgid "View" +msgstr "Görünüm" + +msgid "Virtual Pet" +msgstr "Sanal Hayvan" + +msgid "Vitality Sensor" +msgstr "Vitality Sensor " + +msgid "Volleyball" +msgstr "Voleybol" + +msgid "WARNING:" +msgstr "UYARI:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB boÅŸ, tamamı %.1fGB" + +msgid "Watercraft Racing" +msgstr "Watercraft Racing" + +msgid "Wheel" +msgstr "Direksiyon" + +msgid "Wide Screen:" +msgstr "GeniÅŸ Ekran" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Wii Channel" + +msgid "Wii Speak" +msgstr "Wii Speak" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "Wiimote" + +msgid "Wrestling" +msgstr "Wrestling" + +msgid "Write Playlog:" +msgstr "Playlog yazma:" + +#, c-format +msgid "Writing to %s" +msgstr "%s yazılıyor" + +#, c-format +msgid "Writing: %s" +msgstr "Yazılıyor: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "Hatalı Boyut: %d (%d)" + +msgid "Yes" +msgstr "Evet" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"Cihazı prizden çıkarıp tekrar takabilirsiniz\n" +"veya biraz daha bekleyebilirsiniz" + +msgid "Zapper" +msgstr "Zapper" + +msgid "[ CHANGED ]" +msgstr "[ DEĞİŞMÄ°Åž ]" + +msgid "[ FOUND ]" +msgstr "[ BULUNDU ]" + +msgid "[ SAVED ]" +msgstr "[ KAYDEDÄ°LDÄ° ]" + +msgid "[HOME]" +msgstr "[HOME]" + +msgid "[No HQ]" +msgstr "[HQ DeÄŸil]" + +msgid "[USED]" +msgstr "[KULLANILAN]" + +msgid "[default]" +msgstr "[varsayılan]" + +msgid "[saved]" +msgstr "[kaydedildi]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "Bozuk WBFS Dosyası: %s" + +msgid "calculating space, please wait..." +msgstr "BoÅŸ yer hesaplanıyor, lütfen bekleyin..." + +msgid "delete" +msgstr "Sil" + +#, c-format +msgid "dest: %p - %p" +msgstr "hedef: %p - %p" + +msgid "discard" +msgstr "discard" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "Alan adı %s çözümlenemedi" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "Okuma hatası: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "%d oyuncu için" + +msgid "for 1 player" +msgstr "1 oyuncu için" + +msgid "format" +msgstr "biçimlendirme" + +msgid "free" +msgstr "boÅŸ" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "Liner okuma hızı: %.2f mb/s" + +msgid "linear..." +msgstr "liner..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin boyutu: %d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "Müzik dosyası çok büyük (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "music.mp3 veya music.mod bulunamadı!" + +msgid "no file" +msgstr "Dosya yok" + +msgid "none" +msgstr "Hiçbiri" + +msgid "or format a WBFS partition." +msgstr "veya WFBS bölümü/partition biçimlendir." + +msgid "page" +msgstr "Sayfa" + +msgid "parse error" +msgstr "Ayrıştırma Hatası" + +msgid "r51-" +msgstr "r51-" + +msgid "r52+" +msgstr "r52+" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "Rastgele okuma hızı: %.2f mb/s" + +msgid "random..." +msgstr "Rastgele..." + +msgid "reset" +msgstr "Reset" + +msgid "revert" +msgstr "Döndür" + +msgid "save" +msgstr "kaydet" + +#, c-format +msgid "split error: %s" +msgstr "Ayırma hatası: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "Wii diski açılamıyor" + +#, c-format +msgid "used: %p - %p" +msgstr "kullanılıyor: %p - %p" + +#~ msgid "%s No games found!!" +#~ msgstr "%s Hiçbir oyun bulunamadı!" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "Wii oyunu baÅŸlatılıyor, lütfen bekleyin..." + +#~ msgid "Download Game Database" +#~ msgstr "Oyun Veritabanını Ä°ndir" + +#~ msgid "Downloading %s Covers for %.4s" +#~ msgstr "%s kapak %.4s için indiriliyor" + +#~ msgid "Downloading %s cover... (%s)" +#~ msgstr "%s kapağı indiriliyor... (%s)" + +#~ msgid "Downloading All %sCovers" +#~ msgstr "Bütün %s kapaklar indiriliyor" + +#~ msgid "" +#~ "ERROR: CIOS222 or CIOS223 required for\n" +#~ "starting games from a FAT partition!" +#~ msgstr "" +#~ "HATA: FAT dosya sisteminden oyun yüklenmesi için \n" +#~ " CIOS222 veya CIOS223 gerekli!" + +#~ msgid "ERROR: SD mount NTFS! (%d)" +#~ msgstr "HATA: SD üstünde NTFS baÄŸlarken! (%d)" + +#~ msgid "ERROR: SD/FAT mount wbfs! (%d)" +#~ msgstr "HATA: SD/FAT üstünde WBFS baÄŸlarken! (%d)" + +#~ msgid "ERROR: USB mount NTFS! (%d)" +#~ msgstr "HATA: USB üstünde NTFS baÄŸlarken! (%d)" + +#~ msgid "ERROR: USB/FAT mount wbfs! (%d)" +#~ msgstr "HATA: USB/FAT üstünde WBFS baÄŸlarken! (%d)" + +#~ msgid "ERROR: WBFS FAT mount sector %x not %x" +#~ msgstr "HATA: WBFS FAT baÄŸlanan sektör %x %x deÄŸil" + +#~ msgid "" +#~ "ERROR: ehcmodule2 no longer included!\n" +#~ "external file ehcmodule.elf must be used!" +#~ msgstr "" +#~ "HATA: Ehcmodule2 olmadığında!\n" +#~ " Harici ehcmodule.elf dosyası kullanılmalı!" + +#~ msgid "" +#~ "ERROR: ehcmodule3 no longer included!\n" +#~ "external file ehcmodule3.elf must be used!" +#~ msgstr "" +#~ "HATA: Ehcmodule3 olmadığında!\n" +#~ " Harici ehcmodule3.elf dosyası kullanılmalı!" + +#~ msgid "FAT mode only supported with ios 222 rev4" +#~ msgstr "FAT Modu sadece CIOS 222 rev4’le destekleniyor" + +#~ msgid "FAT32 mount: " +#~ msgstr "FAT32 baÄŸlama: " + +#~ msgid "FAT: fallback to old method" +#~ msgstr "FAT: Eski yönteme dönüş" + +#~ msgid "Found %s Cover" +#~ msgstr "%s kapak bulundu" + +#~ msgid "HTTP ERROR: %s" +#~ msgstr "HTTP HATA: %s" + +#~ msgid "Loading previous game list..." +#~ msgstr "Önceki oyun listesi yükleniyor..." + +#~ msgid "Missing" +#~ msgstr "Kayıp" + +#~ msgid "Missing " +#~ msgstr "Eksik " + +#~ msgid "No more memory to copy file from HTTP response" +#~ msgstr "HTTP isteÄŸi belleÄŸe kopyalanamadı" + +#~ msgid "Opening FAT partition..." +#~ msgstr "Fat bölümü/partition açılıyor..." + +#~ msgid "Opening NTFS partition..." +#~ msgstr " NTFS bölümü/partition açılıyor..." + +#~ msgid "Press %s to select" +#~ msgstr "Seçmek için %s tuÅŸuna basın" + +#~ msgid "Remove Game" +#~ msgstr "Oyunu sil" + +#~ msgid "Size: %d byte" +#~ msgstr "Boyut: %d byte" + +#~ msgid "Switching to IOS222 for FAT support." +#~ msgstr "FAT desteÄŸi için IOS222’yi ayarla." + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "WiiTDB Oyun Veritabanı GüncelleÅŸtirme" + +#~ msgid "Update titles.txt" +#~ msgstr "titles.txt GüncelleÅŸtir" + +#~ msgid "[ SD/SDHC Card ]" +#~ msgstr "[ SD/SDHC Kart ]" + +#~ msgid "[ USB Mass Storage Device ]" +#~ msgstr "[ USB Yığın Depolama Aygıtı ]" + +#~ msgid "cover" +#~ msgstr "Kapak" diff --git a/Languages/ZH_CN-clamis.lang b/Languages/ZH_CN-clamis.lang new file mode 100644 index 0000000..b5a9eb1 --- /dev/null +++ b/Languages/ZH_CN-clamis.lang @@ -0,0 +1,1846 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-06-07 23:40+0200\n" +"PO-Revision-Date: 2010-07-26 11:24+0800\n" +"Last-Translator: Clamis \n" +"Language-Team: Simplified Chinese(ZH_CN) \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB空间剩余(å…±%.1fGB)" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGBå·²å¤åˆ¶äºŽ%d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d个å¯ç”¨" + +#, c-format +msgid "%d more notes" +msgstr "%d更多注释" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s 分割:%d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s,请ç¨å€™..." + +#, c-format +msgid "(%d online)" +msgstr "(%d在线)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d秒超时)" + +msgid "(This can take a couple of minutes)" +msgstr "" + +msgid ".dol too small" +msgstr ".dol文件太å°" + +msgid "3D cover" +msgstr "" + +msgid "< ASC >" +msgstr "< å‡åº >" + +msgid "< DESC >" +msgstr "< é™åº >" + +msgid "< DOWNLOAD >" +msgstr "< 下载 >" + +msgid "About" +msgstr "" + +msgid "Action" +msgstr "动作游æˆ" + +msgid "Additional config:" +msgstr "附加设置:" + +msgid "Admin Lock:" +msgstr "" + +msgid "Admin Unlock" +msgstr "" + +msgid "Adult" +msgstr "æˆäººç±»" + +msgid "Adventure" +msgstr "冒险游æˆ" + +msgid "All" +msgstr "全部" + +msgid "All Games" +msgstr "所有游æˆ" + +msgid "All themes up to date." +msgstr "所有主题å‡å·²æ›´æ–°è‡³æœ€æ–°ç‰ˆã€‚" + +msgid "Alt dol:" +msgstr "替æ¢dol:" + +msgid "Alternative .dol:" +msgstr "替代的.dol:" + +msgid "Anti 002 Fix:" +msgstr "防002错误修正:" + +#, c-format +msgid "App. Path: %s" +msgstr "程åºè·¯å¾„:%s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "确定è¦ä½¿ç”¨%s这个分区å—?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "确定è¦â€˜ä¿®å¤â€™è¿™ä¸ªåˆ†åŒºå—?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "确定è¦åˆ é™¤è¿™ä¸ªæ¸¸æˆå—?" + +msgid "Ascending" +msgstr "" + +msgid "Auto" +msgstr "" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "自动è¿è¡Œæ¸¸æˆï¼šæœªæ‰¾åˆ°%.6sï¼" + +msgid "Available Updates" +msgstr "å¯ç”¨æ›´æ–°" + +msgid "Back" +msgstr "" + +msgid "Balance Board" +msgstr "支æŒå¹³è¡¡æ¿" + +msgid "Basic" +msgstr "" + +msgid "Block IOS Reload:" +msgstr "防IOSé‡è½½ï¼š" + +msgid "Boot Disc" +msgstr "光盘è¿è¡Œ" + +msgid "Boot disc" +msgstr "" + +msgid "Booting Wii game, please wait..." +msgstr "正在å¯åŠ¨Wii游æˆï¼Œè¯·ç¨å€™..." + +#, c-format +msgid "CFG base: %s" +msgstr "CFG根目录:%s" + +msgid "Cancelled." +msgstr "å·²å–消" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "无法建立目录:%s" + +msgid "Cheat Codes:" +msgstr "作弊ç ï¼š" + +msgid "Cheats: " +msgstr "金手指:" + +msgid "Check For Updates" +msgstr "系统更新" + +msgid "Checking for themes..." +msgstr "正在检查主题更新..." + +msgid "Checking for updates..." +msgstr "正在检查更新..." + +msgid "Choose a sorting method" +msgstr "请选择一ç§æŽ’åºæ–¹å¼" + +msgid "Classic" +msgstr "" + +msgid "Classic Controller" +msgstr "支æŒä»»å¤©å ‚ç»å…¸æ‰‹æŸ„" + +msgid "Clear Patches:" +msgstr "清除补ä¸ï¼š" + +#, c-format +msgid "Complete. Size: %d" +msgstr "完æˆï¼Œå¤§å°ï¼š%d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "设置错误,已到达MAX_DNS_ENTRIES但列表ä»æ˜¯ç©ºçš„" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "net_read()报告网络连接错误,错误代ç ï¼š%i" + +msgid "Console" +msgstr "" + +msgid "Console Def." +msgstr "" + +msgid "Controller" +msgstr "" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "无法åˆå§‹åŒ–DIP模å—ï¼(ret = %d)" + +msgid "Country Fix:" +msgstr "国家代ç ä¿®æ­£ï¼š" + +msgid "Cover" +msgstr "" + +msgid "Cover Image:" +msgstr "游æˆå°é¢ï¼š" + +msgid "Cover Style" +msgstr "" + +msgid "Cover~~Back" +msgstr "" + +msgid "Cover~~Front" +msgstr "" + +msgid "Create" +msgstr "创建" + +msgid "Creator" +msgstr "作者" + +#, c-format +msgid "Current Version: %s" +msgstr "当å‰ç‰ˆæœ¬ï¼š%s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "无法找到cIOS %d,请安装该cIOSï¼" + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "无法加载cIOS %dï¼(ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "cIOS %d文件ä¸å®Œæ•´ï¼Œè¯·é‡æ–°å®‰è£…该cIOSï¼" + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "cIOS %s加载æˆåŠŸï¼" + +msgid "DISC cover" +msgstr "" + +msgid "DOWN" +msgstr "" + +msgid "Dance Pad" +msgstr "支æŒè·³èˆžæ¯¯" + +msgid "Database update successful." +msgstr "æ•°æ®åº“æˆåŠŸæ›´æ–°ï¼" + +msgid "Debug" +msgstr "" + +msgid "Delete Game" +msgstr "" + +msgid "Deleting" +msgstr "删除中" + +msgid "Descending" +msgstr "" + +msgid "Developer" +msgstr "å¼€å‘商" + +msgid "Device is not responding!" +msgstr "" + +msgid "Device:" +msgstr "设备:" + +msgid "Disc" +msgstr "光盘" + +msgid "Disc (Ask)" +msgstr "光盘(询问)" + +msgid "Done." +msgstr "完æˆï¼" + +msgid "Download .txt" +msgstr "下载.txt文件" + +msgid "Download All Covers" +msgstr "" + +msgid "Download All Missing Covers" +msgstr "下载所有缺失的游æˆå°é¢" + +msgid "Download Missing Covers" +msgstr "" + +msgid "Download Themes" +msgstr "" + +msgid "Download complete." +msgstr "下载完æˆï¼" + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "" + +msgid "Download titles.txt" +msgstr "" + +msgid "Downloadable Content" +msgstr "支æŒè¿½åŠ å†…容下载包(DLC)" + +msgid "Downloading ALL MISSING covers" +msgstr "正在下载所有缺失的å°é¢" + +msgid "Downloading ALL covers" +msgstr "正在下载所有å°é¢" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "正在下载%.6s的所有å°é¢" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "正在下载%.6s缺失的å°é¢" + +msgid "Downloading Theme previews..." +msgstr "正在下载主题预览..." + +msgid "Downloading cheats..." +msgstr "正在下载金手指..." + +msgid "Downloading database." +msgstr "正在下载数æ®åº“..." + +msgid "Downloading titles.txt ..." +msgstr "正在下载游æˆæ ‡é¢˜åº“titles.txt..." + +#, c-format +msgid "Downloading: %s" +msgstr "正在下载:%s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "正在下载:%s为%s" + +msgid "Downloads" +msgstr "下载次数" + +msgid "Drums" +msgstr "支æŒæ‰“鼓机" + +msgid "Dutch" +msgstr "" + +msgid "ERROR" +msgstr "错误" + +msgid "ERROR creating file" +msgstr "创建文件时å‘生错误" + +msgid "ERROR game opt" +msgstr "游æˆé€‰é¡¹è®¾ç½®é”™è¯¯" + +msgid "ERROR reading BCA!" +msgstr "读å–BCAæ—¶å‘生错误ï¼" + +#, c-format +msgid "ERROR removing %s" +msgstr "移除%sæ—¶å‘生错误ï¼" + +msgid "ERROR writing BCA!" +msgstr "写入BCAæ—¶å‘生错误ï¼" + +msgid "ERROR!" +msgstr "错误ï¼" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "错误ï¼(ret = %d)" + +msgid "ERROR:" +msgstr "错误:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "错误:无法存å–%s" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "错误:Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "错误:BCA功能需è¦CIOS222/223 v4版" + +msgid "ERROR: Cache Close" +msgstr "错误:缓存关闭" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "错误:无法打开游æˆï¼(ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "错误:游æˆå·²å®‰è£…ï¼ï¼" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "错误:GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "错误:无效的游æˆID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "错误:正在读å–EHC模å—! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "错误:未找到任何分区ï¼(ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "错误:这ä¸æ˜¯Wii的游æˆå…‰ç›˜ï¼ï¼" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "错误:Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "错误:OpenPartition %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "错误:OpenPartition(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "错误:SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "错误:SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "错误:å‘生在设置SD模å¼æ—¶" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "错误:USBåˆå§‹åŒ–错误ï¼(%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"错误:从FATæ ¼å¼åˆ†åŒºå¯åŠ¨æ¸¸æˆéœ€è¦:\n" +"cIOS249rev18, cIOS222v4, cIOS223v4,\n" +"cIOS224v5或更高版本支æŒï¼\n" +"请å‡çº§IOS249或选择其它IOSè¿è¡Œã€‚" + +msgid "ERROR: cache: out of memory" +msgstr "错误:缓存:内存溢出" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "错误:get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "错误:内存é‡å ï¼" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "错误:åªæœ‰IOS222/223-mload支æŒWBFS多分区ï¼" + +msgid "ERROR: not enough free space!!" +msgstr "错误:剩余空间ä¸è¶³ï¼ï¼" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "错误:å‘生在打开%.6sæ—¶" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "错误:set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "错误:å‘生在建立碎片%d %dæ—¶" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "错误:å‘生在写入%s (%d)æ—¶." + +msgid "EXTEND" +msgstr "扩展" + +msgid "Ejecting DVD..." +msgstr "正在弹出DVD..." + +msgid "English" +msgstr "" + +msgid "English Title" +msgstr "" + +msgid "Enter Code: " +msgstr "输入代ç ï¼š" + +msgid "Error GRRLIB init" +msgstr "GRRLIBåˆå§‹åŒ–错误ï¼" + +msgid "Error Initializing Network." +msgstr "网络åˆå§‹åŒ–æ—¶å‘生错误ï¼" + +msgid "Error adding disc!" +msgstr "添加光盘时å‘生错误ï¼" + +msgid "Error creating directory..." +msgstr "创建目录时å‘生错误..." + +msgid "Error discarding options!" +msgstr "放弃ä¿å­˜é€‰é¡¹æ—¶å‘生错误ï¼" + +msgid "Error downloading theme preview..." +msgstr "下载主题预览时å‘生错误..." + +msgid "Error downloading theme..." +msgstr "下载主题时å‘生错误..." + +msgid "Error downloading themes..." +msgstr "下载主题时å‘生错误..." + +msgid "Error downloading update..." +msgstr "下载更新时å‘生错误..." + +msgid "Error downloading updates..." +msgstr "下载更新时å‘生错误..." + +msgid "Error downloading." +msgstr "下载时å‘生错误ï¼" + +msgid "Error establishing connection" +msgstr "建立连接时å‘生错误ï¼" + +msgid "Error extracting theme..." +msgstr "解压缩主题时å‘生错误..." + +msgid "Error opening database, update did not complete." +msgstr "æ•°æ®åº“打开时å‘生错误,更新未完æˆï¼" + +#, c-format +msgid "Error opening: %s" +msgstr "打开时å‘生错误:%s" + +#, c-format +msgid "Error playing %s" +msgstr "游æˆ%sè¿è¡Œæ—¶å‘生错误" + +msgid "Error reading .dol" +msgstr "读å–.dol文件时å‘生错误" + +msgid "Error reading dol header" +msgstr "读å–dol头信æ¯æ—¶å‘生错误" + +#, c-format +msgid "Error saving %s" +msgstr "ä¿å­˜%sæ—¶å‘生错误" + +msgid "Error saving options!" +msgstr "ä¿å­˜é€‰é¡¹æ—¶å‘生错误ï¼" + +msgid "Error saving settings!" +msgstr "ä¿å­˜è®¾ç½®æ—¶å‘生错误ï¼" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"ä¿å­˜æ¸¸æˆæ—¥å¿—æ—¶å‘生错误ï¼\n" +"请从Wii主èœå•é‡å¯CFG进行修å¤ã€‚" + +msgid "Error: Invalid PNG image!" +msgstr "错误:无效的PNG图片ï¼" + +msgid "Error: no URL." +msgstr "错误:没有URL链接!" + +msgid "Error: no data." +msgstr "错误:没有对应数æ®ï¼" + +msgid "Exit" +msgstr "" + +#, c-format +msgid "Extracting: %s" +msgstr "正在解压缩:%s" + +msgid "FAIL" +msgstr "失败" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT:分é…错误 %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "严é‡é”™è¯¯ï¼šåˆ†é…网格(%d)" + +#, c-format +msgid "FILL %d" +msgstr "å¡«å……%d" + +msgid "FLAT cover" +msgstr "" + +msgid "FULL cover" +msgstr "" + +msgid "Fav" +msgstr "" + +msgid "Fav: Off" +msgstr "" + +msgid "Fav: On" +msgstr "" + +msgid "Favorite" +msgstr "" + +msgid "Favorite Games" +msgstr "最喜爱的游æˆ" + +msgid "Favorite:" +msgstr "我的最爱:" + +msgid "Favorites:" +msgstr "" + +msgid "Fighting" +msgstr "格斗游æˆ" + +#, c-format +msgid "File not found! %s" +msgstr "未找到文件ï¼%s" + +msgid "Filter" +msgstr "" + +msgid "Filter Games" +msgstr "游æˆç­›é€‰" + +msgid "Filter by Controller" +msgstr "按控制类型筛选" + +msgid "Filter by Genre" +msgstr "按游æˆç±»åž‹ç­›é€‰" + +msgid "Filter by Online Features" +msgstr "按è”机特性筛选" + +msgid "Filter:" +msgstr "" + +msgid "Fixing EXTEND partition..." +msgstr "正在修å¤æ‰©å±•åˆ†åŒº..." + +msgid "Force NTSC" +msgstr "" + +msgid "Force PAL50" +msgstr "" + +msgid "Force PAL60" +msgstr "" + +msgid "Formatting" +msgstr "正在格å¼åŒ–中" + +#, c-format +msgid "Found %s" +msgstr "" + +msgid "French" +msgstr "" + +msgid "Full" +msgstr "" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "" + +msgid "Game Options" +msgstr "游æˆé€‰é¡¹" + +#, c-format +msgid "Game Options: %s" +msgstr "游æˆé€‰é¡¹ï¼š%s" + +msgid "Gamecube" +msgstr "支æŒNGC手柄(GameCube)" + +msgid "Gamer Card:" +msgstr "" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "" + +msgid "Genre" +msgstr "" + +msgid "German" +msgstr "" + +msgid "Global Options" +msgstr "全局选项" + +msgid "Guitar" +msgstr "支æŒå‰ä»–" + +msgid "HQ cover" +msgstr "" + +msgid "HTTP Response was without a file" +msgstr "HTTPçš„å“应ä¸åŒ…å«ä»»ä½•æ–‡ä»¶" + +msgid "Hide" +msgstr "" + +msgid "Hide Game:" +msgstr "éšè—游æˆï¼š" + +msgid "Hold button B to cancel." +msgstr "按ä½Bé”®å–消" + +msgid "Homebrew Channel" +msgstr "" + +msgid "Hook Type:" +msgstr "é’©å­ç±»åž‹ï¼š" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "IDä¸åŒ¹é…:[%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOSé‡è½½ï¼šå·²é˜»æ­¢" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s)失败,代ç ï¼š%d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "LZ77在编ç %xæ—¶å‘生冲çª" + +msgid "Info" +msgstr "" + +#, c-format +msgid "Info file: %s" +msgstr "ä¿¡æ¯æ–‡ä»¶ï¼š%s" + +msgid "Initializing Network..." +msgstr "正在åˆå§‹åŒ–网络..." + +msgid "Install" +msgstr "" + +msgid "Install Date" +msgstr "安装数æ®" + +msgid "Install Game" +msgstr "安装游æˆ" + +msgid "Install game" +msgstr "安装游æˆ" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "安装错误ï¼(ret = %d)" + +msgid "Installing game, please wait..." +msgstr "正在安装游æˆï¼Œè¯·ç¨å€™..." + +msgid "Invalid .dol" +msgstr "无效的.dol文件" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "无效分区:'%s'" + +msgid "Italian" +msgstr "" + +msgid "Japanese" +msgstr "" + +msgid "Japanese Title" +msgstr "" + +msgid "Keyboard" +msgstr "支æŒé”®ç›˜" + +msgid "Korean" +msgstr "" + +msgid "LEFT" +msgstr "" + +msgid "LOCKED!" +msgstr "å·²é”定ï¼" + +msgid "Language:" +msgstr "游æˆè¯­è¨€ï¼š" + +msgid "Last Play Date" +msgstr "最åŽæ¸¸æˆæ—¥æœŸ" + +msgid "Launch Methods" +msgstr "å¯åŠ¨æ–¹å¼" + +msgid "Load OK!" +msgstr "读å–æˆåŠŸï¼" + +#, c-format +msgid "Loader Version: %s" +msgstr "程åºç‰ˆæœ¬å·ï¼š%s" + +msgid "Loading ..." +msgstr "读å–中..." + +msgid "Main" +msgstr "" + +msgid "Main Menu" +msgstr "主èœå•" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"请确硬盘连接在Wiiçš„0å·USB端å£ä¸Šï¼\n" +"(0å·ç«¯å£å°±æ˜¯ä¸»æœºèƒŒé¢é è¿‘外边框的那个)" + +msgid "Manage" +msgstr " ç®¡ç† " + +msgid "Manage Cheats" +msgstr "" + +msgid "Microphone" +msgstr "支æŒéº¦å…‹é£Ž" + +msgid "Motion+" +msgstr "支æŒåŠ¨æ„Ÿå¼ºåŒ–器(Wii MotionPlus)" + +msgid "Mounting device, please wait..." +msgstr "正在加载设备,请ç¨å€™..." + +msgid "Music" +msgstr "音ä¹æ¸¸æˆ" + +#, c-format +msgid "Music file from dir: %s" +msgstr "背景音ä¹æ–‡ä»¶ç›®å½•ï¼š%s" + +#, c-format +msgid "Music file size: %d" +msgstr "背景音ä¹æ–‡ä»¶å¤§å°ï¼š%d" + +#, c-format +msgid "Music file: %s" +msgstr "背景音ä¹æ–‡ä»¶ï¼š%s" + +msgid "Music: Disabled" +msgstr "背景音ä¹ï¼šç¦ç”¨" + +msgid "Music: Enabled" +msgstr "背景音ä¹ï¼šå¼€å¯" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "背景音ä¹ï¼šæ­£åœ¨å¯»æ‰¾%s文件ä½ç½®åœ¨: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "背景音ä¹ï¼šæ’­æ”¾ä¸‹ä¸€ä¸ªæ–‡ä»¶ï¼š%i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "背景音ä¹ï¼šæ‰¾åˆ°%s个文件:%i" + +msgid "Music: musicArray contents: " +msgstr "背景音ä¹ï¼šéŸ³ä¹ç»„内容:" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"注æ„:CIOS249 rev10之å‰çš„版本:\n" +"ä¸æ”¯æŒä»ŽSDHCå¡è¯»å–è¿è¡Œæ¸¸æˆï¼" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"注æ„:CIOS249 rev14之å‰çš„版本:\n" +"很å¯èƒ½æ— æ³•å¤„ç†001错误,导致游æˆæ— æ³•è¿è¡Œï¼" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"注æ„: CIOS249 rev9之å‰çš„版本:\n" +"ä¸æ”¯æŒä»ŽUSB设备读å–è¿è¡Œæ¸¸æˆï¼" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"注æ„:你正在使用CIOS249 rev13:\n" +"退出游æˆåŽå¿…é¡»å¤ä½æˆ–é‡å¯Wii,\n" +"å¦åˆ™å†è¿è¡Œå…¶å®ƒæ¸¸æˆæ—¶ä¼šç›´æŽ¥æ­»æœºï¼" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"注æ„:你正在使用CIOS249 rev14:\n" +"该版本已知与D9光盘有兼容性问题,\n" +"强烈建议使用最新版或其它IOS安装è¿è¡Œè¯¥æ¸¸æˆï¼" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "注æ„:需è¦é‡å¯ä¸»ç¨‹åºä»¥ä½¿æ›´æ–°ç”Ÿæ•ˆï¼" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"注æ„:P#%d分区是扩展分区,但å´å…·æœ‰WBFS文件系统ï¼\n" +"该设置无效,请按%s键将分区类型从扩展改为数æ®ã€‚" + +msgid "NTFS compression not supported!" +msgstr "" + +msgid "NTFS encryption not supported!" +msgstr "" + +msgid "Network connection established." +msgstr "网络连接已建立" + +msgid "Network error. Can't update gamercards." +msgstr "" + +msgid "New Themes" +msgstr "新增主题" + +msgid "Nintendo DS" +msgstr "支æŒNDSè”机(Nintendo DS)" + +msgid "Nintendo DS Connectivity" +msgstr "支æŒä¸ŽNDS掌机è”机" + +msgid "No" +msgstr "å¦" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "URL'%s'中没有域å部分" + +msgid "No games found!" +msgstr "没有å‘现游æˆï¼" + +msgid "No partition selected!" +msgstr "未选择分区ï¼" + +msgid "No themes found." +msgstr "未找到主题。" + +msgid "No updates found." +msgstr "未找到更新。" + +msgid "None found on disc" +msgstr "光盘上未找到内容" + +msgid "Not Found!" +msgstr "未找到ï¼" + +msgid "Number of Online Players" +msgstr "è”机玩家数é‡" + +msgid "Number of Players" +msgstr "玩家数é‡" + +msgid "Nunchuk" +msgstr "支æŒåŒèŠ‚æ£æ‰‹æŸ„(Wii Nunchuk)" + +msgid "OK" +msgstr "完毕" + +msgid "OK!" +msgstr "确认ï¼" + +msgid "Ocarina (cheats):" +msgstr "Ocarina(金手指):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina金手指管ç†å™¨" + +msgid "Ocarina: Code Error" +msgstr "Ocarina:作弊ç é”™è¯¯" + +msgid "Ocarina: Codes found." +msgstr "Ocarina:找到作弊ç ï¼" + +msgid "Ocarina: No codes found" +msgstr "Ocarina:未找到作弊ç " + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina:内存溢出错误" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina:正在æœç´¢ä½œå¼Šç ..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina:作弊ç è¿‡å¤š" + +msgid "Off" +msgstr "关闭" + +msgid "On" +msgstr "å¼€å¯" + +msgid "Online" +msgstr "" + +msgid "Online Content" +msgstr "支æŒåœ¨çº¿å†…容更新" + +msgid "Online Play" +msgstr "网络游æˆ" + +msgid "Online Players" +msgstr "" + +msgid "Online Score List" +msgstr "支æŒåœ¨çº¿æˆç»©æ¦œ" + +msgid "Online Updates" +msgstr "" + +msgid "Open Sort" +msgstr "" + +msgid "Open Style" +msgstr "" + +msgid "Opening DVD disc..." +msgstr "正在打开DVD光盘..." + +#, c-format +msgid "Opening partition: %s" +msgstr "" + +msgid "Options" +msgstr "" + +msgid "Options discarded for this game." +msgstr "已放弃ä¿å­˜æ­¤æ¸¸æˆè®¾ç½®" + +msgid "Options saved for this game." +msgstr "å·²ä¿å­˜æ­¤æ¸¸æˆè®¾ç½®" + +msgid "Out of memory" +msgstr "内存溢出" + +msgid "Page:" +msgstr "" + +msgid "Partition:" +msgstr "分区:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "分区:%s没有找到ï¼" + +msgid "Party" +msgstr "èšä¼šæ¸¸æˆ" + +msgid "Paused Start" +msgstr "æš‚åœå¼€å§‹" + +msgid "Platformer" +msgstr "å¹³å°åŠ¨ä½œæ¸¸æˆ" + +msgid "Play Count" +msgstr "游æˆæ¬¡æ•°" + +msgid "Players" +msgstr "" + +msgid "Please insert a game disc..." +msgstr "请æ’入游æˆå…‰ç›˜..." + +#, c-format +msgid "Press %s button for options." +msgstr "按%s键进入选项èœå•" + +#, c-format +msgid "Press %s button to %s." +msgstr "按%sé”®%s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "按%s键使用金手指" + +#, c-format +msgid "Press %s button to cancel." +msgstr "按%sé”®å–消" + +#, c-format +msgid "Press %s button to change device." +msgstr "按%s键更改设备" + +#, c-format +msgid "Press %s button to continue." +msgstr "按%s键继续" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "按%s键删除文件系统" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "按%s键转存BCA" + +#, c-format +msgid "Press %s button to exit." +msgstr "按%s键退出" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "按%s键修å¤æ‰©å±•åˆ†åŒºæˆ–WBFS分区" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "按%s键格å¼åŒ–WBFS分区" + +#, c-format +msgid "Press %s button to go back." +msgstr "按%s键返回" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "按%s键选择分区" + +#, c-format +msgid "Press %s button to select." +msgstr "按%s键选择" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "按%s键跳过作弊ç " + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "" + +#, fuzzy, c-format +msgid "Press %s for fullscreen preview" +msgstr "按1键进行全å±é¢„览" + +#, c-format +msgid "Press %s for game options" +msgstr "按%s键进入游æˆé€‰é¡¹èœå•" + +#, c-format +msgid "Press %s for global options" +msgstr "按%s键进入全局选项èœå•" + +#, c-format +msgid "Press %s to discard options" +msgstr "按%s键放弃ä¿å­˜é€‰é¡¹" + +#, c-format +msgid "Press %s to download and update" +msgstr "按%s键下载并进行更新" + +#, c-format +msgid "Press %s to download this theme" +msgstr "按%s键下载该款主题" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "按%s键下载,按%s键返回" + +#, c-format +msgid "Press %s to return" +msgstr "按%s键返回" + +#, c-format +msgid "Press %s to save global settings" +msgstr "按%sé”®ä¿å­˜å…¨å±€è®¾ç½®" + +#, c-format +msgid "Press %s to save options" +msgstr "按%sé”®ä¿å­˜é€‰é¡¹" + +#, c-format +msgid "Press %s to save selection" +msgstr "按%sé”®ä¿å­˜é€‰æ‹©" + +#, c-format +msgid "Press %s to select filter type" +msgstr "按%s键选择筛选方å¼" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "按%s键选择排åºæ–¹å¼" + +#, c-format +msgid "Press %s to start game" +msgstr "按%s键开始游æˆ" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "按%s键下载更新(ä¸æ›´æ–°meta.xmlæ述文件)" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "按%s/%s键选择设备" + +msgid "Press 2 to reload IOS" +msgstr "" + +msgid "Press A to select device" +msgstr "" + +msgid "Press B to exit to HBC" +msgstr "" + +msgid "Press HOME to reset" +msgstr "" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "" + +msgid "Press any button to continue..." +msgstr "按任æ„键继续..." + +msgid "Press any button to exit..." +msgstr "按任æ„键退出..." + +msgid "Press any button to restart..." +msgstr "按任æ„é”®é‡å¯..." + +msgid "Press any button..." +msgstr "按任æ„键继续..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "按%s键下载å°é¢ã€‚" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "按%s键弹出DVD光盘" + +msgid "Profile:" +msgstr "é…置:" + +#, c-format +msgid "Profile: %s" +msgstr "é…置文件:%s" + +msgid "Program Updates" +msgstr "" + +msgid "Publisher" +msgstr "å‘行商" + +msgid "Puzzle" +msgstr "益智游æˆ" + +msgid "Quit" +msgstr "" + +msgid "RAW" +msgstr "原始数æ®" + +msgid "RIGHT" +msgstr "" + +msgid "RPG" +msgstr "角色扮演游æˆ" + +msgid "Racing" +msgstr "竞速游æˆ" + +#, c-format +msgid "Rated %s" +msgstr "评级为%s" + +msgid "Rating" +msgstr "评分" + +msgid "Read" +msgstr "读å–" + +msgid "Reading BCA..." +msgstr "正在读å–BCA..." + +msgid "Reboot" +msgstr "" + +msgid "Release Date" +msgstr "å‘布日期" + +msgid "Release Notes: (short)" +msgstr "å‘布说明:(简介)" + +msgid "Removing game, please wait..." +msgstr "正在删除游æˆï¼Œè¯·ç¨å€™..." + +msgid "Restarting Wii..." +msgstr "正在é‡æ–°å¯åŠ¨Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "已返回ï¼(ret = %d)" + +msgid "Rhythm" +msgstr "节å¥æ¸¸æˆ" + +msgid "Rows:" +msgstr "" + +msgid "Running benchmark, please wait" +msgstr "正在è¿è¡ŒåŸºå‡†æµ‹è¯•ï¼Œè¯·ç¨å€™" + +msgid "S. Chinese" +msgstr "" + +msgid "SD/SDHC Card" +msgstr "SD/SDHCå¡" + +msgid "SUCCESS!" +msgstr "æˆåŠŸï¼" + +msgid "Save .gct" +msgstr "ä¿å­˜.gct文件" + +msgid "Save Debug" +msgstr "" + +msgid "Save Settings" +msgstr "" + +msgid "Save debug.log" +msgstr "" + +msgid "Saving Settings... " +msgstr "正在ä¿å­˜è®¾ç½®..." + +msgid "Saving cheats..." +msgstr "正在ä¿å­˜é‡‘手指..." + +msgid "Saving gamelist.txt ... " +msgstr "正在ä¿å­˜gamelist.txt..." + +msgid "Saving settings..." +msgstr "正在ä¿å­˜è®¾ç½®..." + +msgid "Saving:" +msgstr "" + +#, c-format +msgid "Saving: %s" +msgstr "正在ä¿å­˜ï¼š%s" + +msgid "Scroll:" +msgstr "" + +msgid "Select Alternative .dol:" +msgstr "请选择替代的.dol文件:" + +msgid "Select WBFS device:" +msgstr "请选择WBFS设备:" + +msgid "Select a different partition" +msgstr "请选择其它分区" + +msgid "Select a partition" +msgstr "请选择一个分区" + +msgid "Select all" +msgstr "全选" + +msgid "Select the game you want to boot" +msgstr "请选择想è¦è¿è¡Œçš„游æˆ" + +msgid "Selected Game" +msgstr "已选游æˆ" + +msgid "Settings" +msgstr "" + +msgid "Shooter" +msgstr "射击游æˆ" + +msgid "Show All" +msgstr "" + +msgid "Show cIOS info" +msgstr "" + +msgid "Shutdown" +msgstr "" + +msgid "Side:" +msgstr "" + +msgid "Simulation" +msgstr "模拟游æˆ" + +msgid "Size(GB) Type Mount Used" +msgstr "" + +#, c-format +msgid "Size: %d bytes" +msgstr "大å°ï¼š%d字节" + +msgid "Sort" +msgstr "" + +msgid "Sort Games" +msgstr "游æˆæŽ’åº" + +msgid "Sort Order:" +msgstr "" + +msgid "Sort Type:" +msgstr "" + +#, c-format +msgid "Sort: %s-%s" +msgstr "排åºï¼š%s-%s" + +msgid "Spanish" +msgstr "" + +msgid "Sports" +msgstr "体育游æˆ" + +msgid "Start" +msgstr "" + +msgid "Start Game" +msgstr "开始游æˆ" + +msgid "Start this game?" +msgstr "开始这个游æˆï¼Ÿ" + +msgid "Stopping DVD..." +msgstr "" + +msgid "Strategy" +msgstr "战略游æˆ" + +msgid "Style" +msgstr "" + +msgid "Style:" +msgstr "" + +msgid "Sync FAT free space info?" +msgstr "" + +msgid "Synchronizing, please wait." +msgstr "" + +msgid "System" +msgstr "" + +msgid "System Def." +msgstr "" + +msgid "T. Chinese" +msgstr "" + +msgid "Theme" +msgstr "主题" + +msgid "Theme Info" +msgstr "主题信æ¯" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "主题å¯èƒ½å¤ªå¤§äº†ï¼Œå»ºè®®é‡å¯Cfg。" + +msgid "Theme:" +msgstr "主题:" + +#, c-format +msgid "Theme: %s" +msgstr "主题:%s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "主题由wii.spiffy360.com网站æä¾›" + +msgid "Themes to download" +msgstr "å¯ä¸‹è½½ä¸»é¢˜" + +msgid "Themes with updates" +msgstr "å¯æ›´æ–°ä¸»é¢˜" + +msgid "Title" +msgstr "标题" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "作弊ç è¿‡å¤šï¼(%d)" + +msgid "Too many code lines!" +msgstr "作弊ç è¡Œæ•°è¿‡å¤šï¼" + +#, c-format +msgid "Too many fragments! %d" +msgstr "碎片过多ï¼%d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "å°è¯•(url#%d)..." + +msgid "UNUSED" +msgstr "" + +msgid "UP" +msgstr "" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL'%s'ä¸æ˜¯ä»¥'http://'开头的" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL'%s'中没有路径部分" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "å‘现USB Gecko,开å¯Debugging模å¼" + +msgid "USB Mass Storage Device" +msgstr "USB大容é‡å­˜å‚¨è®¾å¤‡" + +msgid "Unknown" +msgstr "未知" + +msgid "Unknown syntax!" +msgstr "未知语法ï¼" + +msgid "Unplayed" +msgstr "" + +msgid "Unplayed Games" +msgstr "未玩过的游æˆ" + +msgid "Update WiiTDB Game Database" +msgstr "更新游æˆæ•°æ®åº“(WiiTDB)" + +msgid "Update themes" +msgstr "主题更新" + +msgid "Update titles.txt" +msgstr "更新游æˆæ ‡é¢˜åº“(titles.txt)" + +msgid "Updates" +msgstr "" + +msgid "Updating database." +msgstr "正在更新数æ®åº“" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "已用空间:%.1fGB å¯ç”¨ç©ºé—´ï¼š%.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "使用已ä¿å­˜çš„替代.dol:" + +msgid "Version" +msgstr "版本å·" + +msgid "Video Patch:" +msgstr "视频补ä¸ï¼š" + +msgid "Video:" +msgstr "视频制å¼ï¼š" + +msgid "View" +msgstr "" + +msgid "Vitality Sensor" +msgstr "支æŒæ´»åŠ›æ„Ÿåº”器" + +msgid "WARNING:" +msgstr "警告:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS:剩余%.1fGB空间(å…±%.1fGB)" + +msgid "Wheel" +msgstr "支æŒæ–¹å‘盘" + +msgid "Wii Speak" +msgstr "支æŒä¸“用麦克风(Wii Speak)" + +msgid "WiiTDB Game Database" +msgstr "" + +msgid "Wiimote" +msgstr "支æŒé¥æŽ§å™¨æ‰‹æŸ„(Wii Remote)" + +msgid "Write Playlog:" +msgstr "记录游æˆæ—¥å¿—:" + +#, c-format +msgid "Writing to %s" +msgstr "正在写入到%s" + +#, c-format +msgid "Writing: %s" +msgstr "正在写入:%s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "错误大å°ï¼š%d (%d)" + +msgid "Yes" +msgstr "是" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" + +msgid "Zapper" +msgstr "支æŒæžªæ‰˜(Wii Zapper)" + +msgid "[ CHANGED ]" +msgstr "[ 已更改 ]" + +msgid "[ FOUND ]" +msgstr "[ 已找到 ]" + +msgid "[ SAVED ]" +msgstr "[ å·²ä¿å­˜ ]" + +msgid "[HOME]" +msgstr "" + +msgid "[No HQ]" +msgstr "" + +msgid "[USED]" +msgstr "[已使用]" + +msgid "[default]" +msgstr "" + +msgid "[saved]" +msgstr "" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "æŸåçš„WBFS文件:%s" + +msgid "calculating space, please wait..." +msgstr "正在计算所需空间,请ç¨å€™..." + +msgid "delete" +msgstr "删除" + +#, c-format +msgid "dest: %p - %p" +msgstr "dest: %p - %p" + +msgid "discard" +msgstr "" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "无法解æžåŸŸå%s" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "读å–错误:%s (%d)" + +#, c-format +msgid "for %d players" +msgstr "支æŒ%d人游æˆ" + +msgid "for 1 player" +msgstr "å•äººæ¸¸æˆ" + +msgid "format" +msgstr "æ ¼å¼åŒ–" + +msgid "free" +msgstr "" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "线性读å–速度:%.2f mb/s" + +msgid "linear..." +msgstr "线性..." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "背景音ä¹æ–‡ä»¶è¿‡å¤§(%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "没有找到music.mp3或music.modï¼" + +msgid "no file" +msgstr "æ— å¯ç”¨æ–‡ä»¶" + +msgid "none" +msgstr "æ— " + +msgid "or format a WBFS partition." +msgstr "或格å¼åŒ–WBFS分区" + +msgid "page" +msgstr "页" + +msgid "parse error" +msgstr "解æžé”™è¯¯" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "éšæœºè¯»å–速度:%.2f mb/s" + +msgid "random..." +msgstr "éšæœº..." + +msgid "reset" +msgstr "" + +msgid "revert" +msgstr "" + +msgid "save" +msgstr "ä¿å­˜" + +#, c-format +msgid "split error: %s" +msgstr "分割错误:%s" + +msgid "uDraw GameTablet" +msgstr "" + +msgid "unable to open wii disc" +msgstr "无法打开Wii光盘" + +#, c-format +msgid "used: %p - %p" +msgstr "已使用:%p - %p" + +#~ msgid "Loading previous game list..." +#~ msgstr "正在读å–上次游æˆåˆ—表..." + +#~ msgid "Remove Game" +#~ msgstr "删除游æˆ" diff --git a/Languages/ZH_CN.lang b/Languages/ZH_CN.lang new file mode 100644 index 0000000..d09e8cc --- /dev/null +++ b/Languages/ZH_CN.lang @@ -0,0 +1,2363 @@ +# Configurable USB Loader ZH-CN By 91wii.com +# Copyright (C) 2010 91wii.Com THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Kavid , 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: 2014-04-20 16:50+GMT+8\n" +"Last-Translator: kavid \n" +"Language-Team: kavid \n" +"MIME-Version: 2.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: Simplified Chinese\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB空间剩余(å…±%.1fGB)" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGBå·²å¤åˆ¶äºŽ%d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d个å¯ç”¨" + +#, c-format +msgid "%d more notes" +msgstr "%d更多注释" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s 分割:%d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s,请ç¨å€™..." + +#, c-format +msgid "%s: Favorites" +msgstr "%s:收è—" + +#, c-format +msgid "%s: GUI " +msgstr "%s:ç•Œé¢ " + +#, c-format +msgid "%s: Options " +msgstr "%s:选项 " + +#, c-format +msgid "(%d online)" +msgstr "(%d在线)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d秒超时)" + +msgid "(This can take a couple of minutes)" +msgstr "(å¯èƒ½ä¼šèŠ±ä¸€äº›æ—¶é—´)" + +msgid ".dol too small" +msgstr ".dol文件太å°" + +msgid "1.2+" +msgstr "1.2以上版本" + +msgid "1st-Person Shooter" +msgstr "第一人称射击" + +msgid "2.0+" +msgstr "2.0以上版本" + +msgid "2.1+" +msgstr "2.1以上版本" + +msgid "2.2+" +msgstr "2.2以上版本" + +msgid "3D cover" +msgstr "3Då°é¢" + +msgid "3rd-person Shooter" +msgstr "第三人称射击" + +msgid "< ASC >" +msgstr "< å‡åº >" + +msgid "< DESC >" +msgstr "< é™åº >" + +msgid "< DOWNLOAD >" +msgstr "< 下载 >" + +msgid "About" +msgstr "关于" + +msgid "Action" +msgstr "动作游æˆ" + +msgid "Additional config:" +msgstr "附加设置:" + +msgid "Admin Lock:" +msgstr "å¯åŠ¨ç®¡ç†" + +msgid "Admin Unlock" +msgstr "关闭管ç†" + +msgid "Adult" +msgstr "æˆäººç±»åž‹" + +msgid "Adventure" +msgstr "冒险游æˆ" + +msgid "All" +msgstr "全部" + +msgid "All Channels" +msgstr "所有频é“" + +msgid "All Games" +msgstr "所有游æˆ" + +msgid "All themes up to date." +msgstr "所有主题å‡å·²æ›´æ–°è‡³æœ€æ–°ç‰ˆ" + +msgid "Alt Button Cfg:" +msgstr "选择CFG的按键" + +msgid "Alt dol:" +msgstr "选择DOL" + +msgid "Alternative .dol:" +msgstr "选择.dol:" + +msgid "Anti 002 Fix:" +msgstr "防002错误修正:" + +#, c-format +msgid "App. Path: %s" +msgstr "应用程åºè·¯å¾„:%s" + +msgid "Arcade" +msgstr "街机" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"你确定è¦ä½¿ç”¨%s\n" +"此分区?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"你确定è¦ä¿®å¤\n" +"此分区?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "你确定è¦åˆ é™¤æ­¤æ¬¾æ¸¸æˆï¼Ÿ" + +msgid "Ascending" +msgstr "å‡åºæŽ’列" + +msgid "Auto" +msgstr "自动" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "自动è¿è¡Œæ¸¸æˆï¼šæœªæ‰¾åˆ°%.6sï¼" + +msgid "Available Updates" +msgstr "å¯ç”¨æ›´æ–°" + +msgid "Back" +msgstr "返回" + +msgid "Balance Board" +msgstr "对应平衡æ¿" + +msgid "Baseball" +msgstr "棒çƒ" + +msgid "Basic" +msgstr "基本" + +msgid "Basketball" +msgstr "篮çƒ" + +msgid "Bike Racing" +msgstr "自行车比赛" + +msgid "Billiards" +msgstr "å°çƒ" + +msgid "Block IOS Reload:" +msgstr "阻止IOSé‡åŠ è½½ï¼š" + +msgid "Board Game" +msgstr "平衡æ¿æ¸¸æˆ" + +msgid "Boot Disc" +msgstr "å¯åŠ¨å…‰ç›˜" + +msgid "Boot disc" +msgstr "è¿è¡Œå…‰ç›˜" + +msgid "Booting game, please wait..." +msgstr "正在å¯åŠ¨æ¸¸æˆï¼Œè¯·ç¨ç­‰..." + +msgid "Bowling" +msgstr "ä¿é¾„çƒè¿åŠ¨" + +msgid "Boxing" +msgstr "拳击游æˆ" + +msgid "Business Sim" +msgstr "商业模拟游æˆ" + +#, c-format +msgid "CFG base: %s" +msgstr "CFG存储ä½ç½®ï¼š%s" + +msgid "Camera" +msgstr "æ‘„åƒå¤´" + +msgid "Can not dump GC savegames.\n" +msgstr "无法转存GC的游æˆå­˜æ¡£ã€‚\n" + +msgid "Can't install Wii games!" +msgstr "无法安装Wii游æˆï¼" + +msgid "Cancelled." +msgstr "å·²å–消。" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "无法建立目录:%s" + +msgid "Cards" +msgstr "å¡ç‰‡ç±»" + +msgid "Channel" +msgstr "频é“" + +msgid "Cheat Codes:" +msgstr "金手指代ç ï¼š" + +msgid "Cheats: " +msgstr "金手指:" + +msgid "Check For Updates" +msgstr "检查更新" + +msgid "Checking for themes..." +msgstr "正在检查主题更新" + +msgid "Checking for updates..." +msgstr "正在检查更新..." + +msgid "Chess" +msgstr "象棋" + +msgid "Choose a sorting method" +msgstr "请选择一ç§æŽ’åºæ–¹å¼" + +msgid "Classic" +msgstr "对应ç»å…¸æ‰‹æŸ„" + +msgid "Classic Controller" +msgstr "对应ç»å…¸æ‰‹æŸ„" + +msgid "Clear" +msgstr "清除" + +msgid "Clear Patches:" +msgstr "清除补ä¸ï¼š" + +msgid "Coaching" +msgstr "训练类型" + +msgid "Compare Type" +msgstr "比较å¼" + +msgid "Compilation" +msgstr "åˆé›†ç±»æ¸¸æˆ" + +#, c-format +msgid "Complete. Size: %d" +msgstr "完æˆï¼Œå¤§å°ï¼š%d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "设置错误,已到达MAX_DNS_ENTRIES且列表为空" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "从net_read()网络连接错误,错误代ç ï¼š%i" + +msgid "Console" +msgstr "主机" + +msgid "Construction Sim" +msgstr "建筑模拟" + +msgid "Contains" +msgstr "包å«" + +msgid "Controller" +msgstr "手柄" + +msgid "Cooking" +msgstr "烹饪类" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "无法åˆå§‹åŒ–DIP模å—ï¼(ret = %d)" + +msgid "Country Fix:" +msgstr "国家代ç ä¿®æ­£ï¼š" + +msgid "Cover" +msgstr "å°é¢" + +msgid "Cover Image:" +msgstr "游æˆå°é¢ï¼š" + +msgid "Cover Style" +msgstr "å°é¢ç±»åž‹" + +msgid "Covers Available" +msgstr "å¯ç”¨å°é¢" + +msgid "Cover~~Back" +msgstr "背é¢" + +msgid "Cover~~Front" +msgstr "æ­£é¢" + +msgid "Create" +msgstr "创建" + +msgid "Creator" +msgstr "主题作者" + +msgid "Cricket" +msgstr "æ¿çƒ" + +#, c-format +msgid "Current Version: %s" +msgstr "当å‰ç‰ˆæœ¬ï¼š%s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "cIOS未找到,请安装cIOS" + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "无法加载cIOS %dï¼(ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "cIOS有错误请é‡æ–°å®‰è£…cIOS" + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "cIOS %s加载æˆåŠŸï¼" + +msgid "DEVO" +msgstr "DEVO" + +msgid "DISC cover" +msgstr "光盘å°é¢" + +msgid "DOWN" +msgstr "å‘下" + +msgid "Dance" +msgstr "跳舞类" + +msgid "Dance Pad" +msgstr "对应跳舞毯" + +msgid "Darts" +msgstr "掷飞镖游æˆ" + +msgid "Database update successful." +msgstr "æ•°æ®åº“æ›´æ–°æˆåŠŸã€‚" + +msgid "Debug" +msgstr "调试" + +msgid "Delete Game" +msgstr "删除游æˆ" + +msgid "Deleting" +msgstr "删除中" + +msgid "Descending" +msgstr "é™åºæŽ’列" + +msgid "Developer" +msgstr "å¼€å‘å°ç»„" + +msgid "Device is not responding!" +msgstr "设备无å“应ï¼" + +msgid "Device:" +msgstr "设备:" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "Devolutionåªèƒ½è¿è¡Œå®Œæ•´NGC游æˆçš„é•œåƒ!\n" + +msgid "Devolution:" +msgstr "Devolution:" + +msgid "Disc" +msgstr "光盘" + +msgid "Disc (Ask)" +msgstr "光盘(请求)" + +msgid "Done." +msgstr "完æˆï¼" + +msgid "Download .txt" +msgstr "下载.txt" + +msgid "Download All Covers" +msgstr "下载所有å°é¢" + +msgid "Download All Missing Covers" +msgstr "下载所有缺失的游æˆå°é¢" + +msgid "Download Missing Covers" +msgstr "下载所有缺失å°é¢" + +msgid "Download Plugins" +msgstr "下载æ’件" + +msgid "Download Themes" +msgstr "下载主题" + +msgid "Download complete." +msgstr "下载完æˆï¼" + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "游æˆå¡ä¸‹è½½é”™è¯¯ #%d。" + +msgid "Download game information" +msgstr "下载游æˆä¿¡æ¯" + +msgid "Downloadable Content" +msgstr "支æŒä¸‹è½½å¢žå€¼å†…容(DLC)" + +msgid "Downloading ALL MISSING covers" +msgstr "正在下载所有缺失的å°é¢" + +msgid "Downloading ALL covers" +msgstr "正在下载所有å°é¢" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "正在下载%.6s的所有å°é¢" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "正在下载%.6s缺失的å°é¢" + +msgid "Downloading Theme previews..." +msgstr "下载主题预览中......" + +msgid "Downloading cheats..." +msgstr "正在下载金手指..." + +msgid "Downloading database." +msgstr "正在下载数æ®åº“" + +msgid "Downloading devolution." +msgstr "正在下载devolution。" + +msgid "Downloading mighty plugin." +msgstr "正在下载mightyæ’件。" + +msgid "Downloading neek2o plugin." +msgstr "正在下载neek2o æ’件。" + +msgid "Downloading titles.txt ..." +msgstr "正在下载titles.txt文件..." + +msgid "Downloading translation file." +msgstr "正在下载语言文件" + +msgid "Downloading unifont." +msgstr "正在下载字库文件" + +#, c-format +msgid "Downloading: %s" +msgstr "正在下载:%s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "正在下载:%s as %s" + +msgid "Downloads" +msgstr "下载次数" + +msgid "Drawing" +msgstr "绘图类游æˆ" + +msgid "Drums" +msgstr "对应太鼓" + +msgid "Dump savegame" +msgstr "转存游æˆå­˜æ¡£" + +msgid "Duplicate ID3" +msgstr "å¤åˆ¶ID3" + +msgid "Dutch" +msgstr "è·å…°è¯­" + +msgid "ERROR" +msgstr "错误" + +msgid "ERROR creating file" +msgstr "创建文件时错误" + +msgid "ERROR game opt" +msgstr "游æˆé€‰é¡¹é”™è¯¯" + +msgid "ERROR reading BCA!" +msgstr "BCA读å–错误ï¼" + +#, c-format +msgid "ERROR removing %s" +msgstr "移除%s时错误" + +msgid "ERROR writing BCA!" +msgstr "BCA写入错误" + +msgid "ERROR!" +msgstr "错误ï¼" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "错误ï¼(ret = %d)" + +msgid "ERROR:" +msgstr "错误:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "错误:无法访问%s" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "错误:Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "错误:BCA功能需è¦å®‰è£…cIOS222/223 v4以上版本" + +msgid "ERROR: Cache Close" +msgstr "错误:缓存关闭" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "错误:无法打开游æˆï¼(ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "错误:游æˆå·²å®‰è£…ï¼ï¼" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "错误:GetCerts %d" + +msgid "ERROR: Invalid Game ID" +msgstr "错误:无效的游æˆID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "错误:读å–EHC模å—! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"错误:NTFS分区写数æ®åŠŸèƒ½ç¦ç”¨ï¼\n" +"请在config.txt添加å‚æ•°ntfs_write=1" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "错误:没有找到分区ï¼(ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "错误:ä¸æ˜¯Wii专用光盘ï¼ï¼" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "错误:Offset(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "错误:打开分区 %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "错误:打开分区(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "错误:选择片段列表(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "错误:选择WBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "错误:设置SD模å¼" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "错误:USBåˆå§‹åŒ–ï¼(%d)" + +msgid "ERROR: WBFS not mounted!" +msgstr "错误:WBFS没有被加载ï¼" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"错误:从FAT或者NTFS分区è¿è¡Œæ¸¸æˆ,\n" +"需安装cIOS249 REV18,Herms cIOS v4或d2x最新或ç€æ›´é«˜ç‰ˆæœ¬ã€‚" + +msgid "ERROR: cache: out of memory" +msgstr "错误:缓存:内存溢出" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "错误:正在创建:%s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "错误:get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "错误:内存é‡å ï¼" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "支æŒWBFS多分区错误:仅cIOS222/223-mload支æŒæ­¤åŠŸèƒ½\n" + +msgid "ERROR: not enough free space!!" +msgstr "错误:剩余容é‡ä¸è¶³ï¼ï¼" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "错误:ntfs分区完全无法挂载。" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "错误:打开%.6sæ—¶" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "错误:set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "错误:建立碎片%d %dæ—¶" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "错误:无法链接到存储设备或ç€ç¡¬ç›˜åˆ†åŒº %s ï¼ï¼" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "错误:写入%s (%d)æ—¶." + +msgid "EXTEND" +msgstr "扩展" + +msgid "Educational" +msgstr "培训类" + +msgid "Ejecting DVD..." +msgstr "正在弹出DVD..." + +msgid "English" +msgstr "英语" + +msgid "English Title" +msgstr "英文标题" + +msgid "Enter Code: " +msgstr "输入代ç ï¼š" + +msgid "Error GRRLIB init" +msgstr "GRRLIBåˆå§‹åŒ–错误" + +msgid "Error Initializing Network." +msgstr "网络åˆå§‹åŒ–错误ï¼" + +msgid "Error adding disc!" +msgstr "添加光盘错误ï¼" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "缓冲区分é…错误:%d" + +msgid "Error creating directory..." +msgstr "创建目录错误..." + +msgid "Error discarding options!" +msgstr "放弃选项错误ï¼" + +msgid "Error downloading theme preview..." +msgstr "下载主题预览错误..." + +msgid "Error downloading theme..." +msgstr "下载主题出错..." + +msgid "Error downloading themes..." +msgstr "下载主题出错..." + +msgid "Error downloading update..." +msgstr "下载更新出错..." + +msgid "Error downloading updates..." +msgstr "下载更新出错..." + +msgid "Error downloading." +msgstr "下载时出错ï¼" + +msgid "Error establishing connection" +msgstr "建立连接时错误" + +msgid "Error extracting theme..." +msgstr "æå–主题出错" + +msgid "Error opening database, update did not complete." +msgstr "æ•°æ®åº“打开时错误,更新未完æˆ." + +#, c-format +msgid "Error opening: %s" +msgstr "打开时错误:%s" + +#, c-format +msgid "Error playing %s" +msgstr "游æˆæ—¶é”™è¯¯ %s" + +msgid "Error reading .dol" +msgstr ".dol读å–时错误" + +msgid "Error reading dol header" +msgstr "dol头部信æ¯è¯»å–错误" + +#, c-format +msgid "Error saving %s" +msgstr "ä¿å­˜æ—¶é”™è¯¯ %s" + +msgid "Error saving options!" +msgstr "ä¿å­˜é€‰é¡¹æ—¶é”™è¯¯ï¼" + +msgid "Error saving settings!" +msgstr "ä¿å­˜è®¾ç½®æ—¶é”™è¯¯ï¼" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"ä¿å­˜æ¸¸æˆæ—¥å¿—时错误ï¼\n" +"请从Wii主èœå•å¯åŠ¨æ¥è¿›è¡Œä¿®å¤" + +msgid "Error: Invalid PNG image!" +msgstr "错误:无效的PNG图片ï¼" + +msgid "Error: no URL." +msgstr "错误:没有URL链接!" + +msgid "Error: no data." +msgstr "错误:无数æ®ï¼" + +msgid "Exercise" +msgstr "å¥èº«ç±»" + +msgid "Exit" +msgstr "退出" + +#, c-format +msgid "Extracting: %s" +msgstr "æå–中:" + +msgid "FAIL" +msgstr "失败" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT:é…置错误 %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "严é‡é”™è¯¯ï¼šé…置网格(%d)" + +#, c-format +msgid "FILL %d" +msgstr "å¡«å……%d" + +msgid "FLAT cover" +msgstr "å¹³é¢å°é¢" + +msgid "FULL cover" +msgstr "完整å°é¢" + +msgid "Fav" +msgstr "收è—" + +msgid "Fav: Off" +msgstr "收è—:关闭" + +msgid "Fav: On" +msgstr "收è—:开始" + +msgid "Favorite" +msgstr "收è—" + +msgid "Favorite Games" +msgstr "最喜欢的游æˆ" + +msgid "Favorite:" +msgstr "我的最爱:" + +msgid "Favorites:" +msgstr "我的最爱:" + +msgid "Fighting" +msgstr "格斗游æˆ" + +#, c-format +msgid "File not found! %s" +msgstr "未找到文件ï¼%s" + +msgid "Filter" +msgstr "过滤器" + +msgid "Filter Games" +msgstr "游æˆç­›é€‰" + +msgid "Filter by Controller" +msgstr "按æ“作方å¼ç­›é€‰" + +msgid "Filter by Game Type" +msgstr "按游æˆç±»åž‹ç­›é€‰" + +msgid "Filter by Genre" +msgstr "过滤器类型" + +msgid "Filter by Online Features" +msgstr "按网络特性筛选" + +msgid "Filter:" +msgstr "过滤器:" + +msgid "Fishing" +msgstr "钓鱼类" + +msgid "Fitness" +msgstr "å¥èº«ç±»" + +msgid "Fixing EXTEND partition..." +msgstr "正在修å¤æ‰©å±•åˆ†åŒº..." + +msgid "Flight Sim" +msgstr "飞行模拟" + +msgid "Football" +msgstr "足çƒ" + +msgid "Force Devolution:" +msgstr "强制Devolution:" + +msgid "Force NTSC" +msgstr "强制NTSC" + +msgid "Force NTSC 480p" +msgstr "强制NTSC 480P" + +msgid "Force PAL" +msgstr "强制 PAL" + +msgid "Force PAL 480p" +msgstr "强制 PAL 480P" + +msgid "Force PAL50" +msgstr "强制PAL50" + +msgid "Force PAL60" +msgstr "强制PAL60" + +msgid "Formatting" +msgstr "正在格å¼åŒ–中" + +#, c-format +msgid "Found %s" +msgstr "找到 %s" + +msgid "French" +msgstr "法语" + +msgid "Full" +msgstr "å…¨å°é¢" + +msgid "Futuristic Racing" +msgstr "æžé€Ÿç«žé€Ÿèµ›è½¦" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "游æˆç¼ºçœ" + +msgid "Game ID" +msgstr "游æˆID" + +msgid "Game Options" +msgstr "游æˆé€‰é¡¹" + +#, c-format +msgid "Game Options: %s" +msgstr "游æˆé€‰é¡¹ï¼š%s" + +msgid "Game Type" +msgstr "游æˆç±»åž‹" + +msgid "GameCube" +msgstr "GAMECUBE" + +msgid "Gamecube" +msgstr "对应NGC手柄" + +msgid "Gamer Card:" +msgstr "游æˆå¡" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "游æˆå¡ #%d 宣布: %.*s " + +msgid "Genre" +msgstr "风格" + +msgid "German" +msgstr "德语" + +msgid "Global Options" +msgstr "全局选项" + +msgid "Golf" +msgstr "高尔夫" + +msgid "Guitar" +msgstr "对应å‰ä»–" + +msgid "HQ cover" +msgstr "HQå°é¢" + +msgid "HTTP Response was without a file" +msgstr "HTTPå“应没有文件" + +msgid "Health" +msgstr "å¥åº·" + +msgid "Hidden Object" +msgstr "éšè—项目" + +msgid "Hide" +msgstr "éšè—" + +msgid "Hide Game:" +msgstr "éšè—游æˆï¼š" + +msgid "Hockey" +msgstr "冰çƒ" + +msgid "Hold button B to cancel." +msgstr "按ä½Bé”®å–消" + +msgid "Home Menu" +msgstr "主èœå•" + +msgid "Homebrew Channel" +msgstr "Homebrew Channel" + +msgid "Hook Type:" +msgstr "é’©å­Type:" + +msgid "Hunting" +msgstr "狩猎类" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "IDä¸åŒ¹é…:[%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOSé‡åŠ è½½ï¼šå·²é˜»æ­¢" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s)失败,代ç ï¼š%d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"如果CFG程åºåœ¨å¼€å§‹è¿è¡Œä¸æ­£ç¡®\n" +"下次,把boot。dol,bakæ›´åæ¢å¤\n" +"boot.dol然åŽå†æ¬¡è¿è¡Œã€‚" + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "LZ77在编ç %xæ—¶å‘生冲çª" + +msgid "Infinity Base" +msgstr "æ— é™åŸºåœ°" + +msgid "Info" +msgstr "ä¿¡æ¯" + +#, c-format +msgid "Info file: %s" +msgstr "ä¿¡æ¯æ–‡ä»¶ï¼š%s" + +msgid "Initializing Network..." +msgstr "正在åˆå§‹åŒ–网络..." + +msgid "Install" +msgstr "安装" + +msgid "Install Date" +msgstr "安装日期" + +msgid "Install Game" +msgstr "安装游æˆ" + +msgid "Install game" +msgstr "安装游æˆ" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "安装出错ï¼(ret = %d)" + +msgid "Installing game, please wait..." +msgstr "正在安装游æˆï¼Œè¯·ç¨å€™..." + +msgid "Invalid .dol" +msgstr "无效的.dol文件" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "无效分区:'%s'" + +msgid "Italian" +msgstr "æ„大利语" + +msgid "Japanese" +msgstr "日语" + +msgid "Japanese Title" +msgstr "日语标题" + +msgid "Jump" +msgstr "跳水类" + +msgid "Karaoke" +msgstr "å¡æ‹‰OK" + +msgid "Kart Racing" +msgstr "å¡ä¸è½¦ç«žèµ›" + +msgid "Keyboard" +msgstr "对应键盘" + +msgid "Korean" +msgstr "韩语" + +msgid "LED:" +msgstr "LEDç¯å…‰æ•ˆæžœ:" + +msgid "LEFT" +msgstr "å‘å·¦" + +msgid "LOCKED!" +msgstr "å·²é”定ï¼" + +msgid "Language:" +msgstr "语言:" + +msgid "Last Play Date" +msgstr "最近一次游æˆæ—¥æœŸ" + +msgid "Launch Methods" +msgstr "å¯åŠ¨æ–¹å¼" + +msgid "Life Simulation" +msgstr "模拟生活类" + +msgid "Load OK!" +msgstr "读å–æˆåŠŸï¼" + +#, c-format +msgid "Loader Version: %s" +msgstr "程åºç‰ˆæœ¬ï¼š%s" + +msgid "Loading ..." +msgstr "读å–中..." + +#, c-format +msgid "Loading..%s\n" +msgstr "正在读å–..%s\n" + +msgid "Main" +msgstr "主èœå•" + +msgid "Main Menu" +msgstr "主èœå•" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"确认USBæ•°æ®çº¿ä¸»æŽ¥å£æ˜¯å¦å·²è¿žæŽ¥åˆ°Wiiçš„USB接å£ç«¯å£0ä½ç½®ï¼\n" +"(Port0ä½ç½®æ示:正视Wii主机背é¢ï¼Œæœå³è¾¹çš„USB接å£)" + +msgid "Manage" +msgstr " ç®¡ç† " + +msgid "Manage Cheats" +msgstr "管ç†é‡‘手指" + +msgid "Management Sim" +msgstr "模拟管ç†ç±»" + +msgid "Martial Arts" +msgstr "武术技击" + +msgid "Microphone" +msgstr "对应麦克风" + +msgid "Mighty Plugin" +msgstr "Mightyæ’件" + +msgid "Motion+" +msgstr "对应Wii动感强化器" + +msgid "Motorcycle Racing" +msgstr "摩托车竞赛" + +msgid "Mounting device, please wait..." +msgstr "正在加载设备,请ç¨å€™..." + +msgid "Music" +msgstr "音ä¹æ¸¸æˆ" + +#, c-format +msgid "Music file from dir: %s" +msgstr "背景音ä¹æ–‡ä»¶ç›®å½•ï¼š%s" + +#, c-format +msgid "Music file size: %d" +msgstr "背景音ä¹æ–‡ä»¶å¤§å°ï¼š%d" + +#, c-format +msgid "Music file: %s" +msgstr "背景音ä¹æ–‡ä»¶ï¼š%s" + +msgid "Music: Disabled" +msgstr "背景音ä¹ï¼šç¦ç”¨" + +msgid "Music: Enabled" +msgstr "背景音ä¹ï¼šå¼€å¯" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "背景音ä¹ï¼šæ­£åœ¨å¯»æ‰¾%s文件ä½ç½®åœ¨: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "背景音ä¹ï¼šæ’­æ”¾ä¸‹ä¸€ä¸ªæ–‡ä»¶ï¼š%i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "背景音ä¹ï¼šæ‰¾åˆ°%s个文件:%i" + +msgid "Music: musicArray contents: " +msgstr "背景音ä¹ï¼šéŸ³ä¹ç»„内容:" + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "模拟内存路径::\n" + +msgid "NAND Emu:" +msgstr "内存模拟:" + +msgid "NMM:" +msgstr "å…记忆å¡" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"注æ„:cIOS249 REV10早期的版本:\n" +"ä¸æ”¯æŒä»ŽSDHC存储å¡è¯»å–游æˆï¼" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"注æ„:cIOS249 REV14之å‰çš„版本:\n" +"很å¯èƒ½è¿˜æœªå¤„ç†001错误,导致游æˆæ— æ³•è¿è¡Œï¼" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"注æ„: cIOS249 REV9之å‰çš„版本:\n" +"ä¸æ”¯æŒä»ŽUSB设备读å–游æˆï¼" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"注æ„:你正在使用cIOS249 REV13:\n" +"退出游æˆåŽå¿…须关机或é‡å¯Wii,\n" +"å¦åˆ™å†è¿è¡Œå…¶å®ƒæ¸¸æˆæ—¶ä¼šç›´æŽ¥æ­»æœºï¼" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"注æ„:你正在使用cIOS249 REV14:\n" +"该版本已知与D9光盘有兼容性问题,\n" +"建议使用最新版或其它cIOS安装è¿è¡Œè¯¥æ¸¸æˆï¼" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"注æ„:程åºéœ€è¦é‡æ–°å¯åŠ¨\n" +"以使更新生效" + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" +"注æ„:cfg程åºéœ€è¦é‡æ–°å¯åŠ¨\n" +"以便使得设置生效。" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"注æ„:P#%d分区是扩展分区,但å´å…·æœ‰WBFS文件系统ï¼\n" +"该设置无效,请按%s键将分区类型从扩展改为数æ®ã€‚" + +msgid "NTFS compression not supported!" +msgstr "ä¸æ”¯æŒNTFS压缩ï¼ï¼" + +msgid "NTFS encryption not supported!" +msgstr "ä¸æ”¯æŒNTFS加密ï¼ï¼ï¼" + +msgid "NTSC-J patch:" +msgstr "日本游æˆè¡¥ä¸ï¼š" + +msgid "Neek2o Plugin" +msgstr "Neek2oæ’件" + +msgid "Network connection established." +msgstr "网络连接已建立" + +msgid "Network error. Can't update gamercards." +msgstr "网络错误。无法更新游æˆå¡ã€‚" + +msgid "New Themes" +msgstr "新主题" + +msgid "Nintendo DS" +msgstr "对应Nintendo DS" + +msgid "Nintendo DS Connectivity" +msgstr "支æŒä¸ŽNintendo DSè”动" + +msgid "No" +msgstr "å¦" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "URL'%s'中没有域å部分" + +msgid "No games found!" +msgstr "没有å‘现游æˆ!" + +msgid "No partition selected!" +msgstr "未选择分区ï¼" + +msgid "No themes found." +msgstr "未找到主题" + +msgid "No updates found." +msgstr "未找到更新" + +msgid "NoDisc:" +msgstr "å…光盘:" + +msgid "None found on disc" +msgstr "光盘上未找到内容" + +msgid "Not Found boot.dol!" +msgstr "没有找到boot.dol文件" + +msgid "Not Found!" +msgstr "未找到!" + +msgid "Number of Online Players" +msgstr "在线游æˆäººæ•°" + +msgid "Number of Players" +msgstr "游æˆäººæ•°" + +msgid "Nunchuk" +msgstr "对应WiiåŒèŠ‚æ£å·¦å³æ‰‹æŸ„" + +msgid "OK" +msgstr "完毕" + +msgid "OK!" +msgstr "确认ï¼" + +msgid "Ocarina (cheats):" +msgstr "Ocarina(金手指):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina金手指管ç†å™¨" + +msgid "Ocarina: Code Error" +msgstr "Ocarina:代ç é”™è¯¯" + +msgid "Ocarina: Codes found." +msgstr "Ocarina:找到代ç ï¼" + +msgid "Ocarina: No codes found" +msgstr "Ocarina:未找到代ç " + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina:内存溢出错误" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina:正在æœç´¢ä»£ç ..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina:代ç è¿‡å¤š" + +msgid "Off" +msgstr "关闭" + +msgid "Off-Road Racing" +msgstr "越野赛车" + +msgid "On" +msgstr "å¼€å¯" + +msgid "Online" +msgstr "在线" + +msgid "Online Content" +msgstr "支æŒåœ¨çº¿æ›´æ–°" + +msgid "Online Play" +msgstr "支æŒWi-Fi在线游æˆ" + +msgid "Online Players" +msgstr "在线玩家" + +msgid "Online Score List" +msgstr "在线æˆç»©æŽ’行榜" + +msgid "Online Updates" +msgstr "在线更新" + +msgid "Open Sort" +msgstr "打开排åº" + +msgid "Open Style" +msgstr "打开类型" + +msgid "Opening DVD disc..." +msgstr "正在打开DVD光盘..." + +#, c-format +msgid "Opening partition: %s" +msgstr "正在打开分区: %s" + +msgid "Options" +msgstr "选项" + +msgid "Options discarded for this game." +msgstr "已放弃ä¿å­˜æ­¤æ¸¸æˆè®¾ç½®" + +msgid "Options saved for this game." +msgstr "å·²ä¿å­˜æ­¤æ¸¸æˆè®¾ç½®" + +msgid "Out of memory" +msgstr "内存溢出" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" + +msgid "PAD HOOK" +msgstr "PADé’©å­" + +msgid "PAD HOOK:" +msgstr "PADé’©å­" + +msgid "Page:" +msgstr "页ç ï¼š" + +msgid "Partial" +msgstr "局部" + +msgid "Partition:" +msgstr "分区:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "分区:%s没有找到ï¼" + +msgid "Party" +msgstr "èšä¼šæ¸¸æˆ" + +msgid "Paused Start" +msgstr "æš‚åœå¼€å§‹" + +msgid "Petanque" +msgstr "法å¼é•¿æ»šçƒæ¸¸æˆ" + +msgid "Pinball" +msgstr "å¼¹çƒæ¸¸æˆ" + +msgid "Platformer" +msgstr "å¹³å°æ¸¸æˆ" + +msgid "Play Count" +msgstr "游æˆæ¬¡æ•°" + +msgid "Players" +msgstr "玩家" + +msgid "Please insert a game disc..." +msgstr "请放入游æˆå…‰ç›˜..." + +msgid "Plugin:" +msgstr "æ’件:" + +msgid "Poker" +msgstr "纸牌类" + +msgid "Portal of Power" +msgstr "力é‡ä¹‹é—¨" + +#, c-format +msgid "Press %s button for options." +msgstr "按%s键进入选项èœå•" + +#, c-format +msgid "Press %s button to %s." +msgstr "按%sé”®%s" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "按%s键使用金手指" + +#, c-format +msgid "Press %s button to cancel." +msgstr "按%sé”®å–消" + +#, c-format +msgid "Press %s button to change device." +msgstr "按%s键更改设备" + +#, c-format +msgid "Press %s button to continue." +msgstr "按%s键继续" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "按%s键删除文件系统" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "按%s键转存BCA" + +#, c-format +msgid "Press %s button to exit." +msgstr "按%s键退出" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "按%s键修å¤æ‰©å±•åˆ†åŒºæˆ–WBFS分区" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "按%s键格å¼åŒ–WBFS分区" + +#, c-format +msgid "Press %s button to go back." +msgstr "按%s键返回" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "按%s键选择分区" + +#, c-format +msgid "Press %s button to select." +msgstr "按%s键选择" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "按%s键跳过代ç " + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "按 %s é”®åŒæ­¥FAT分区。" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "按 %s 切æ¢åˆ°å…¨å±é¢„览模å¼" + +#, c-format +msgid "Press %s for game options" +msgstr "按%s键进入游æˆé€‰é¡¹èœå•" + +#, c-format +msgid "Press %s for global options" +msgstr "按%s键进入全局选项èœå•" + +#, c-format +msgid "Press %s to discard options" +msgstr "按%s键放弃ä¿å­˜é€‰é¡¹" + +#, c-format +msgid "Press %s to download and update" +msgstr "按%s键下载更新" + +#, c-format +msgid "Press %s to download this theme" +msgstr "按%s键下载这款主题" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "按%s键下载,%s键返回" + +#, c-format +msgid "Press %s to return" +msgstr "按%s键返回" + +#, c-format +msgid "Press %s to save global settings" +msgstr "按%sé”®ä¿å­˜å…¨å±€è®¾ç½®" + +#, c-format +msgid "Press %s to save options" +msgstr "按%sé”®ä¿å­˜é€‰é¡¹" + +#, c-format +msgid "Press %s to save selection" +msgstr "按%sé”®ä¿å­˜é€‰æ‹©" + +#, c-format +msgid "Press %s to select filter type" +msgstr "按%s键选择筛选方å¼" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "按%s键选择排åºæ–¹å¼" + +#, c-format +msgid "Press %s to start game" +msgstr "按%s键开始游æˆ" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "按%s键下载更新(ä¸æ›´æ–°meta.xml)" + +#, fuzzy, c-format +msgid "Press %s/%s to select device." +msgstr "按%s/%s键选择设备" + +msgid "Press 1 to skip WBFS mounting" +msgstr "按1é”®å–消WBFS分区挂载" + +msgid "Press 2 to reload IOS" +msgstr "按2é”®é‡æ–°è¯»å–IOS" + +msgid "Press A to continue without config.txt" +msgstr "按 A 继续无config.txt文件" + +msgid "Press A to select device" +msgstr "按A键选择设备" + +msgid "Press B to exit to HBC" +msgstr "按B键退出到HBC" + +msgid "Press HOME to reset" +msgstr "按Homeé”®å¤ä½" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "按左å³é”®é€‰æ‹©IOS" + +msgid "Press any button to continue..." +msgstr "按任æ„键继续..." + +msgid "Press any button to exit..." +msgstr "按任æ„键退出..." + +msgid "Press any button to restart..." +msgstr "按任æ„é”®é‡å¯..." + +msgid "Press any button.\n" +msgstr "按任æ„键。\n" + +msgid "Press any button..." +msgstr "按任æ„é”®..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "按 %s 键下载å°é¢ã€‚" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "按%s键弹出DVD光盘" + +msgid "Priiloader" +msgstr "Priiloader" + +msgid "Profile:" +msgstr "é…置:" + +#, c-format +msgid "Profile: %s" +msgstr "é…置文件:%s" + +msgid "Program Updates" +msgstr "程åºæ›´æ–°" + +msgid "Publisher" +msgstr "å‘行厂商" + +msgid "Puzzle" +msgstr "益智游æˆ" + +msgid "Quit" +msgstr "退出" + +msgid "RAW" +msgstr "原始数æ®" + +msgid "RIGHT" +msgstr "å‘å³" + +msgid "RPG" +msgstr "角色扮演游æˆ" + +msgid "Racing" +msgstr "竞速游æˆ" + +msgid "Rail Shooter" +msgstr "é“路射手" + +#, c-format +msgid "Rated %s" +msgstr "评级为%s" + +msgid "Rating" +msgstr "评分" + +msgid "Read" +msgstr "读å–" + +msgid "Reading BCA..." +msgstr "正在读å–BCA..." + +msgid "Reboot" +msgstr "é‡æ–°å¯åŠ¨" + +msgid "Region" +msgstr "区域" + +msgid "Release Date" +msgstr "å‘售日期" + +msgid "Release Notes: (short)" +msgstr "å‘布说明:(简介)" + +msgid "Removing game, please wait..." +msgstr "正在删除游æˆï¼Œè¯·ç¨å€™..." + +msgid "Restarting Wii..." +msgstr "正在é‡æ–°å¯åŠ¨Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "已返回ï¼(ret = %d)" + +msgid "Rhythm" +msgstr "节å¥æ¸¸æˆ" + +msgid "Rows:" +msgstr "行数:" + +msgid "Rugby" +msgstr "橄榄çƒ" + +msgid "Running benchmark, please wait" +msgstr "正在è¿è¡ŒåŸºå‡†æµ‹è¯•ï¼Œè¯·ç¨å€™" + +msgid "S. Chinese" +msgstr "简体中文" + +msgid "SD/SDHC Card" +msgstr "SD/SDHCå¡" + +msgid "SUCCESS!" +msgstr "æˆåŠŸï¼" + +msgid "Save .gct" +msgstr "ä¿å­˜.gct" + +msgid "Save Debug" +msgstr "ä¿å­˜è°ƒè¯•ä¿¡æ¯" + +msgid "Save Settings" +msgstr "ä¿å­˜è®¾ç½®" + +msgid "Save debug.log" +msgstr "ä¿å­˜debug.log文件" + +msgid "Savegame not found.\n" +msgstr "没有找到游æˆå­˜æ¡£ã€‚\n" + +msgid "Savegame:" +msgstr "游æˆå­˜æ¡£ï¼š" + +msgid "Saving Settings... " +msgstr "正在ä¿å­˜è®¾ç½®..." + +msgid "Saving cheats..." +msgstr "正在ä¿å­˜é‡‘手指..." + +msgid "Saving gamelist.txt ... " +msgstr "正在ä¿å­˜gamelist.txt..." + +msgid "Saving settings..." +msgstr "正在ä¿å­˜è®¾ç½®..." + +msgid "Saving:" +msgstr "正在ä¿å­˜ï¼š" + +#, c-format +msgid "Saving: %s" +msgstr "正在ä¿å­˜ï¼š%s" + +msgid "Screenshot:" +msgstr "截å±" + +msgid "Scroll:" +msgstr "滚动:" + +msgid "Search" +msgstr "寻找" + +msgid "Search for:" +msgstr "寻找:" + +msgid "Select Alternative .dol:" +msgstr "请选择替代的.dol文件:" + +msgid "Select WBFS device:" +msgstr "请选择WBFS设备:" + +msgid "Select a different partition" +msgstr "请选择其它分区" + +msgid "Select a partition" +msgstr "请选择一个分区" + +msgid "Select all" +msgstr "全部选择" + +msgid "Select device:" +msgstr "选择设备:" + +msgid "Select the game you want to boot" +msgstr "请选择你想è¦è¿è¡Œçš„游æˆ" + +msgid "Selected Game" +msgstr "已选游æˆ" + +msgid "Settings" +msgstr "设置" + +msgid "Shooter" +msgstr "射击游æˆ" + +msgid "Show All" +msgstr "显示所有" + +msgid "Show cIOS info" +msgstr "显示cIOSä¿¡æ¯" + +msgid "Shutdown" +msgstr "关闭" + +msgid "Side:" +msgstr "å°é¢ï¼š" + +msgid "Sim Racing" +msgstr "模拟赛车" + +msgid "Simulation" +msgstr "模拟游æˆ" + +msgid "Size(GB) Type Mount Used" +msgstr "已使用类型分区大å°ï¼ˆGB)" + +#, c-format +msgid "Size: %d bytes" +msgstr "大å°ï¼š%d字节" + +msgid "Skateboard" +msgstr "滑æ¿" + +msgid "Skateboarding" +msgstr "滑æ¿è¿åŠ¨" + +msgid "Skiing" +msgstr "滑雪" + +msgid "Snowboarding" +msgstr "滑æ¿æ»‘雪" + +msgid "Soccer" +msgstr "足çƒ" + +msgid "Sort" +msgstr "排åº" + +msgid "Sort Games" +msgstr "游æˆæŽ’åº" + +msgid "Sort Order:" +msgstr "排åºæ¬¡åºï¼š" + +msgid "Sort Type:" +msgstr "排åºç±»åˆ«ï¼š" + +#, c-format +msgid "Sort: %s-%s" +msgstr "排åºï¼š%s-%s" + +msgid "Spanish" +msgstr "西ç­ç‰™è¯­" + +msgid "Sports" +msgstr "è¿åŠ¨æ¸¸æˆ" + +msgid "Start" +msgstr "开始" + +msgid "Start Game" +msgstr "开始游æˆ" + +msgid "Start this game?" +msgstr "开始这款游æˆï¼Ÿ" + +msgid "Stealth Action" +msgstr "ç»å¯†é£žè¡Œæ´»åŠ¨" + +msgid "Stopping DVD..." +msgstr "正在关闭DVD光盘..." + +msgid "Strategy" +msgstr "战略游æˆ" + +msgid "Style" +msgstr "ç•Œé¢è®¾ç½®" + +msgid "Style:" +msgstr "ç•Œé¢è®¾ç½®ï¼š" + +msgid "Surfing" +msgstr "冲浪è¿åŠ¨" + +msgid "Survival Horror" +msgstr "æ怖生存类" + +msgid "Sync FAT free space info?" +msgstr "是å¦åŒæ­¥FAT分区剩余空间信æ¯ï¼Ÿ" + +msgid "Synchronizing, please wait." +msgstr "正在åŒæ­¥ä¿¡æ¯ä¸­ï¼Œè¯·ç­‰å¾…。" + +msgid "Synopsis" +msgstr "摘è¦" + +msgid "Synopsis Length" +msgstr "摘è¦é•¿åº¦" + +msgid "System" +msgstr "系统" + +msgid "System Def." +msgstr "系统定义" + +msgid "T. Chinese" +msgstr "ç¹ä½“中文" + +msgid "Table Tennis" +msgstr "乒乓çƒ" + +msgid "Tennis" +msgstr "网çƒ" + +msgid "Theme" +msgstr "主题" + +msgid "Theme Info" +msgstr "主题信æ¯" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "主题有å¯èƒ½è¿‡å¤§å»ºè®®é‡å¯CFG" + +msgid "Theme:" +msgstr "主题:" + +#, c-format +msgid "Theme: %s" +msgstr "主题:%s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "主题æä¾›By wii.spiffy360.com" + +msgid "Themes to download" +msgstr "下载主题" + +msgid "Themes with updates" +msgstr "主题包å«æ›´æ–°" + +msgid "Title" +msgstr "标题" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "金手指过多ï¼(%d)" + +msgid "Too many code lines!" +msgstr "代ç è¡Œæ•°è¿‡å¤šï¼" + +#, c-format +msgid "Too many fragments! %d" +msgstr "碎片过多ï¼%d" + +msgid "Total Body Tracking" +msgstr "全身跟踪" + +msgid "Train Simulation" +msgstr "ç«è½¦æ¨¡æ‹Ÿ" + +msgid "Trivia" +msgstr "答题类游æˆ" + +msgid "Truck Racing" +msgstr "å¡è½¦èµ›è½¦" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "å°è¯•(url#%d)..." + +msgid "Turntable" +msgstr "转盘" + +msgid "UNUSED" +msgstr "未使用" + +msgid "UP" +msgstr "å‘上" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL'%s'ä¸æ˜¯ä»¥'http://'开头的" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL'%s'中没有路径部分" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "å‘现USB Gecko,Debugging模å¼å¼€å¯" + +msgid "USB Mass Storage Device" +msgstr "USB大容é‡å­˜å‚¨è®¾å¤‡" + +msgid "Unknown" +msgstr "未知" + +msgid "Unknown syntax!" +msgstr "未知语法ï¼" + +msgid "Unplayed" +msgstr "未玩过游æˆ" + +msgid "Unplayed Games" +msgstr "未玩过的游æˆ" + +msgid "Update themes" +msgstr "更新主题" + +msgid "Updates" +msgstr "æ›´æ–°" + +msgid "Updating database." +msgstr "正在更新数æ®åº“" + +msgid "Updating devolution" +msgstr "正在更新devolution" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "已使用:%.1fGB 剩余:%.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "使用已ä¿å­˜çš„替代.dol:" + +msgid "VC-Arcade" +msgstr "VC街机" + +msgid "VC-Commodore 64" +msgstr "VC-Commodore 64" + +msgid "VC-N64" +msgstr "VC-任天堂64" + +msgid "VC-NES" +msgstr "VC-红白机" + +msgid "VC-Neo Geo" +msgstr "VC-Neo Geo" + +msgid "VC-SMS" +msgstr "VC-SMS" + +msgid "VC-SNES" +msgstr "VC超级任天堂" + +msgid "VC-Sega Genesis" +msgstr "VC-Sega Genesis" + +msgid "VC-TurboGrafx-16" +msgstr "VC-TurboGrafx-16" + +msgid "Version" +msgstr "版本å·" + +msgid "Video Patch:" +msgstr "视频补ä¸ï¼š" + +msgid "Video:" +msgstr "视频制å¼ï¼š" + +msgid "View" +msgstr "查看" + +msgid "Virtual Pet" +msgstr "宠物养æˆç±»" + +msgid "Vitality Sensor" +msgstr "对应活力探测器" + +msgid "Volleyball" +msgstr "排çƒ" + +msgid "WARNING:" +msgstr "警告:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS:%.1fGB剩余(å…±%.1fGB)" + +msgid "Watercraft Racing" +msgstr "水上类竞赛" + +msgid "Wheel" +msgstr "对应方å‘盘" + +msgid "Wide Screen:" +msgstr "宽å±" + +msgid "Wii" +msgstr "Wii" + +msgid "Wii Channel" +msgstr "Wii频é“" + +msgid "Wii Speak" +msgstr "对应Wii麦克风" + +msgid "WiiWare" +msgstr "WiiWare" + +msgid "Wiimote" +msgstr "对应Wiiå³æ‰‹æŸ„" + +msgid "Wrestling" +msgstr "摔跤" + +msgid "Write Playlog:" +msgstr "记录游æˆè¿è¡Œæ—¥å¿—:" + +#, c-format +msgid "Writing to %s" +msgstr "正在写入到%s" + +#, c-format +msgid "Writing: %s" +msgstr "正在写入:%s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "错误大å°ï¼š%d (%d)" + +msgid "Yes" +msgstr "是" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"ä½ å¯ä»¥æ‹”一下设备然åŽå†æ’回去,\n" +"或者ç¨å¾®ç­‰ä¸€ä¼šã€‚" + +msgid "Zapper" +msgstr "对应Wii枪托" + +msgid "[ CHANGED ]" +msgstr "[ 已更改 ]" + +msgid "[ FOUND ]" +msgstr "[ 已找到 ]" + +msgid "[ SAVED ]" +msgstr "[ å·²ä¿å­˜ ]" + +msgid "[HOME]" +msgstr "[HOMEé”®]" + +msgid "[No HQ]" +msgstr "æ— HQå°é¢" + +msgid "[USED]" +msgstr "[已使用]" + +msgid "[default]" +msgstr "缺çœ" + +msgid "[saved]" +msgstr "å·²ä¿å­˜" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "æŸåçš„WBFS文件:%s" + +msgid "calculating space, please wait..." +msgstr "正在计算所需容é‡ï¼Œè¯·ç¨å€™..." + +msgid "delete" +msgstr "删除" + +#, c-format +msgid "dest: %p - %p" +msgstr "é™åº: %p - %p" + +msgid "discard" +msgstr "抛弃" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "无法解æžåŸŸå%s" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "读å–错误:%s (%d)" + +#, c-format +msgid "for %d players" +msgstr "支æŒ%d人游æˆ" + +msgid "for 1 player" +msgstr "å•äººæ¸¸æˆ" + +msgid "format" +msgstr "æ ¼å¼åŒ–" + +msgid "free" +msgstr "剩余" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "线性读å–速度:%.2f mb/s" + +msgid "linear..." +msgstr "线性..." + +#, c-format +msgid "loader.bin size: %d" +msgstr "loader.bin文件大å°ï¼š%d" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "音ä¹æ–‡ä»¶è¿‡å¤§(%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "没有找到music.mp3或music.modï¼" + +msgid "no file" +msgstr "无文件" + +msgid "none" +msgstr "æ— " + +msgid "or format a WBFS partition." +msgstr "或格å¼åŒ–WBFS分区" + +msgid "page" +msgstr "页" + +msgid "parse error" +msgstr "解æžé”™è¯¯" + +msgid "r51-" +msgstr "r51以下版本" + +msgid "r52+" +msgstr "r52以上版本" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "éšæœºè¯»å–速度:%.2f mb/s" + +msgid "random..." +msgstr "éšæœº..." + +msgid "reset" +msgstr "å¤ä½" + +msgid "revert" +msgstr "æ¢å¤" + +msgid "save" +msgstr "ä¿å­˜" + +#, c-format +msgid "split error: %s" +msgstr "分割错误:%s" + +msgid "uDraw GameTablet" +msgstr "uDraw 游æˆå¹³æ¿" + +msgid "unable to open wii disc" +msgstr "无法打开Wii光盘" + +#, c-format +msgid "used: %p - %p" +msgstr "已使用: %p - %p" + +#~ msgid " Copying files from USB to SD card...(%c)" +#~ msgstr "正在把文件从USB设备中å¤åˆ¶åˆ°SDå¡ä¸­...(%c)" + +#~ msgid "Booting Wii game, please wait..." +#~ msgstr "正在å¯åŠ¨Wii游æˆï¼Œè¯·ç¨å€™......" + +#~ msgid "Console Def." +#~ msgstr "主机定义。" + +#~ msgid "DISK1:%s[%s]\n" +#~ msgstr "ç£ç›˜1:%s[%s]\n" + +#~ msgid "DISK2:%s[%s]2\n" +#~ msgstr "ç£ç›˜2:%s[%s]2\n" + +#~ msgid "Download titles.txt" +#~ msgstr "下载Titltes.txt文件" + +#~ msgid "Front" +#~ msgstr "å‰" + +#~ msgid "Loading previous game list..." +#~ msgstr "正在读å–上次游æˆåˆ—表..." + +#~ msgid "Not Found boot.dol!!" +#~ msgstr "未找到Boot.dolï¼" + +#~ msgid "Remove Game" +#~ msgstr "删除游æˆ" + +#~ msgid "Synopsis Len" +#~ msgstr "摘è¦é•¿åº¦" + +#~ msgid "Update WiiTDB Game Database" +#~ msgstr "æ›´æ–°WiiTDB游æˆæ•°æ®åº“" + +#~ msgid "Update titles.txt" +#~ msgstr "æ›´æ–°titles.txt" + +#~ msgid "WiiTDB Game Database" +#~ msgstr "WiiTDB游æˆæ•°æ®åº“" + +#~ msgid "Wiird" +#~ msgstr "Wii金手指" diff --git a/Languages/ZH_TW.lang b/Languages/ZH_TW.lang new file mode 100644 index 0000000..f5a14f0 --- /dev/null +++ b/Languages/ZH_TW.lang @@ -0,0 +1,1865 @@ +# CFG USB Loader language file template. +# Put the translated string in msgstr "" +# Fill in the Last-Translator and Language-Team fields. +# Please use utf-8 charset when editing. +msgid "" +msgstr "" +"Project-Id-Version: CFG USB Loader\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-05-19 16:17+0800\n" +"PO-Revision-Date: 2010-03-17 05:55+0800\n" +"Last-Translator: 19872001\n" +"Language-Team: Traditional Chinese (ZH_TW) <19872001@tvgzone.com>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "%.1fGB 剩餘 %.1fGB" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "%.2fGB 已複製在 %d:%02d:%02d" + +#, c-format +msgid "%d available" +msgstr "%d å¯ä½¿ç”¨" + +#, c-format +msgid "%d more notes" +msgstr "%d 更多註釋" + +#, c-format +msgid "%s Split: %d %s" +msgstr "%s 分割: %d %s" + +#, c-format +msgid "%s, please wait..." +msgstr "%s, 請等候..." + +#, c-format +msgid "(%d online)" +msgstr "(æ”¯æ´ %d å玩家連線)" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "(%d 秒逾時)" + +msgid "(This can take a couple of minutes)" +msgstr "(這å¯èƒ½éœ€è¦æ•¸åˆ†é˜)" + +msgid ".dol too small" +msgstr ".dol 太å°" + +msgid "3D cover" +msgstr "3D å°é¢" + +msgid "< ASC >" +msgstr "< éžå¢ž >" + +msgid "< DESC >" +msgstr "< éžæ¸› >" + +msgid "< DOWNLOAD >" +msgstr "< 下載 >" + +msgid "About" +msgstr "關於" + +msgid "Action" +msgstr "動作" + +msgid "Additional config:" +msgstr "é¡å¤–的設定:" + +msgid "Admin Lock:" +msgstr "系統管ç†ä¸ŠéŽ–:" + +msgid "Admin Unlock" +msgstr "系統管ç†è§£éŽ–" + +msgid "Adult" +msgstr "æˆäºº" + +msgid "Adventure" +msgstr "冒險" + +msgid "All" +msgstr "全部" + +msgid "All Games" +msgstr "所有éŠæˆ²" + +msgid "All themes up to date." +msgstr "更新全部主題." + +msgid "Alt dol:" +msgstr "替代 dol:" + +msgid "Alternative .dol:" +msgstr "替代的 .dol:" + +msgid "Anti 002 Fix:" +msgstr "抑制 002 修復:" + +#, c-format +msgid "App. Path: %s" +msgstr "程å¼è·¯å¾‘: %s" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" +"你是å¦ç¢ºå®šè¦ä½¿ç”¨ %s\n" +"這個分割å€?" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" +"你是å¦ç¢ºå®šè¦ä¿®å¾©\n" +"這個分割å€?" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "你是å¦ç¢ºå®šè¦ç§»é™¤é€™å€‹éŠæˆ²?" + +msgid "Ascending" +msgstr "ä¾éžå¢žæŽ’åº" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "自動開始éŠæˆ²: %.6s 找ä¸åˆ°!" + +msgid "Available Updates" +msgstr "å¯ç”¨çš„æ›´æ–°" + +msgid "Back" +msgstr "返回" + +msgid "Balance Board" +msgstr "平衡器" + +msgid "Basic" +msgstr "基本的" + +msgid "Block IOS Reload:" +msgstr "å°éŽ– IOS é‡æ–°è¼‰å…¥:" + +msgid "Boot Disc" +msgstr "啟動光碟" + +msgid "Boot disc" +msgstr "由光碟啟動" + +msgid "Booting Wii game, please wait..." +msgstr "正在啟動 Wii éŠæˆ², 請等候..." + +#, c-format +msgid "CFG base: %s" +msgstr "CFG 基底: %s" + +msgid "Cancelled." +msgstr "å·²å–消." + +#, c-format +msgid "Cannot create dir: %s" +msgstr "無法建立目錄: %s" + +msgid "Cheat Codes:" +msgstr "金手指密碼:" + +msgid "Cheats: " +msgstr "金手指: " + +msgid "Check For Updates" +msgstr "檢查更新" + +msgid "Checking for themes..." +msgstr "正在檢查主題..." + +msgid "Checking for updates..." +msgstr "正在檢查更新..." + +msgid "Choose a sorting method" +msgstr "é¸æ“‡ä¸€å€‹æŽ’åºæ–¹å¼" + +msgid "Classic" +msgstr "å¤å…¸çš„" + +msgid "Classic Controller" +msgstr "傳統控制器" + +msgid "Clear Patches:" +msgstr "清除補綴:" + +#, c-format +msgid "Complete. Size: %d" +msgstr "完æˆ. 大å°: %d" + +#, c-format +msgid "Configurable Loader %s" +msgstr "Configurable Loader %s" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "設定錯誤, å·²é”到 MAX_DNS_ENTRIES 並且清單是空的" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "從 net_read() 連線錯誤 錯誤代碼: %i" + +msgid "Console" +msgstr "主控å°" + +msgid "Console Def." +msgstr "é è¨­ä¸»æŽ§å°." + +msgid "Controller" +msgstr "控制器" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "無法åˆå§‹åŒ– DIP 模組! (ret = %d)" + +msgid "Country Fix:" +msgstr "國別代碼修復:" + +msgid "Cover" +msgstr "å°é¢" + +msgid "Cover Image:" +msgstr "å°é¢åœ–片:" + +msgid "Cover Style" +msgstr "å°é¢é¢¨æ ¼" + +msgid "Cover~~Back" +msgstr "å°é¢~~背é¢" + +msgid "Cover~~Front" +msgstr "å°é¢~~æ­£é¢" + +msgid "Create" +msgstr "建立" + +msgid "Creator" +msgstr "建立者" + +#, c-format +msgid "Current Version: %s" +msgstr "ç›®å‰çš„版本: %s" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" +"cIOS %d 無法被找到!\n" +"請安è£å®ƒ." + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "cIOS %d 無法載入! (ret = %d)" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" +"cIOS %d 是一個殘段!\n" +"è«‹é‡æ–°å®‰è£å®ƒ." + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "cIOS %s 載入完æˆ" + +msgid "DISC cover" +msgstr "光碟å°é¢" + +msgid "DOWN" +msgstr "下" + +msgid "Dance Pad" +msgstr "跳舞è¸å¢Š" + +msgid "Database update successful." +msgstr "資料庫更新æˆåŠŸ." + +msgid "Debug" +msgstr "åµéŒ¯" + +msgid "Delete Game" +msgstr "刪除éŠæˆ²" + +msgid "Deleting" +msgstr "正在刪除" + +msgid "Descending" +msgstr "ä¾éžæ¸›æŽ’åº" + +msgid "Developer" +msgstr "開發æˆå“¡" + +msgid "Device is not responding!" +msgstr "è£ç½®æ²’有回應!" + +msgid "Device:" +msgstr "è£ç½®:" + +msgid "Disc" +msgstr "光碟" + +msgid "Disc (Ask)" +msgstr "光碟 (è©¢å•)" + +msgid "Done." +msgstr "完æˆ" + +msgid "Download .txt" +msgstr "下載 .txt" + +msgid "Download All Covers" +msgstr "下載所有的å°é¢" + +msgid "Download All Missing Covers" +msgstr "下載所有缺少的å°é¢" + +msgid "Download Missing Covers" +msgstr "下載缺少的å°é¢" + +msgid "Download Themes" +msgstr "下載主題包" + +msgid "Download complete." +msgstr "下載完æˆ." + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "下載 Gamercard #%d 錯誤." + +msgid "Download titles.txt" +msgstr "下載 titles.txt" + +msgid "Downloadable Content" +msgstr "å¯ä¸‹è¼‰çš„內容" + +msgid "Downloading ALL MISSING covers" +msgstr "正在下載所有缺少的å°é¢" + +msgid "Downloading ALL covers" +msgstr "正在下載所有å°é¢" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "正在下載 %.6s 所有å°é¢" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "正在下載 %.6s 缺少的å°é¢" + +msgid "Downloading Theme previews..." +msgstr "正在下載主題é è¦½..." + +msgid "Downloading cheats..." +msgstr "正在下載金手指..." + +msgid "Downloading database." +msgstr "正在下載資料庫." + +msgid "Downloading titles.txt ..." +msgstr "正在下載 titles.txt ..." + +#, c-format +msgid "Downloading: %s" +msgstr "正在下載: %s" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "正在下載: %s 為 %s" + +msgid "Downloads" +msgstr "下載" + +msgid "Drums" +msgstr "康加鼓" + +msgid "Dutch" +msgstr "è·è˜­æ–‡" + +msgid "ERROR" +msgstr "錯誤" + +msgid "ERROR creating file" +msgstr "建立檔案時錯誤" + +msgid "ERROR game opt" +msgstr "éŠæˆ²é¸é …錯誤" + +msgid "ERROR reading BCA!" +msgstr "è®€å– BCA 時錯誤!" + +#, c-format +msgid "ERROR removing %s" +msgstr "移除 %s 時錯誤" + +msgid "ERROR writing BCA!" +msgstr "寫入 BCA 時錯誤!" + +msgid "ERROR!" +msgstr "錯誤!" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "錯誤! (ret = %d)" + +msgid "ERROR:" +msgstr "錯誤:" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "錯誤: %s 無法存å–" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "錯誤: Apploader %d" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "錯誤: BCA éœ€è¦ cIOS222/223 v4" + +msgid "ERROR: Cache Close" +msgstr "錯誤: å¿«å–關閉" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "錯誤: 無法開啟éŠæˆ²! (ret = %d)" + +msgid "ERROR: Game already installed!!" +msgstr "錯誤: éŠæˆ²å·²ç¶“被安è£!!" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "錯誤: å–得憑證 %d" + +msgid "ERROR: Invalid Game ID" +msgstr "錯誤: 無效的éŠæˆ² ID" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "錯誤: 載入 EHC 模組! (%d)" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" +"錯誤: NTFS 分割å€å¯«å…¥å·²åœç”¨!\n" +"(請設定 ntfs_write=1)" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "錯誤: 沒有找到分割å€! (ret = %d)" + +msgid "ERROR: Not a Wii disc!!" +msgstr "錯誤: ä¸æ˜¯ Wii 光碟!!" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "錯誤: å移(0x%llx) %d" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "錯誤: é–‹å•Ÿåˆ†å‰²å€ %d" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "錯誤: 開啟分割å€(0x%llx) %d" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "錯誤: SetFragList(0): %d" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "錯誤: SetWBFS: %d" + +msgid "ERROR: Setting SD mode" +msgstr "錯誤: 設定 SD 模å¼" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "錯誤: USB åˆå§‹åŒ–! (%d)" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" +"錯誤: 從 FAT 分割å€å•Ÿå‹•éŠæˆ²æ™‚éœ€è¦ cIOS249rev18,\n" +"cIOS222v4, cIOS223v4, cIOS224v5 或更高的版本!\n" +"è«‹æ›´æ–° IOS249 或é¸æ“‡ä¸€å€‹ä¸åŒçš„ IOS." + +msgid "ERROR: cache: out of memory" +msgstr "錯誤: å¿«å–: 記憶體ä¸è¶³" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "錯誤: 正在建立: %s" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "錯誤: get_frag_list: %d" + +msgid "ERROR: memory overlap!" +msgstr "錯誤: 記憶體é‡ç–Š!" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" +"錯誤: å¤šé‡ WBFS 分割å€\n" +"僅 IOS222/223-mload 支æ´" + +msgid "ERROR: not enough free space!!" +msgstr "錯誤: 沒有足夠的å¯ç”¨ç©ºé–“!!" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "錯誤: NTFS 分割å€æ²’有乾淨的解除掛載" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "錯誤: é–‹å•Ÿ %.6s 時" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "錯誤: set_frag_list: %d" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "錯誤: 設定碎片 %d %d 時" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "錯誤: 寫入 %s (%d) 時." + +msgid "EXTEND" +msgstr "æ“´å……" + +msgid "Ejecting DVD..." +msgstr "正在退出 DVD..." + +msgid "English" +msgstr "英文" + +msgid "English Title" +msgstr "英文標題" + +msgid "Enter Code: " +msgstr "輸入代碼:" + +msgid "Error GRRLIB init" +msgstr "åˆå§‹åŒ– GRRLIB 錯誤" + +msgid "Error Initializing Network." +msgstr "åˆå§‹åŒ–網路時錯誤." + +msgid "Error adding disc!" +msgstr "加入光碟時錯誤!" + +msgid "Error creating directory..." +msgstr "建立目錄時錯誤..." + +msgid "Error discarding options!" +msgstr "放棄é¸é …時錯誤!" + +msgid "Error downloading theme preview..." +msgstr "下載主題é è¦½æ™‚錯誤..." + +msgid "Error downloading theme..." +msgstr "下載主題時錯誤..." + +msgid "Error downloading themes..." +msgstr "下載主題時錯誤..." + +msgid "Error downloading update..." +msgstr "下載更新時錯誤..." + +msgid "Error downloading updates..." +msgstr "下載更新時錯誤..." + +msgid "Error downloading." +msgstr "下載時錯誤." + +msgid "Error establishing connection" +msgstr "建立連線時錯誤" + +msgid "Error extracting theme..." +msgstr "解壓縮主題時錯誤..." + +msgid "Error opening database, update did not complete." +msgstr "開啟資料庫錯誤, 更新沒有完æˆ." + +#, c-format +msgid "Error opening: %s" +msgstr "開啟時錯誤: %s" + +#, c-format +msgid "Error playing %s" +msgstr "éŠæˆ²æ™‚錯誤 %s" + +msgid "Error reading .dol" +msgstr "è®€å– .dol 時錯誤" + +msgid "Error reading dol header" +msgstr "è®€å– dol 檔頭時錯誤" + +#, c-format +msgid "Error saving %s" +msgstr "儲存時錯誤 %s" + +msgid "Error saving options!" +msgstr "儲存é¸é …時錯誤!" + +msgid "Error saving settings!" +msgstr "儲存設定時錯誤!" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" +"儲存éŠæˆ²è¨˜éŒ„檔案時錯誤.\n" +"從 Wii 系統é¸å–®å•Ÿå‹•çš„話能修復." + +msgid "Error: Invalid PNG image!" +msgstr "錯誤: 無效的 PNG 圖片!" + +msgid "Error: no URL." +msgstr "錯誤: 沒有 URL." + +msgid "Error: no data." +msgstr "錯誤: 沒有資料." + +msgid "Exit" +msgstr "離開" + +#, c-format +msgid "Extracting: %s" +msgstr "正在解壓縮: %s" + +msgid "FAIL" +msgstr "失敗" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "FAT: é…置錯誤 %p %p %p" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "åš´é‡éŒ¯èª¤: é…置資料格(%d)" + +#, c-format +msgid "FILL %d" +msgstr "填滿 %d" + +msgid "FLAT cover" +msgstr "å¹³é¢å°é¢" + +msgid "FULL cover" +msgstr "完整å°é¢" + +msgid "Fav" +msgstr "顯示最喜愛的" + +msgid "Fav: Off" +msgstr "ä¸é¡¯ç¤ºæœ€æ„›çš„" + +msgid "Fav: On" +msgstr "顯示最喜愛的" + +msgid "Favorite" +msgstr "最喜愛的" + +msgid "Favorite Games" +msgstr "最愛的éŠæˆ²" + +msgid "Favorite:" +msgstr "最愛的:" + +msgid "Favorites:" +msgstr "最喜愛的" + +msgid "Fighting" +msgstr "格鬥" + +#, c-format +msgid "File not found! %s" +msgstr "找ä¸åˆ°æª”案! %s" + +msgid "Filter" +msgstr "éŽæ¿¾" + +msgid "Filter Games" +msgstr "éŠæˆ²éŽæ¿¾" + +msgid "Filter by Controller" +msgstr "ä¾æŽ§åˆ¶å™¨éŽæ¿¾" + +msgid "Filter by Genre" +msgstr "ä¾é¡žåž‹éŽæ¿¾" + +msgid "Filter by Online Features" +msgstr "ä¾ç¶²è·¯åŠŸèƒ½éŽæ¿¾" + +msgid "Filter:" +msgstr "éŽæ¿¾:" + +msgid "Fixing EXTEND partition..." +msgstr "正在修復擴充分割å€..." + +msgid "Force NTSC" +msgstr "強制 NTSC" + +msgid "Force PAL50" +msgstr "強制 PAL50" + +msgid "Force PAL60" +msgstr "強制 PAL60" + +msgid "Formatting" +msgstr "正在格å¼åŒ–" + +#, c-format +msgid "Found %s" +msgstr "找到 %s" + +msgid "French" +msgstr "法文" + +msgid "Full" +msgstr "完整的" + +msgid "GB" +msgstr "GB" + +msgid "Game Default" +msgstr "éŠæˆ²é è¨­" + +msgid "Game Options" +msgstr "éŠæˆ²é¸é …" + +#, c-format +msgid "Game Options: %s" +msgstr "éŠæˆ²é¸é …: %s" + +msgid "Gamecube" +msgstr "Gamecube" + +msgid "Gamer Card:" +msgstr "玩家å¡:" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "Gamercard #%d 報告: %.*s" + +msgid "Genre" +msgstr "é¡žåž‹" + +msgid "German" +msgstr "å¾·æ–‡" + +msgid "Global Options" +msgstr "全域é¸é …" + +msgid "Guitar" +msgstr "å‰ä»–" + +msgid "HQ cover" +msgstr "HQ å°é¢" + +msgid "HTTP Response was without a file" +msgstr "HTTP 回應沒有檔案" + +msgid "Hide" +msgstr "éš±è—" + +msgid "Hide Game:" +msgstr "éš±è—éŠæˆ²:" + +msgid "Hold button B to cancel." +msgstr "按ä½æŒ‰éˆ• B å–消." + +msgid "Homebrew Channel" +msgstr "退出至HBCé »é“" + +msgid "Hook Type:" +msgstr "攔截類型:" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "ID ä¸ç¬¦åˆ: [%.6s] [%.6s]" + +msgid "IOS Reload: Blocked" +msgstr "IOS é‡æ–°è¼‰å…¥: å·²å°éŽ–" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "IOS_Open(%s) 失敗, 代碼 %d" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" +"如果下次無法é©ç•¶åœ°å•Ÿå‹• Cfg 時,\n" +"複製 boot.dol.bak å–代為 boot.dol\n" +"然後å†è©¦ä¸€æ¬¡." + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "LZ77 ç·¨ç¢¼æ–¹å¼ %x ä¸ä¸€è‡´" + +msgid "Info" +msgstr "資訊" + +#, c-format +msgid "Info file: %s" +msgstr "檔案資訊: %s" + +msgid "Initializing Network..." +msgstr "åˆå§‹åŒ–網路..." + +msgid "Install" +msgstr "安è£" + +msgid "Install Date" +msgstr "安è£æ—¥æœŸ" + +msgid "Install Game" +msgstr "安è£éŠæˆ²" + +msgid "Install game" +msgstr "安è£éŠæˆ²" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "安è£éŒ¯èª¤! (ret = %d)" + +msgid "Installing game, please wait..." +msgstr "正在安è£éŠæˆ², 請等候..." + +msgid "Invalid .dol" +msgstr "無效的 .dol" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "無效的分割å€: '%s'" + +msgid "Italian" +msgstr "義大利文" + +msgid "Japanese" +msgstr "日文" + +msgid "Japanese Title" +msgstr "日文標題" + +msgid "Keyboard" +msgstr "éµç›¤" + +msgid "Korean" +msgstr "韓文" + +msgid "LEFT" +msgstr "å·¦" + +msgid "LOCKED!" +msgstr "已鎖定!" + +msgid "Language:" +msgstr "語言:" + +msgid "Last Play Date" +msgstr "最後éŠæˆ²æ—¥æœŸ" + +msgid "Launch Methods" +msgstr "執行方å¼" + +msgid "Load OK!" +msgstr "載入完æˆ!" + +#, c-format +msgid "Loader Version: %s" +msgstr "載入器版本: %s" + +msgid "Loading ..." +msgstr "正在載入 ..." + +msgid "Main" +msgstr "主é¸å–®" + +msgid "Main Menu" +msgstr "主é¸å–®" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" +"請確定使用 USB çš„ 0 號連接埠!\n" +"(最é è¿‘主機邊緣的連接埠)" + +msgid "Manage" +msgstr "管ç†" + +msgid "Manage Cheats" +msgstr "管ç†é‡‘手指" + +msgid "Microphone" +msgstr "麥克風" + +msgid "Motion+" +msgstr "動感強化器" + +msgid "Mounting device, please wait..." +msgstr "正在掛載è£ç½®, 請等候..." + +msgid "Music" +msgstr "音樂" + +#, c-format +msgid "Music file from dir: %s" +msgstr "音樂檔案從目錄: %s" + +#, c-format +msgid "Music file size: %d" +msgstr "音樂檔案大å°: %d" + +#, c-format +msgid "Music file: %s" +msgstr "音樂檔案: %s" + +msgid "Music: Disabled" +msgstr "音樂: 關閉" + +msgid "Music: Enabled" +msgstr "音樂: 啟用" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "音樂: 正在尋找 %s 檔案在: %s" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "音樂: 播放下一個檔案: %i" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "音樂: 找到 %s 個檔案: %i" + +msgid "Music: musicArray contents: " +msgstr "音樂: 音樂陣列內容: " + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" +"注æ„: cIOS249 rev10 之å‰çš„版本:\n" +"ä¸æ”¯æ´å¾ž SDHC 載入éŠæˆ²!" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" +"注æ„: cIOS249 rev14 之å‰çš„版本:\n" +"å¯èƒ½æœƒç™¼ç”Ÿ #001 的錯誤, 導致éŠæˆ²ç„¡æ³•åŸ·è¡Œ!" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" +"注æ„: cIOS249 rev9 之å‰çš„版本:\n" +"ä¸æ”¯æ´å¾ž USB 載入éŠæˆ²!" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" +"注æ„: 你正在使用 cIOS249 rev13:\n" +"當你離開éŠæˆ²ä¹‹å¾Œä½ å¿…須關閉 Wii 主機\n" +"é›»æºæˆ–é‡ç½® Wii 主機, å¦å‰‡å°‡ç„¡æ³•åŸ·è¡Œ\n" +"å¦ä¸€å€‹éŠæˆ²!" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" +"注æ„: 你正在使用的 cIOS249 rev14:\n" +"它和雙層 DVD éŠæˆ²æœ‰ä¸ç›¸å®¹çš„å•é¡Œç™¼ç”Ÿ\n" +"安è£é€™å€‹éŠæˆ²æ™‚請使用其他版本的 IOS." + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" +"注æ„: 載入器需è¦é‡æ–°å•Ÿå‹•\n" +"æ‰èƒ½å¤ å¥—用更新內容." + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" +"注æ„: åˆ†å‰²å€ P#%d 類型是延伸, 但它包å«\n" +"WBFS 檔案系統. 這是一個無效的設定. 請按\n" +"%s 鈕變更延伸分割å€é¡žåž‹." + +msgid "NTFS compression not supported!" +msgstr "å°šæœªæ”¯æ´ NTFS 壓縮!" + +msgid "NTFS encryption not supported!" +msgstr "å°šæœªæ”¯æ´ NTFS 加密!" + +msgid "Network connection established." +msgstr "建立網路連線." + +msgid "Network error. Can't update gamercards." +msgstr "網路錯誤. 無法更新 Gamercard." + +msgid "New Themes" +msgstr "新增主題" + +msgid "Nintendo DS" +msgstr "任天堂 DS" + +msgid "Nintendo DS Connectivity" +msgstr "任天堂 DS 無線通信功能" + +msgid "No" +msgstr "å¦" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "沒有域å部分在 URL '%s'" + +msgid "No games found!" +msgstr "沒有發ç¾éŠæˆ²!" + +msgid "No partition selected!" +msgstr "沒有é¸å–的分割å€!" + +msgid "No themes found." +msgstr "沒有發ç¾ä¸»é¡Œ." + +msgid "No updates found." +msgstr "沒有發ç¾æ›´æ–°." + +msgid "None found on disc" +msgstr "光碟上沒有發ç¾" + +msgid "Not Found!" +msgstr "找ä¸åˆ°!" + +msgid "Number of Online Players" +msgstr "線上玩家人數" + +msgid "Number of Players" +msgstr "玩家人數" + +msgid "Nunchuk" +msgstr "雙節æ£æŽ§åˆ¶å™¨" + +msgid "OK" +msgstr "完æˆ" + +msgid "OK!" +msgstr "確定!" + +msgid "Ocarina (cheats):" +msgstr "Ocarina (金手指):" + +msgid "Ocarina Cheat Manager" +msgstr "Ocarina 金手指管ç†å™¨" + +msgid "Ocarina: Code Error" +msgstr "Ocarina: 密碼錯誤" + +msgid "Ocarina: Codes found." +msgstr "Ocarina: 找到密碼." + +msgid "Ocarina: No codes found" +msgstr "Ocarina: 沒有找到密碼" + +msgid "Ocarina: Out Of Memory Error" +msgstr "Ocarina: 記憶體ä¸è¶³éŒ¯èª¤" + +msgid "Ocarina: Searching codes..." +msgstr "Ocarina: 正在æœå°‹å¯†ç¢¼..." + +msgid "Ocarina: Too many codes" +msgstr "Ocarina: 太多的密碼" + +msgid "Off" +msgstr "關閉" + +msgid "On" +msgstr "é–‹å•Ÿ" + +msgid "Online" +msgstr "連線" + +msgid "Online Content" +msgstr "線上內容" + +msgid "Online Play" +msgstr "線上éŠæˆ²" + +msgid "Online Players" +msgstr "連上玩家" + +msgid "Online Score List" +msgstr "線上得分清單" + +msgid "Online Updates" +msgstr "線上更新" + +msgid "Open Sort" +msgstr "打開排åº" + +msgid "Open Style" +msgstr "打開風格" + +msgid "Opening DVD disc..." +msgstr "正在開啟 DVD 光碟..." + +#, c-format +msgid "Opening partition: %s" +msgstr "正在開啟分割å€: %s" + +msgid "Options" +msgstr "功能é¸é …" + +msgid "Options discarded for this game." +msgstr "放棄這個éŠæˆ²è¨­å®š." + +msgid "Options saved for this game." +msgstr "儲存這個éŠæˆ²è¨­å®š." + +msgid "Out of memory" +msgstr "記憶體ä¸è¶³" + +msgid "Page:" +msgstr "é æ•¸:" + +msgid "Partition:" +msgstr "分割å€:" + +#, c-format +msgid "Partition: %s not found!" +msgstr "分割å€: %s 找ä¸åˆ°!" + +msgid "Party" +msgstr "èšæœƒ" + +msgid "Paused Start" +msgstr "已暫åœé–‹å§‹" + +msgid "Platformer" +msgstr "å¹³å°" + +msgid "Play Count" +msgstr "éŠæˆ²æ¬¡æ•¸" + +msgid "Players" +msgstr "玩家" + +msgid "Please insert a game disc..." +msgstr "è«‹æ’å…¥éŠæˆ²å…‰ç¢Ÿ..." + +#, c-format +msgid "Press %s button for options." +msgstr "請按 %s 按鈕進入設定." + +#, c-format +msgid "Press %s button to %s." +msgstr "請按 %s 鈕到 %s." + +#, c-format +msgid "Press %s button to apply codes." +msgstr "請按 %s 鈕套用密碼." + +#, c-format +msgid "Press %s button to cancel." +msgstr "請按 %s 鈕å–消." + +#, c-format +msgid "Press %s button to change device." +msgstr "請按 %s 鈕變更è£ç½®." + +#, c-format +msgid "Press %s button to continue." +msgstr "請按 %s 鈕繼續." + +#, c-format +msgid "Press %s button to delete FS." +msgstr "請按 %s 鈕刪除檔案系統." + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "請按 %s 鈕轉儲 BCA." + +#, c-format +msgid "Press %s button to exit." +msgstr "請按 %s 鈕離開." + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "請按 %s 鈕修復延伸或 WBFS 分割å€." + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "請按 %s 鈕格å¼åŒ– WBFS æ ¼å¼." + +#, c-format +msgid "Press %s button to go back." +msgstr "請按 %s 鈕返回." + +#, c-format +msgid "Press %s button to select a partition." +msgstr "請按 %s 鈕é¸æ“‡åˆ†å‰²å€." + +#, c-format +msgid "Press %s button to select." +msgstr "請按 %s 鈕é¸å–." + +#, c-format +msgid "Press %s button to skip codes." +msgstr "請按 %s 鈕略éŽå¯†ç¢¼." + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "請按 %s 鈕åŒæ­¥ FAT 分割å€." + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "請按 %s 全螢幕é è¦½" + +#, c-format +msgid "Press %s for game options" +msgstr "請按 %s 進入éŠæˆ²é¸é …" + +#, c-format +msgid "Press %s for global options" +msgstr "請按 %s 進入全域é¸é …" + +#, c-format +msgid "Press %s to discard options" +msgstr "請按 %s 放棄é¸é …" + +#, c-format +msgid "Press %s to download and update" +msgstr "請按 %s 下載åŠæ›´æ–°" + +#, c-format +msgid "Press %s to download this theme" +msgstr "請按 %s 下載這個主題" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "請按 %s 下載, %s 返回" + +#, c-format +msgid "Press %s to return" +msgstr "請按 %s 返回" + +#, c-format +msgid "Press %s to save global settings" +msgstr "請按 %s 儲存全域設定" + +#, c-format +msgid "Press %s to save options" +msgstr "請按 %s 儲存é¸é …" + +#, c-format +msgid "Press %s to save selection" +msgstr "請按 %s 儲存é¸å–çš„" + +#, c-format +msgid "Press %s to select filter type" +msgstr "請按 %s é¸æ“‡éŽæ¿¾é¡žåž‹" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "請按 %s é¸æ“‡æŽ’åºæ–¹å¼" + +#, c-format +msgid "Press %s to start game" +msgstr "請按 %s 開始éŠæˆ²" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "請按 %s 更新外部 meta.xml" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "請按 %s/%s é¸æ“‡è£ç½®." + +msgid "Press 2 to reload IOS" +msgstr "請按 2 鈕é‡æ–°è¼‰å…¥ IOS" + +msgid "Press A to select device" +msgstr "請按 A 鈕é¸æ“‡è£ç½®" + +msgid "Press B to exit to HBC" +msgstr "請按 B 鈕離開到 HBC" + +msgid "Press HOME to reset" +msgstr "請按 HOME 鈕é‡ç½®" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "請按 å·¦/å³ éˆ•é¸æ“‡ IOS" + +msgid "Press any button to continue..." +msgstr "請按任æ„按鈕繼續..." + +msgid "Press any button to exit..." +msgstr "請按任æ„按鈕離開..." + +msgid "Press any button to restart..." +msgstr "請按任æ„按鈕é‡æ–°å•Ÿå‹•..." + +msgid "Press any button..." +msgstr "請按任æ„按鈕..." + +#, c-format +msgid "Press button %s to download covers." +msgstr "請按 %s 鈕下載å°é¢." + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "請按 %s 鈕退出 DVD 光碟." + +msgid "Profile:" +msgstr "設定檔:" + +#, c-format +msgid "Profile: %s" +msgstr "設定檔: %s" + +msgid "Program Updates" +msgstr "程å¼æ›´æ–°" + +msgid "Publisher" +msgstr "發行者" + +msgid "Puzzle" +msgstr "益智" + +msgid "Quit" +msgstr "退出" + +msgid "RAW" +msgstr "未經處ç†" + +msgid "RIGHT" +msgstr "å³" + +msgid "RPG" +msgstr "角色扮演" + +msgid "Racing" +msgstr "競速" + +#, c-format +msgid "Rated %s" +msgstr "分級 %s" + +msgid "Rating" +msgstr "分級" + +msgid "Read" +msgstr "讀å–" + +msgid "Reading BCA..." +msgstr "æ­£åœ¨è®€å– BCA..." + +msgid "Reboot" +msgstr "é‡æ–°å•Ÿå‹•" + +msgid "Release Date" +msgstr "發售日期" + +msgid "Release Notes: (short)" +msgstr "發佈註釋: (ç°¡ç•¥)" + +msgid "Remove Game" +msgstr "移除éŠæˆ²" + +msgid "Removing game, please wait..." +msgstr "正在移除éŠæˆ², 請等候..." + +msgid "Restarting Wii..." +msgstr "正在é‡æ–°å•Ÿå‹• Wii..." + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "已返回! (ret = %d)" + +msgid "Rhythm" +msgstr "節å¥" + +msgid "Rows:" +msgstr "列數:" + +msgid "Running benchmark, please wait" +msgstr "正在執行基準, 請等候" + +msgid "S. Chinese" +msgstr "中文 (ç°¡é«”)" + +msgid "SD/SDHC Card" +msgstr "SD/SDHC å¡" + +msgid "SUCCESS!" +msgstr "æˆåŠŸ!" + +msgid "Save .gct" +msgstr "儲存 .gct" + +msgid "Save Debug" +msgstr "åµéŒ¯å­˜æª”" + +msgid "Save Settings" +msgstr "儲存設定" + +msgid "Save debug.log" +msgstr "儲存 debug.log" + +msgid "Saving Settings... " +msgstr "正在儲存設定..." + +msgid "Saving cheats..." +msgstr "正在儲存金手指..." + +msgid "Saving gamelist.txt ... " +msgstr "正在儲存 gamelist.txt ... " + +msgid "Saving settings..." +msgstr "正在儲存設定..." + +msgid "Saving:" +msgstr "正在儲存:" + +#, c-format +msgid "Saving: %s" +msgstr "正在儲存: %s" + +msgid "Scroll:" +msgstr "æ²å‹•:" + +msgid "Select Alternative .dol:" +msgstr "é¸æ“‡æ›¿ä»£çš„ .dol:" + +msgid "Select WBFS device:" +msgstr "é¸æ“‡ WBFS è£ç½®:" + +msgid "Select a different partition" +msgstr "é¸æ“‡ä¸åŒçš„分割å€" + +msgid "Select a partition" +msgstr "é¸æ“‡åˆ†å‰²å€" + +msgid "Select all" +msgstr "å…¨é¸" + +msgid "Select the game you want to boot" +msgstr "é¸æ“‡ä½ è¦å•Ÿå‹•çš„éŠæˆ²" + +msgid "Selected Game" +msgstr "é¸å–çš„éŠæˆ²" + +msgid "Settings" +msgstr "設定" + +msgid "Shooter" +msgstr "å°„æ“Š" + +msgid "Show All" +msgstr "顯示全部" + +msgid "Show cIOS info" +msgstr "顯示 cIOS 資訊" + +msgid "Shutdown" +msgstr "關機" + +msgid "Side:" +msgstr "å´é‚Š:" + +msgid "Simulation" +msgstr "模擬" + +msgid "Size(GB) Type Mount Used" +msgstr "大å°(GB) é¡žåž‹ 掛載 已使用" + +#, c-format +msgid "Size: %d bytes" +msgstr "大å°: %d ä½å…ƒçµ„" + +msgid "Sort" +msgstr "排åº" + +msgid "Sort Games" +msgstr "排åºéŠæˆ²" + +msgid "Sort Order:" +msgstr "排åºæ–¹å¼:" + +msgid "Sort Type:" +msgstr "排åºé¡žåž‹:" + +#, c-format +msgid "Sort: %s-%s" +msgstr "排åº: %s-%s" + +msgid "Spanish" +msgstr "西ç­ç‰™æ–‡" + +msgid "Sports" +msgstr "é‹å‹•" + +msgid "Start" +msgstr "開始" + +msgid "Start Game" +msgstr "開始éŠæˆ²" + +msgid "Start this game?" +msgstr "開始這個éŠæˆ²?" + +msgid "Stopping DVD..." +msgstr "åœæ­¢ DVD..." + +msgid "Strategy" +msgstr "戰略" + +msgid "Style" +msgstr "風格" + +msgid "Style:" +msgstr "風格:" + +msgid "Sync FAT free space info?" +msgstr "åŒæ­¥ FAT 分割å€å‰©é¤˜ç©ºé–“資訊?" + +msgid "Synchronizing, please wait." +msgstr "正在åŒæ­¥, 請等候." + +msgid "System" +msgstr "系統" + +msgid "System Def." +msgstr "系統é è¨­." + +msgid "T. Chinese" +msgstr "中文 (ç¹é«”)" + +msgid "Theme" +msgstr "主題" + +msgid "Theme Info" +msgstr "主題資訊" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" +"主題å¯èƒ½å¤ªå¤§.\n" +"建議é‡æ–°å•Ÿå‹• CFG.\n" + +msgid "Theme:" +msgstr "主題:" + +#, c-format +msgid "Theme: %s" +msgstr "主題: %s" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "主題由 wii.spiffy360.com æä¾›" + +msgid "Themes to download" +msgstr "主題下載" + +msgid "Themes with updates" +msgstr "主題更新" + +msgid "Title" +msgstr "標題" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "太多的金手指! (%d)" + +msgid "Too many code lines!" +msgstr "太多行密碼!" + +#, c-format +msgid "Too many fragments! %d" +msgstr "太多的碎片! %d" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "嘗試 (url#%d) ..." + +msgid "UNUSED" +msgstr "尚未使用" + +msgid "UP" +msgstr "上" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "URL '%s' 無法開啟, 需è¦å¸¶æœ‰ 'http://'" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "URL '%s' 路徑部分ä¸å®Œæ•´" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "USB Gecko 發ç¾. 啟用åµéŒ¯." + +msgid "USB Mass Storage Device" +msgstr "USB 大型存放è£ç½®" + +msgid "Unknown" +msgstr "未知的" + +msgid "Unknown syntax!" +msgstr "未知的語法!" + +msgid "Unplayed" +msgstr "尚未玩éŽ" + +msgid "Unplayed Games" +msgstr "沒有玩éŽçš„éŠæˆ²" + +msgid "Update WiiTDB Game Database" +msgstr "æ›´æ–° WiiTDB éŠæˆ²è³‡æ–™åº«" + +msgid "Update themes" +msgstr "更新主題" + +msgid "Update titles.txt" +msgstr "æ›´æ–° titles.txt" + +msgid "Updates" +msgstr "æ›´æ–°" + +msgid "Updating database." +msgstr "正在更新資料庫." + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "已使用: %.1fGB 剩餘: %.1fGB [%d]" + +msgid "Using Saved Alternative .dol:" +msgstr "使用儲存替代 .dol:" + +msgid "Version" +msgstr "版本" + +msgid "Video Patch:" +msgstr "視訊補綴:" + +msgid "Video:" +msgstr "視訊:" + +msgid "View" +msgstr "檢視" + +msgid "Vitality Sensor" +msgstr "活力åµæ¸¬å™¨" + +msgid "WARNING:" +msgstr "警告:" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "WBFS: %.1fGB 剩餘 %.1fGB" + +msgid "Wheel" +msgstr "æ–¹å‘盤" + +msgid "Wii Speak" +msgstr "Wii èŠå¤©è£ç½®" + +msgid "WiiTDB Game Database" +msgstr "WiiTDB éŠæˆ²è³‡æ–™åº«" + +msgid "Wiimote" +msgstr "é™æŽ§å™¨" + +msgid "Write Playlog:" +msgstr "寫入éŠæˆ²è¨˜éŒ„:" + +#, c-format +msgid "Writing to %s" +msgstr "正在寫入到 %s" + +#, c-format +msgid "Writing: %s" +msgstr "正在寫入: %s" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "錯誤的大å°: %d (%d)" + +msgid "Yes" +msgstr "是" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" +"您å¯ä»¥å˜—試移除è£ç½®å¾Œåœ¨æ’回\n" +"或是等候一會" + +msgid "Zapper" +msgstr "æ§æž¶" + +msgid "[ CHANGED ]" +msgstr "[ 已變更 ]" + +msgid "[ FOUND ]" +msgstr "[ 已找到 ]" + +msgid "[ SAVED ]" +msgstr "[ 已儲存 ]" + +msgid "[HOME]" +msgstr "[回到系統é¸å–®]" + +msgid "[No HQ]" +msgstr "[No HQ]" + +msgid "[USED]" +msgstr "[已使用]" + +msgid "[default]" +msgstr "[é è¨­å€¼]" + +msgid "[saved]" +msgstr "[已儲存]" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "壞的 wbfs 檔案: %s" + +msgid "calculating space, please wait..." +msgstr "正在計算空間, 請等候..." + +msgid "delete" +msgstr "刪除" + +#, c-format +msgid "dest: %p - %p" +msgstr "接收: %p - %p" + +msgid "discard" +msgstr "丟棄" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "域å %s 無法被解æž" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "讀å–錯誤: %s (%d)" + +#, c-format +msgid "for %d players" +msgstr "最多 %d 人éŠæˆ²" + +msgid "for 1 player" +msgstr "單人éŠæˆ²" + +msgid "format" +msgstr "æ ¼å¼åŒ–" + +msgid "free" +msgstr "剩餘" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "線性讀å–速度: %.2f mb/s" + +msgid "linear..." +msgstr "線性..." + +#, c-format +msgid "music file too big (%d) %s" +msgstr "音樂檔案太大 (%d) %s" + +msgid "music.mp3 or music.mod not found!" +msgstr "找ä¸åˆ° music.mp3 或 music.mod!" + +msgid "no file" +msgstr "沒有檔案" + +msgid "none" +msgstr "ç„¡" + +msgid "or format a WBFS partition." +msgstr "或格å¼åŒ– WBFS 分割å€." + +msgid "page" +msgstr "é " + +msgid "parse error" +msgstr "剖æžéŒ¯èª¤" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "隨機讀å–速度: %.2f mb/s" + +msgid "random..." +msgstr "隨機..." + +msgid "reset" +msgstr "é‡æ–°è¨­å®š" + +msgid "revert" +msgstr "回復" + +msgid "save" +msgstr "儲存" + +#, c-format +msgid "split error: %s" +msgstr "分割錯誤: %s" + +msgid "uDraw GameTablet" +msgstr "uDraw GameTablet" + +msgid "unable to open wii disc" +msgstr "無法開啟 Wii 光碟" + +#, c-format +msgid "used: %p - %p" +msgstr "已使用: %p - %p" + +#~ msgid "Loading previous game list..." +#~ msgstr "正在載入之å‰çš„éŠæˆ²æ¸…å–®..." diff --git a/Languages/add_bom.sh b/Languages/add_bom.sh new file mode 100644 index 0000000..ee9bc49 --- /dev/null +++ b/Languages/add_bom.sh @@ -0,0 +1,8 @@ +#!/bin/sh +if [ -n "$1" ]; then +perl -e '@file=<>;print "\xEF\xBB\xBF";print(@file);' < "$1" > "$1".tmp +mv "$1".tmp "$1" +else +perl -e '@file=<>;print "\xEF\xBB\xBF";print(@file);' +fi + diff --git a/Languages/del_bom.sh b/Languages/del_bom.sh new file mode 100644 index 0000000..300184a --- /dev/null +++ b/Languages/del_bom.sh @@ -0,0 +1,2 @@ +#!/bin/sh +perl -e '@file=<>;$file[0] =~ s/^\xEF\xBB\xBF//;print(@file);' diff --git a/Languages/lang.pot b/Languages/lang.pot new file mode 100644 index 0000000..a22fce9 --- /dev/null +++ b/Languages/lang.pot @@ -0,0 +1,2282 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-10 11:11-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#, c-format +msgid "%.1fGB free of %.1fGB" +msgstr "" + +#, c-format +msgid "%.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d" +msgstr "" + +#, c-format +msgid "%.2fGB copied in %d:%02d:%02d" +msgstr "" + +#, c-format +msgid "%d available" +msgstr "" + +#, c-format +msgid "%d more notes" +msgstr "" + +#, c-format +msgid "%s Split: %d %s" +msgstr "" + +#, c-format +msgid "%s, please wait..." +msgstr "" + +#, c-format +msgid "%s: Favorites" +msgstr "" + +#, c-format +msgid "%s: GUI " +msgstr "" + +#, c-format +msgid "%s: Options " +msgstr "" + +#, c-format +msgid "(%d online)" +msgstr "" + +#, c-format +msgid "(%d seconds timeout)" +msgstr "" + +msgid "(This can take a couple of minutes)" +msgstr "" + +msgid ".dol too small" +msgstr "" + +msgid "1.2+" +msgstr "" + +msgid "1st-Person Shooter" +msgstr "" + +msgid "2.0+" +msgstr "" + +msgid "2.1+" +msgstr "" + +msgid "2.2+" +msgstr "" + +msgid "3D cover" +msgstr "" + +msgid "3rd-person Shooter" +msgstr "" + +msgid "< ASC >" +msgstr "" + +msgid "< DESC >" +msgstr "" + +msgid "< DOWNLOAD >" +msgstr "" + +msgid "About" +msgstr "" + +msgid "Action" +msgstr "" + +msgid "Additional config:" +msgstr "" + +msgid "Admin Lock:" +msgstr "" + +msgid "Admin Unlock" +msgstr "" + +msgid "Adult" +msgstr "" + +msgid "Adventure" +msgstr "" + +msgid "All" +msgstr "" + +msgid "All Channels" +msgstr "" + +msgid "All Games" +msgstr "" + +msgid "All themes up to date." +msgstr "" + +msgid "Alt Button Cfg:" +msgstr "" + +msgid "Alt dol:" +msgstr "" + +msgid "Alternative .dol:" +msgstr "" + +msgid "Anti 002 Fix:" +msgstr "" + +#, c-format +msgid "App. Path: %s" +msgstr "" + +msgid "Arcade" +msgstr "" + +#, c-format +msgid "" +"Are you sure you want to %s\n" +"this partition?" +msgstr "" + +msgid "" +"Are you sure you want to FIX\n" +"this partition?" +msgstr "" + +msgid "" +"Are you sure you want to remove this\n" +"game?" +msgstr "" + +msgid "Ascending" +msgstr "" + +msgid "Auto" +msgstr "" + +#, c-format +msgid "Auto-start game: %.6s not found!" +msgstr "" + +msgid "Available Updates" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "Balance Board" +msgstr "" + +msgid "Baseball" +msgstr "" + +msgid "Basic" +msgstr "" + +msgid "Basketball" +msgstr "" + +msgid "Bike Racing" +msgstr "" + +msgid "Billiards" +msgstr "" + +msgid "Block IOS Reload:" +msgstr "" + +msgid "Board Game" +msgstr "" + +msgid "Boot Disc" +msgstr "" + +msgid "Boot disc" +msgstr "" + +msgid "Booting game, please wait..." +msgstr "" + +msgid "Bowling" +msgstr "" + +msgid "Boxing" +msgstr "" + +msgid "Business Sim" +msgstr "" + +#, c-format +msgid "CFG base: %s" +msgstr "" + +msgid "Camera" +msgstr "" + +msgid "Can not dump GC savegames.\n" +msgstr "" + +msgid "Can't install Wii games!" +msgstr "" + +msgid "Cancelled." +msgstr "" + +#, c-format +msgid "Cannot create dir: %s" +msgstr "" + +msgid "Cards" +msgstr "" + +msgid "Channel" +msgstr "" + +msgid "Cheat Codes:" +msgstr "" + +msgid "Cheats: " +msgstr "" + +msgid "Check For Updates" +msgstr "" + +msgid "Checking for themes..." +msgstr "" + +msgid "Checking for updates..." +msgstr "" + +msgid "Chess" +msgstr "" + +msgid "Choose a sorting method" +msgstr "" + +msgid "Classic" +msgstr "" + +msgid "Classic Controller" +msgstr "" + +msgid "Clear" +msgstr "" + +msgid "Clear Patches:" +msgstr "" + +msgid "Coaching" +msgstr "" + +msgid "Compare Type" +msgstr "" + +msgid "Compilation" +msgstr "" + +#, c-format +msgid "Complete. Size: %d" +msgstr "" + +#, c-format +msgid "Configurable Loader %s" +msgstr "" + +msgid "Configuration error, MAX_DNS_ENTRIES reached while the list is empty" +msgstr "" + +#, c-format +msgid "Connection error from net_read() Errorcode: %i" +msgstr "" + +msgid "Console" +msgstr "" + +msgid "Construction Sim" +msgstr "" + +msgid "Contains" +msgstr "" + +msgid "Controller" +msgstr "" + +msgid "Cooking" +msgstr "" + +#, c-format +msgid "Could not initialize DIP module! (ret = %d)" +msgstr "" + +msgid "Country Fix:" +msgstr "" + +msgid "Cover" +msgstr "" + +msgid "Cover Image:" +msgstr "" + +msgid "Cover Style" +msgstr "" + +msgid "Covers Available" +msgstr "" + +msgid "Cover~~Back" +msgstr "" + +msgid "Cover~~Front" +msgstr "" + +msgid "Create" +msgstr "" + +msgid "Creator" +msgstr "" + +msgid "Cricket" +msgstr "" + +#, c-format +msgid "Current Version: %s" +msgstr "" + +#, c-format +msgid "" +"Custom IOS %d could not be found!\n" +"Please install it." +msgstr "" + +#, c-format +msgid "Custom IOS %d could not be loaded! (ret = %d)" +msgstr "" + +#, c-format +msgid "" +"Custom IOS %d is a stub!\n" +"Please reinstall it." +msgstr "" + +#, c-format +msgid "Custom IOS %s Loaded OK" +msgstr "" + +msgid "DEVO" +msgstr "" + +msgid "DISC cover" +msgstr "" + +msgid "DOWN" +msgstr "" + +msgid "Dance" +msgstr "" + +msgid "Dance Pad" +msgstr "" + +msgid "Darts" +msgstr "" + +msgid "Database update successful." +msgstr "" + +msgid "Debug" +msgstr "" + +msgid "Delete Game" +msgstr "" + +msgid "Deleting" +msgstr "" + +msgid "Descending" +msgstr "" + +msgid "Developer" +msgstr "" + +msgid "Device is not responding!" +msgstr "" + +msgid "Device:" +msgstr "" + +msgid "Devolution only accepts clean dumps!\n" +msgstr "" + +msgid "Devolution:" +msgstr "" + +msgid "Disc" +msgstr "" + +msgid "Disc (Ask)" +msgstr "" + +msgid "Done." +msgstr "" + +msgid "Download .txt" +msgstr "" + +msgid "Download All Covers" +msgstr "" + +msgid "Download All Missing Covers" +msgstr "" + +msgid "Download Missing Covers" +msgstr "" + +msgid "Download Plugins" +msgstr "" + +msgid "Download Themes" +msgstr "" + +msgid "Download complete." +msgstr "" + +#, c-format +msgid "Download error on gamercard #%d." +msgstr "" + +msgid "Download game information" +msgstr "" + +msgid "Downloadable Content" +msgstr "" + +msgid "Downloading ALL MISSING covers" +msgstr "" + +msgid "Downloading ALL covers" +msgstr "" + +#, c-format +msgid "Downloading ALL covers for %.6s" +msgstr "" + +#, c-format +msgid "Downloading MISSING covers for %.6s" +msgstr "" + +msgid "Downloading Theme previews..." +msgstr "" + +msgid "Downloading cheats..." +msgstr "" + +msgid "Downloading database." +msgstr "" + +msgid "Downloading devolution." +msgstr "" + +msgid "Downloading mighty plugin." +msgstr "" + +msgid "Downloading neek2o plugin." +msgstr "" + +msgid "Downloading titles.txt ..." +msgstr "" + +msgid "Downloading translation file." +msgstr "" + +msgid "Downloading unifont." +msgstr "" + +#, c-format +msgid "Downloading: %s" +msgstr "" + +#, c-format +msgid "Downloading: %s as %s" +msgstr "" + +msgid "Downloads" +msgstr "" + +msgid "Drawing" +msgstr "" + +msgid "Drums" +msgstr "" + +msgid "Dump savegame" +msgstr "" + +msgid "Duplicate ID3" +msgstr "" + +msgid "Dutch" +msgstr "" + +msgid "ERROR" +msgstr "" + +msgid "ERROR creating file" +msgstr "" + +msgid "ERROR game opt" +msgstr "" + +msgid "ERROR reading BCA!" +msgstr "" + +#, c-format +msgid "ERROR removing %s" +msgstr "" + +msgid "ERROR writing BCA!" +msgstr "" + +msgid "ERROR!" +msgstr "" + +#, c-format +msgid "ERROR! (ret = %d)" +msgstr "" + +msgid "ERROR:" +msgstr "" + +#, c-format +msgid "ERROR: %s is not accessible" +msgstr "" + +#, c-format +msgid "ERROR: Apploader %d" +msgstr "" + +msgid "ERROR: CIOS222/223 v4 required for BCA" +msgstr "" + +msgid "ERROR: Cache Close" +msgstr "" + +#, c-format +msgid "ERROR: Could not open game! (ret = %d)" +msgstr "" + +msgid "ERROR: Game already installed!!" +msgstr "" + +#, c-format +msgid "ERROR: GetCerts %d" +msgstr "" + +msgid "ERROR: Invalid Game ID" +msgstr "" + +#, c-format +msgid "ERROR: Loading EHC module! (%d)" +msgstr "" + +msgid "" +"ERROR: NTFS write disabled!\n" +"(set ntfs_write=1)" +msgstr "" + +#, c-format +msgid "ERROR: No partitions found! (ret = %d)" +msgstr "" + +msgid "ERROR: Not a Wii disc!!" +msgstr "" + +#, c-format +msgid "ERROR: Offset(0x%llx) %d" +msgstr "" + +#, c-format +msgid "ERROR: OpenPartition %d" +msgstr "" + +#, c-format +msgid "ERROR: OpenPartition(0x%llx) %d" +msgstr "" + +#, c-format +msgid "ERROR: SetFragList(0): %d" +msgstr "" + +#, c-format +msgid "ERROR: SetWBFS: %d" +msgstr "" + +msgid "ERROR: Setting SD mode" +msgstr "" + +#, c-format +msgid "ERROR: USB init! (%d)" +msgstr "" + +msgid "ERROR: WBFS not mounted!" +msgstr "" + +msgid "" +"ERROR: cIOS249rev18, cIOS222v4,\n" +"cIOS223v4, cIOS224v5 or higher required\n" +"for starting games from a FAT partition!\n" +"Upgrade IOS249 or choose a different IOS." +msgstr "" + +msgid "ERROR: cache: out of memory" +msgstr "" + +#, c-format +msgid "ERROR: creating: %s" +msgstr "" + +#, c-format +msgid "ERROR: get_frag_list: %d" +msgstr "" + +msgid "ERROR: memory overlap!" +msgstr "" + +msgid "" +"ERROR: multiple wbfs partitions\n" +"supported only with IOS222/223-mload" +msgstr "" + +msgid "ERROR: not enough free space!!" +msgstr "" + +msgid "ERROR: ntfs was not cleanly unmounted" +msgstr "" + +#, c-format +msgid "ERROR: opening %.6s" +msgstr "" + +#, c-format +msgid "ERROR: set_frag_list: %d" +msgstr "" + +#, c-format +msgid "ERROR: setting up fragments %d %d" +msgstr "" + +#, c-format +msgid "ERROR: the drive or partition %s/ is not connected!!" +msgstr "" + +#, c-format +msgid "ERROR: writing %s (%d)." +msgstr "" + +msgid "EXTEND" +msgstr "" + +msgid "Educational" +msgstr "" + +msgid "Ejecting DVD..." +msgstr "" + +msgid "English" +msgstr "" + +msgid "English Title" +msgstr "" + +msgid "Enter Code: " +msgstr "" + +msgid "Error GRRLIB init" +msgstr "" + +msgid "Error Initializing Network." +msgstr "" + +msgid "Error adding disc!" +msgstr "" + +#, c-format +msgid "Error allocating buffer: %d" +msgstr "" + +msgid "Error creating directory..." +msgstr "" + +msgid "Error discarding options!" +msgstr "" + +msgid "Error downloading theme preview..." +msgstr "" + +msgid "Error downloading theme..." +msgstr "" + +msgid "Error downloading themes..." +msgstr "" + +msgid "Error downloading update..." +msgstr "" + +msgid "Error downloading updates..." +msgstr "" + +msgid "Error downloading." +msgstr "" + +msgid "Error establishing connection" +msgstr "" + +msgid "Error extracting theme..." +msgstr "" + +msgid "Error opening database, update did not complete." +msgstr "" + +#, c-format +msgid "Error opening: %s" +msgstr "" + +#, c-format +msgid "Error playing %s" +msgstr "" + +msgid "Error reading .dol" +msgstr "" + +msgid "Error reading dol header" +msgstr "" + +#, c-format +msgid "Error saving %s" +msgstr "" + +msgid "Error saving options!" +msgstr "" + +msgid "Error saving settings!" +msgstr "" + +msgid "" +"Error storing playlog file.\n" +"Start from the Wii Menu to fix." +msgstr "" + +msgid "Error: Invalid PNG image!" +msgstr "" + +msgid "Error: no URL." +msgstr "" + +msgid "Error: no data." +msgstr "" + +msgid "Exercise" +msgstr "" + +msgid "Exit" +msgstr "" + +#, c-format +msgid "Extracting: %s" +msgstr "" + +msgid "FAIL" +msgstr "" + +#, c-format +msgid "FAT: ALLOC ERROR %p %p %p" +msgstr "" + +#, c-format +msgid "FATAL: alloc grid(%d)" +msgstr "" + +#, c-format +msgid "FILL %d" +msgstr "" + +msgid "FLAT cover" +msgstr "" + +msgid "FULL cover" +msgstr "" + +msgid "Fav" +msgstr "" + +msgid "Fav: Off" +msgstr "" + +msgid "Fav: On" +msgstr "" + +msgid "Favorite" +msgstr "" + +msgid "Favorite Games" +msgstr "" + +msgid "Favorite:" +msgstr "" + +msgid "Favorites:" +msgstr "" + +msgid "Fighting" +msgstr "" + +#, c-format +msgid "File not found! %s" +msgstr "" + +msgid "Filter" +msgstr "" + +msgid "Filter Games" +msgstr "" + +msgid "Filter by Controller" +msgstr "" + +msgid "Filter by Game Type" +msgstr "" + +msgid "Filter by Genre" +msgstr "" + +msgid "Filter by Online Features" +msgstr "" + +msgid "Filter:" +msgstr "" + +msgid "Fishing" +msgstr "" + +msgid "Fitness" +msgstr "" + +msgid "Fixing EXTEND partition..." +msgstr "" + +msgid "Flight Sim" +msgstr "" + +msgid "Football" +msgstr "" + +msgid "Force Devolution:" +msgstr "" + +msgid "Force NTSC" +msgstr "" + +msgid "Force NTSC 480p" +msgstr "" + +msgid "Force PAL" +msgstr "" + +msgid "Force PAL 480p" +msgstr "" + +msgid "Force PAL50" +msgstr "" + +msgid "Force PAL60" +msgstr "" + +msgid "Formatting" +msgstr "" + +#, c-format +msgid "Found %s" +msgstr "" + +msgid "French" +msgstr "" + +msgid "Full" +msgstr "" + +msgid "Futuristic Racing" +msgstr "" + +msgid "GB" +msgstr "" + +msgid "Game Default" +msgstr "" + +msgid "Game ID" +msgstr "" + +msgid "Game Options" +msgstr "" + +#, c-format +msgid "Game Options: %s" +msgstr "" + +msgid "Game Type" +msgstr "" + +msgid "GameCube" +msgstr "" + +msgid "Gamecube" +msgstr "" + +msgid "Gamer Card:" +msgstr "" + +#, c-format +msgid "Gamercard #%d reported: %.*s" +msgstr "" + +msgid "Genre" +msgstr "" + +msgid "German" +msgstr "" + +msgid "Global Options" +msgstr "" + +msgid "Golf" +msgstr "" + +msgid "Guitar" +msgstr "" + +msgid "HQ cover" +msgstr "" + +msgid "HTTP Response was without a file" +msgstr "" + +msgid "Health" +msgstr "" + +msgid "Hidden Object" +msgstr "" + +msgid "Hide" +msgstr "" + +msgid "Hide Game:" +msgstr "" + +msgid "Hockey" +msgstr "" + +msgid "Hold button B to cancel." +msgstr "" + +msgid "Home Menu" +msgstr "" + +msgid "Homebrew Channel" +msgstr "" + +msgid "Hook Type:" +msgstr "" + +msgid "Hunting" +msgstr "" + +#, c-format +msgid "ID mismatch: [%.6s] [%.6s]" +msgstr "" + +msgid "IOS Reload: Blocked" +msgstr "" + +#, c-format +msgid "IOS_Open(%s) failed with code %d" +msgstr "" + +msgid "" +"If Cfg does not start properly\n" +"next time, copy boot.dol.bak over\n" +"boot.dol and try again." +msgstr "" + +#, c-format +msgid "Inconsistency in LZ77 encoding %x" +msgstr "" + +msgid "Infinity Base" +msgstr "" + +msgid "Info" +msgstr "" + +#, c-format +msgid "Info file: %s" +msgstr "" + +msgid "Initializing Network..." +msgstr "" + +msgid "Install" +msgstr "" + +msgid "Install Date" +msgstr "" + +msgid "Install Game" +msgstr "" + +msgid "Install game" +msgstr "" + +#, c-format +msgid "Installation ERROR! (ret = %d)" +msgstr "" + +msgid "Installing game, please wait..." +msgstr "" + +msgid "Invalid .dol" +msgstr "" + +#, c-format +msgid "Invalid partition: '%s'" +msgstr "" + +msgid "Italian" +msgstr "" + +msgid "Japanese" +msgstr "" + +msgid "Japanese Title" +msgstr "" + +msgid "Jump" +msgstr "" + +msgid "Karaoke" +msgstr "" + +msgid "Kart Racing" +msgstr "" + +msgid "Keyboard" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "LED:" +msgstr "" + +msgid "LEFT" +msgstr "" + +msgid "LOCKED!" +msgstr "" + +msgid "Language:" +msgstr "" + +msgid "Last Play Date" +msgstr "" + +msgid "Launch Methods" +msgstr "" + +msgid "Life Simulation" +msgstr "" + +msgid "Load OK!" +msgstr "" + +#, c-format +msgid "Loader Version: %s" +msgstr "" + +msgid "Loading ..." +msgstr "" + +#, c-format +msgid "Loading..%s\n" +msgstr "" + +msgid "Main" +msgstr "" + +msgid "Main Menu" +msgstr "" + +msgid "" +"Make sure USB port 0 is used!\n" +"(The one nearest to the edge)" +msgstr "" + +msgid "Manage" +msgstr "" + +msgid "Manage Cheats" +msgstr "" + +msgid "Management Sim" +msgstr "" + +msgid "Martial Arts" +msgstr "" + +msgid "Microphone" +msgstr "" + +msgid "Mighty Plugin" +msgstr "" + +msgid "Motion+" +msgstr "" + +msgid "Motorcycle Racing" +msgstr "" + +msgid "Mounting device, please wait..." +msgstr "" + +msgid "Music" +msgstr "" + +#, c-format +msgid "Music file from dir: %s" +msgstr "" + +#, c-format +msgid "Music file size: %d" +msgstr "" + +#, c-format +msgid "Music file: %s" +msgstr "" + +msgid "Music: Disabled" +msgstr "" + +msgid "Music: Enabled" +msgstr "" + +#, c-format +msgid "Music: Looking for %s files in: %s" +msgstr "" + +#, c-format +msgid "Music: Next file index to play: %i" +msgstr "" + +#, c-format +msgid "Music: Number of %s files found: %i" +msgstr "" + +msgid "Music: musicArray contents: " +msgstr "" + +#, c-format +msgid "" +"NAND Emu Path:\n" +"%s\n" +msgstr "" + +msgid "NAND Emu:" +msgstr "" + +msgid "NMM:" +msgstr "" + +msgid "" +"NOTE: CIOS249 before rev10:\n" +"Loading games from SDHC not supported!" +msgstr "" + +msgid "" +"NOTE: CIOS249 before rev14:\n" +"Possible error #001 is not handled!" +msgstr "" + +msgid "" +"NOTE: CIOS249 before rev9:\n" +"Loading games from USB not supported!" +msgstr "" + +msgid "" +"NOTE: You are using CIOS249 rev13:\n" +"To quit the game you must reset or\n" +"power-off the Wii before you start\n" +"another game or it will hang here!" +msgstr "" + +msgid "" +"NOTE: You are using CIOS249 rev14:\n" +"It has known problems with dual layer\n" +"games. It is highly recommended that\n" +"you use a different IOS or revision\n" +"for installation/playback of this game." +msgstr "" + +msgid "" +"NOTE: loader restart is required\n" +"for the update to take effect." +msgstr "" + +msgid "" +"NOTE: may loader restart is required\n" +"for the settings to take effect." +msgstr "" + +#, c-format +msgid "" +"NOTE: partition P#%d is type EXTEND but\n" +"contains a WBFS filesystem. This is an\n" +"invalid setup. Press %s to change the\n" +"partition type from EXTEND to data." +msgstr "" + +msgid "NTFS compression not supported!" +msgstr "" + +msgid "NTFS encryption not supported!" +msgstr "" + +msgid "NTSC-J patch:" +msgstr "" + +msgid "Neek2o Plugin" +msgstr "" + +msgid "Network connection established." +msgstr "" + +msgid "Network error. Can't update gamercards." +msgstr "" + +msgid "New Themes" +msgstr "" + +msgid "Nintendo DS" +msgstr "" + +msgid "Nintendo DS Connectivity" +msgstr "" + +msgid "No" +msgstr "" + +#, c-format +msgid "No domain part in URL '%s'" +msgstr "" + +msgid "No games found!" +msgstr "" + +msgid "No partition selected!" +msgstr "" + +msgid "No themes found." +msgstr "" + +msgid "No updates found." +msgstr "" + +msgid "NoDisc:" +msgstr "" + +msgid "None found on disc" +msgstr "" + +msgid "Not Found boot.dol!" +msgstr "" + +msgid "Not Found!" +msgstr "" + +msgid "Number of Online Players" +msgstr "" + +msgid "Number of Players" +msgstr "" + +msgid "Nunchuk" +msgstr "" + +msgid "OK" +msgstr "" + +msgid "OK!" +msgstr "" + +msgid "Ocarina (cheats):" +msgstr "" + +msgid "Ocarina Cheat Manager" +msgstr "" + +msgid "Ocarina: Code Error" +msgstr "" + +msgid "Ocarina: Codes found." +msgstr "" + +msgid "Ocarina: No codes found" +msgstr "" + +msgid "Ocarina: Out Of Memory Error" +msgstr "" + +msgid "Ocarina: Searching codes..." +msgstr "" + +msgid "Ocarina: Too many codes" +msgstr "" + +msgid "Off" +msgstr "" + +msgid "Off-Road Racing" +msgstr "" + +msgid "On" +msgstr "" + +msgid "Online" +msgstr "" + +msgid "Online Content" +msgstr "" + +msgid "Online Play" +msgstr "" + +msgid "Online Players" +msgstr "" + +msgid "Online Score List" +msgstr "" + +msgid "Online Updates" +msgstr "" + +msgid "Open Sort" +msgstr "" + +msgid "Open Style" +msgstr "" + +msgid "Opening DVD disc..." +msgstr "" + +#, c-format +msgid "Opening partition: %s" +msgstr "" + +msgid "Options" +msgstr "" + +msgid "Options discarded for this game." +msgstr "" + +msgid "Options saved for this game." +msgstr "" + +msgid "Out of memory" +msgstr "" + +#, c-format +msgid "P1 %3u%% | P2 %3u%% | P3 %3u%% | P4 %3u%%" +msgstr "" + +msgid "PAD HOOK" +msgstr "" + +msgid "PAD HOOK:" +msgstr "" + +msgid "Page:" +msgstr "" + +msgid "Partial" +msgstr "" + +msgid "Partition:" +msgstr "" + +#, c-format +msgid "Partition: %s not found!" +msgstr "" + +msgid "Party" +msgstr "" + +msgid "Paused Start" +msgstr "" + +msgid "Petanque" +msgstr "" + +msgid "Pinball" +msgstr "" + +msgid "Platformer" +msgstr "" + +msgid "Play Count" +msgstr "" + +msgid "Players" +msgstr "" + +msgid "Please insert a game disc..." +msgstr "" + +msgid "Plugin:" +msgstr "" + +msgid "Poker" +msgstr "" + +msgid "Portal of Power" +msgstr "" + +#, c-format +msgid "Press %s button for options." +msgstr "" + +#, c-format +msgid "Press %s button to %s." +msgstr "" + +#, c-format +msgid "Press %s button to apply codes." +msgstr "" + +#, c-format +msgid "Press %s button to cancel." +msgstr "" + +#, c-format +msgid "Press %s button to change device." +msgstr "" + +#, c-format +msgid "Press %s button to continue." +msgstr "" + +#, c-format +msgid "Press %s button to delete FS." +msgstr "" + +#, c-format +msgid "Press %s button to dump BCA." +msgstr "" + +#, c-format +msgid "Press %s button to exit." +msgstr "" + +#, c-format +msgid "Press %s button to fix EXTEND/WBFS." +msgstr "" + +#, c-format +msgid "Press %s button to format WBFS." +msgstr "" + +#, c-format +msgid "Press %s button to go back." +msgstr "" + +#, c-format +msgid "Press %s button to select a partition." +msgstr "" + +#, c-format +msgid "Press %s button to select." +msgstr "" + +#, c-format +msgid "Press %s button to skip codes." +msgstr "" + +#, c-format +msgid "Press %s button to sync FAT." +msgstr "" + +#, c-format +msgid "Press %s for fullscreen preview" +msgstr "" + +#, c-format +msgid "Press %s for game options" +msgstr "" + +#, c-format +msgid "Press %s for global options" +msgstr "" + +#, c-format +msgid "Press %s to discard options" +msgstr "" + +#, c-format +msgid "Press %s to download and update" +msgstr "" + +#, c-format +msgid "Press %s to download this theme" +msgstr "" + +#, c-format +msgid "Press %s to download, %s to return" +msgstr "" + +#, c-format +msgid "Press %s to return" +msgstr "" + +#, c-format +msgid "Press %s to save global settings" +msgstr "" + +#, c-format +msgid "Press %s to save options" +msgstr "" + +#, c-format +msgid "Press %s to save selection" +msgstr "" + +#, c-format +msgid "Press %s to select filter type" +msgstr "" + +#, c-format +msgid "Press %s to select sorting method" +msgstr "" + +#, c-format +msgid "Press %s to start game" +msgstr "" + +#, c-format +msgid "Press %s to update without meta.xml" +msgstr "" + +#, c-format +msgid "Press %s/%s to select device." +msgstr "" + +msgid "Press 1 to skip WBFS mounting" +msgstr "" + +msgid "Press 2 to reload IOS" +msgstr "" + +msgid "Press A to continue without config.txt" +msgstr "" + +msgid "Press A to select device" +msgstr "" + +msgid "Press B to exit to HBC" +msgstr "" + +msgid "Press HOME to reset" +msgstr "" + +msgid "Press LEFT/RIGHT to select IOS" +msgstr "" + +msgid "Press any button to continue..." +msgstr "" + +msgid "Press any button to exit..." +msgstr "" + +msgid "Press any button to restart..." +msgstr "" + +msgid "Press any button.\n" +msgstr "" + +msgid "Press any button..." +msgstr "" + +#, c-format +msgid "Press button %s to download covers." +msgstr "" + +#, c-format +msgid "Press button %s to eject DVD." +msgstr "" + +msgid "Priiloader" +msgstr "" + +msgid "Profile:" +msgstr "" + +#, c-format +msgid "Profile: %s" +msgstr "" + +msgid "Program Updates" +msgstr "" + +msgid "Publisher" +msgstr "" + +msgid "Puzzle" +msgstr "" + +msgid "Quit" +msgstr "" + +msgid "RAW" +msgstr "" + +msgid "RIGHT" +msgstr "" + +msgid "RPG" +msgstr "" + +msgid "Racing" +msgstr "" + +msgid "Rail Shooter" +msgstr "" + +#, c-format +msgid "Rated %s" +msgstr "" + +msgid "Rating" +msgstr "" + +msgid "Read" +msgstr "" + +msgid "Reading BCA..." +msgstr "" + +msgid "Reboot" +msgstr "" + +msgid "Region" +msgstr "" + +msgid "Release Date" +msgstr "" + +msgid "Release Notes: (short)" +msgstr "" + +msgid "Removing game, please wait..." +msgstr "" + +msgid "Restarting Wii..." +msgstr "" + +#, c-format +msgid "Returned! (ret = %d)" +msgstr "" + +msgid "Rhythm" +msgstr "" + +msgid "Rows:" +msgstr "" + +msgid "Rugby" +msgstr "" + +msgid "Running benchmark, please wait" +msgstr "" + +msgid "S. Chinese" +msgstr "" + +msgid "SD/SDHC Card" +msgstr "" + +msgid "SUCCESS!" +msgstr "" + +msgid "Save .gct" +msgstr "" + +msgid "Save Debug" +msgstr "" + +msgid "Save Settings" +msgstr "" + +msgid "Save debug.log" +msgstr "" + +msgid "Savegame not found.\n" +msgstr "" + +msgid "Savegame:" +msgstr "" + +msgid "Saving Settings... " +msgstr "" + +msgid "Saving cheats..." +msgstr "" + +msgid "Saving gamelist.txt ... " +msgstr "" + +msgid "Saving settings..." +msgstr "" + +msgid "Saving:" +msgstr "" + +#, c-format +msgid "Saving: %s" +msgstr "" + +msgid "Screenshot:" +msgstr "" + +msgid "Scroll:" +msgstr "" + +msgid "Search" +msgstr "" + +msgid "Search for:" +msgstr "" + +msgid "Select Alternative .dol:" +msgstr "" + +msgid "Select WBFS device:" +msgstr "" + +msgid "Select a different partition" +msgstr "" + +msgid "Select a partition" +msgstr "" + +msgid "Select all" +msgstr "" + +msgid "Select device:" +msgstr "" + +msgid "Select the game you want to boot" +msgstr "" + +msgid "Selected Game" +msgstr "" + +msgid "Settings" +msgstr "" + +msgid "Shooter" +msgstr "" + +msgid "Show All" +msgstr "" + +msgid "Show cIOS info" +msgstr "" + +msgid "Shutdown" +msgstr "" + +msgid "Side:" +msgstr "" + +msgid "Sim Racing" +msgstr "" + +msgid "Simulation" +msgstr "" + +msgid "Size(GB) Type Mount Used" +msgstr "" + +#, c-format +msgid "Size: %d bytes" +msgstr "" + +msgid "Skateboard" +msgstr "" + +msgid "Skateboarding" +msgstr "" + +msgid "Skiing" +msgstr "" + +msgid "Snowboarding" +msgstr "" + +msgid "Soccer" +msgstr "" + +msgid "Sort" +msgstr "" + +msgid "Sort Games" +msgstr "" + +msgid "Sort Order:" +msgstr "" + +msgid "Sort Type:" +msgstr "" + +#, c-format +msgid "Sort: %s-%s" +msgstr "" + +msgid "Spanish" +msgstr "" + +msgid "Sports" +msgstr "" + +msgid "Start" +msgstr "" + +msgid "Start Game" +msgstr "" + +msgid "Start this game?" +msgstr "" + +msgid "Stealth Action" +msgstr "" + +msgid "Stopping DVD..." +msgstr "" + +msgid "Strategy" +msgstr "" + +msgid "Style" +msgstr "" + +msgid "Style:" +msgstr "" + +msgid "Surfing" +msgstr "" + +msgid "Survival Horror" +msgstr "" + +msgid "Sync FAT free space info?" +msgstr "" + +msgid "Synchronizing, please wait." +msgstr "" + +msgid "Synopsis" +msgstr "" + +msgid "Synopsis Length" +msgstr "" + +msgid "System" +msgstr "" + +msgid "System Def." +msgstr "" + +msgid "T. Chinese" +msgstr "" + +msgid "Table Tennis" +msgstr "" + +msgid "Tennis" +msgstr "" + +msgid "Theme" +msgstr "" + +msgid "Theme Info" +msgstr "" + +msgid "" +"Theme may be too large.\n" +"Reboot of Cfg suggested.\n" +msgstr "" + +msgid "Theme:" +msgstr "" + +#, c-format +msgid "Theme: %s" +msgstr "" + +msgid "Themes provided by wii.spiffy360.com" +msgstr "" + +msgid "Themes to download" +msgstr "" + +msgid "Themes with updates" +msgstr "" + +msgid "Title" +msgstr "" + +#, c-format +msgid "Too many cheats! (%d)" +msgstr "" + +msgid "Too many code lines!" +msgstr "" + +#, c-format +msgid "Too many fragments! %d" +msgstr "" + +msgid "Total Body Tracking" +msgstr "" + +msgid "Train Simulation" +msgstr "" + +msgid "Trivia" +msgstr "" + +msgid "Truck Racing" +msgstr "" + +#, c-format +msgid "Trying (url#%d) ..." +msgstr "" + +msgid "Turntable" +msgstr "" + +msgid "UNUSED" +msgstr "" + +msgid "UP" +msgstr "" + +#, c-format +msgid "URL '%s' doesn't start with 'http://'" +msgstr "" + +#, c-format +msgid "URL '%s' has no PATH part" +msgstr "" + +msgid "USB Gecko found. Debugging is enabled." +msgstr "" + +msgid "USB Mass Storage Device" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Unknown syntax!" +msgstr "" + +msgid "Unplayed" +msgstr "" + +msgid "Unplayed Games" +msgstr "" + +msgid "Update themes" +msgstr "" + +msgid "Updates" +msgstr "" + +msgid "Updating database." +msgstr "" + +msgid "Updating devolution" +msgstr "" + +#, c-format +msgid "Used: %.1fGB Free: %.1fGB [%d]" +msgstr "" + +msgid "Using Saved Alternative .dol:" +msgstr "" + +msgid "VC-Arcade" +msgstr "" + +msgid "VC-Commodore 64" +msgstr "" + +msgid "VC-N64" +msgstr "" + +msgid "VC-NES" +msgstr "" + +msgid "VC-Neo Geo" +msgstr "" + +msgid "VC-SMS" +msgstr "" + +msgid "VC-SNES" +msgstr "" + +msgid "VC-Sega Genesis" +msgstr "" + +msgid "VC-TurboGrafx-16" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Video Patch:" +msgstr "" + +msgid "Video:" +msgstr "" + +msgid "View" +msgstr "" + +msgid "Virtual Pet" +msgstr "" + +msgid "Vitality Sensor" +msgstr "" + +msgid "Volleyball" +msgstr "" + +msgid "WARNING:" +msgstr "" + +#, c-format +msgid "WBFS: %.1fGB free of %.1fGB" +msgstr "" + +msgid "Watercraft Racing" +msgstr "" + +msgid "Wheel" +msgstr "" + +msgid "Wide Screen:" +msgstr "" + +msgid "Wii" +msgstr "" + +msgid "Wii Channel" +msgstr "" + +msgid "Wii Speak" +msgstr "" + +msgid "WiiWare" +msgstr "" + +msgid "Wiimote" +msgstr "" + +msgid "Wrestling" +msgstr "" + +msgid "Write Playlog:" +msgstr "" + +#, c-format +msgid "Writing to %s" +msgstr "" + +#, c-format +msgid "Writing: %s" +msgstr "" + +#, c-format +msgid "Wrong Size: %d (%d)" +msgstr "" + +msgid "Yes" +msgstr "" + +msgid "" +"You can also try unplugging\n" +"and plugging back the device,\n" +"or just wait some more" +msgstr "" + +msgid "Zapper" +msgstr "" + +msgid "[ CHANGED ]" +msgstr "" + +msgid "[ FOUND ]" +msgstr "" + +msgid "[ SAVED ]" +msgstr "" + +msgid "[HOME]" +msgstr "" + +msgid "[No HQ]" +msgstr "" + +msgid "[USED]" +msgstr "" + +msgid "[default]" +msgstr "" + +msgid "[saved]" +msgstr "" + +#, c-format +msgid "bad wbfs file: %s" +msgstr "" + +msgid "calculating space, please wait..." +msgstr "" + +msgid "delete" +msgstr "" + +#, c-format +msgid "dest: %p - %p" +msgstr "" + +msgid "discard" +msgstr "" + +#, c-format +msgid "domain %s could not be resolved" +msgstr "" + +#, c-format +msgid "error reading: %s (%d)" +msgstr "" + +#, c-format +msgid "for %d players" +msgstr "" + +msgid "for 1 player" +msgstr "" + +msgid "format" +msgstr "" + +msgid "free" +msgstr "" + +#, c-format +msgid "linear read speed: %.2f mb/s" +msgstr "" + +msgid "linear..." +msgstr "" + +#, c-format +msgid "loader.bin size: %d" +msgstr "" + +#, c-format +msgid "music file too big (%d) %s" +msgstr "" + +msgid "music.mp3 or music.mod not found!" +msgstr "" + +msgid "no file" +msgstr "" + +msgid "none" +msgstr "" + +msgid "or format a WBFS partition." +msgstr "" + +msgid "page" +msgstr "" + +msgid "parse error" +msgstr "" + +msgid "r51-" +msgstr "" + +msgid "r52+" +msgstr "" + +#, c-format +msgid "random read speed: %.2f mb/s" +msgstr "" + +msgid "random..." +msgstr "" + +msgid "reset" +msgstr "" + +msgid "revert" +msgstr "" + +msgid "save" +msgstr "" + +#, c-format +msgid "split error: %s" +msgstr "" + +msgid "uDraw GameTablet" +msgstr "" + +msgid "unable to open wii disc" +msgstr "" + +#, c-format +msgid "used: %p - %p" +msgstr "" diff --git a/Languages/misc.sh b/Languages/misc.sh new file mode 100644 index 0000000..d3b3914 --- /dev/null +++ b/Languages/misc.sh @@ -0,0 +1,13 @@ +#!/bin/sh +for L in *.lang ; do + echo $L + ./msgreplace.pl "Press " "\b[AB12]\b" "%s" < $L > $L.new + mv $L.new $L + ./msgreplace.pl "Press " "[-+] " "%s " < $L > $L.new + mv $L.new $L + ./msgreplace.pl "Press " "Home" "%s" < $L > $L.new + mv $L.new $L + ./msgreplace.pl "Press " "LEFT/RIGHT" "%s/%s" < $L > $L.new + mv $L.new $L +done + diff --git a/Languages/msg_merge.sh b/Languages/msg_merge.sh new file mode 100644 index 0000000..0245e7d --- /dev/null +++ b/Languages/msg_merge.sh @@ -0,0 +1,33 @@ +#!/bin/sh +POT=$1 + +if [ -z "$POT" ]; then + echo "usage: $0 langXX.pot" + exit +fi + +function del_bom +{ + perl -e '@file=<>;$file[0] =~ s/^\xEF\xBB\xBF//;print(@file);' +} + +function add_bom +{ + perl -e '@file=<>;print "\xEF\xBB\xBF";print(@file);' +} + +for L in *.lang ; do + echo $L + + # check for duplicates + del_bom < $L | msguniq -d > $L.dup + if [ -s $L.dup ]; then + echo DUPLICATES: $L.dup + else + rm $L.dup + fi + + del_bom < $L | msguniq --use-first | msgmerge --sort-output --no-wrap --no-location -N - "$POT" | add_bom > $L.new + mv $L.new $L +done + diff --git a/Languages/msg_miss.sh b/Languages/msg_miss.sh new file mode 100644 index 0000000..72d0102 --- /dev/null +++ b/Languages/msg_miss.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +function del_bom +{ + perl -e '@file=<>;$file[0] =~ s/^\xEF\xBB\xBF//;print(@file);' +} + +function add_bom +{ + perl -e '@file=<>;print "\xEF\xBB\xBF";print(@file);' +} + +for L in *.lang ; do + LM=`basename $L .lang`_miss.lang + echo $L $LM + del_bom < $L | msgattrib --untranslated | add_bom > $LM +done + diff --git a/Languages/msgreplace.pl b/Languages/msgreplace.pl new file mode 100644 index 0000000..5e6d590 --- /dev/null +++ b/Languages/msgreplace.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl + +#print scalar(@ARGV), ":", @ARGV, "\n"; + +if (scalar(@ARGV) != 3) { + print "Usage: $0 msgid_text search replace\n"; + exit; +} + +$msgid = $ARGV[0]; +$search = $ARGV[1]; +$replace = $ARGV[2]; +shift @ARGV; +shift @ARGV; +shift @ARGV; + +while (<>) { + if (/^msgid/) { + #print "MSGID: $_"; + $read_id = $_; + while (<>) { + if (/^msgstr/) { last; } + $read_id .= $_; + } + # msgstr + $read_str = $_; + while (<>) { + if (!/^"/) { last; } + $read_str .= $_; + } + if ($read_id =~ /$msgid/) { + $read_id =~ s/$search/$replace/g; + $read_str =~ s/$search/$replace/g; + } + print $read_id; + print $read_str; + } + print; +} + diff --git a/Languages/msgreplace.sh b/Languages/msgreplace.sh new file mode 100644 index 0000000..93bb860 --- /dev/null +++ b/Languages/msgreplace.sh @@ -0,0 +1,11 @@ +#!/bin/sh +if [ $# != 3 ]; then + echo "usage: $0 msgid_text search replace" + exit +fi +for L in *.lang ; do + echo $L + ./msgreplace.pl "$@" < $L > $L.new + mv $L.new $L +done + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..67b2a71 --- /dev/null +++ b/Makefile @@ -0,0 +1,320 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +VERSION := 70r78 +RELEASE := release +# to override RELEASE use: make announce RELEASE=beta +ifeq ($(findstring compat,$(VERSION)),compat) + RELEASE := release +else ifeq ($(findstring debug,$(VERSION)),debug) + RELEASE := debug +else ifeq ($(findstring c,$(VERSION)),c) + RELEASE := bugfix +else ifeq ($(findstring b,$(VERSION)),b) + RELEASE := beta +else ifeq ($(findstring a,$(VERSION)),a) + RELEASE := alpha +else ifeq ($(findstring t,$(VERSION)),t) + RELEASE := test +else ifeq ($(findstring x,$(VERSION)),x) + RELEASE := experimental +endif + +BINBASE := cfg$(VERSION) +TARGET := $(BINBASE) +BUILD := build +SOURCES := source source/pngu source/libwbfs source/unzip +DATA := data +INCLUDES := +GETTEXT := tools/gettext +UPLOAD := tools/googlecode_upload.py +GAUTH ?= -u username -w passwd +INSTDIR := ../Cfg_USB_Loader_$(VERSION) + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +PORTLIBS := $(DEVKITPRO)/portlibs/ppc + +BUILD_222 := 0 +ifeq ($(BUILD_222),1) + BUILD := build_222 + TARGET := $(BINBASE)-222 + BUILD_222_FLAG := -DBUILD_222=$(BUILD_222) +endif + +BUILD_DEBUG := 0 +ifeq ($(BUILD_DEBUG),1) + BUILD := build_dbg + TARGET := $(BINBASE)-dbg + BUILD_DBG_FLAG := -DBUILD_DBG=3 +endif +#"-g" tells the compiler to include support for the debugger +#"-Wall" tells it to warn us about all suspicious-looking code +DEBUG_OPT = -Os +#DEBUG_OPT = -g +BUILD_FLAGS = -DVERSION=$(VERSION) -DDEVKITPPCVER=$(DKV) -DCCOPT=\"$(DEBUG_OPT)\" $(BUILD_222_FLAG) $(BUILD_DBG_FLAG) +CFLAGS = $(DEBUG_OPT) -Wall $(MACHDEP) $(INCLUDE) $(BUILD_FLAGS) $(DEFINES) +CXXFLAGS = $(CFLAGS) + +# start address: +# Original: 0x80a00100 +# NeoGammaR4: 0x80dfff00 +# cfg 33-36: 0x80c00000 +# cfg 37-49: 0x80b00000 +# cfg 50-..: 0x80a80000 +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80a80000 + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +#LIBS := -lmetaphrasis -lfreetype -lasnd -lpng -lz -lfat -lwiiuse -lbte -lmad -lmodplay -logc -lm -ltremor +LIBAESND=$(wildcard $(LIBOGC_LIB)/libaesnd.a) +ifneq ($(strip $(LIBAESND)),) +LAESND := -laesnd +endif + +LIBS := -lgrrlib -lfat -lntfs -lext2fs -lpng -ljpeg -lwiiuse -lbte -lmad -lmodplay -lasnd -logc -lm -lz + + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +#LIBDIRS := $(DEVKITPPC)/lib $(CURDIR) $(CURDIR)/lib/png $(CURDIR)/lib/freetype $(CURDIR)/lib/libfat +LIBDIRS := $(DEVKITPPC)/lib $(CURDIR) $(CURDIR)/lib/png $(CURDIR)/lib/libfat $(CURDIR)/lib/libntfs $(CURDIR)/lib/libext2fs $(CURDIR)/lib/jpeg $(CURDIR)/lib/grrlib $(PORTLIBS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export PATH := $(CURDIR)/$(GETTEXT):$(PATH) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) -I$(LIBOGC_INC)/ogc -I$(LIBOGC_INC)/ogc/machine + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib$(DKV) -L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) 222 debug lang all clean tags + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @echo + @echo $(CURDIR) + @echo Building $(TARGET) $(BUILD_FLAGS) + @grep _V_STR $(LIBOGC_INC)/ogc/libversion.h | cut -f2 -d'"' + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +222: + @$(MAKE) --no-print-directory BUILD_222=1 + +debug: + @$(MAKE) --no-print-directory BUILD_DEBUG=1 + +#--------------------------------------------------------------------------------- +lang: + @[ -d Languages ] || mkdir -p Languages + @echo + @echo "Building Language File ($@$(VERSION).pot)" + @xgettext --sort-output --no-wrap --no-location -k -kgt -kgts -o Languages/$@$(VERSION).pot source/*.c + +#--------------------------------------------------------------------------------- +all: $(BUILD) 222 lang + @echo Done All +# @make --no-print-directory build +# @make --no-print-directory BUILD_222=1 +# @make --no-print-directory lang + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + @rm -fr $(BUILD)_222 $(OUTPUT)-222.elf $(OUTPUT)-222.dol + @rm -fr $(BUILD)_dbg $(OUTPUT)-dbg.elf $(OUTPUT)-dbg.dol + +cleanall: clean + @rm -fr *.dol *.elf + + +#--------------------------------------------------------------------------------- +run: + wiiload $(OUTPUT).dol debug=0 simple=0 home=exit gui=coverflow3d gui_style=grid gui_antialias=4 gui_compress_covers=1 music=sd://mp3 button_2=random +# wiiload $(OUTPUT).dol simple=0 device=usb cover_style=3d widescreen=1 covers_size=100,100 debug=1 music=sd://mp3 gui_font=Vera_Mono_12_Bold.png hide_game=RB7E admin_lock=1 wpreview_coords=10,100,200,0 +# widescreen = [auto], 0, 1 +# home = [reboot], exit, screenshot +# cover_style = [standard], 3d, disc + +DATE=$(shell date +%F) +SIZE=$(shell stat -c %s $(BINBASE).dol 2>/dev/null) +SIZE_222=$(shell stat -c %s $(BINBASE)-222.dol 2>/dev/null) + +define ANNOUNCE_SH +CHANGES=`sed -n '/cfg v'$(VERSION)' /,/cfg v/{/cfg v/!p}' README-CFG.txt` +echo "===== updates.txt: =====" +cat << END + +release = $(VERSION) ($(RELEASE)) +size = $(SIZE) +date = $(DATE) +url = http://cfg-loader.googlecode.com/files/$(BINBASE).dol +$$CHANGES + +release = $(VERSION)-222 ($(RELEASE)) +size = $(SIZE_222) +date = $(DATE) +url = http://cfg-loader.googlecode.com/files/$(BINBASE)-222.dol +Same as $(VERSION) but with different +default options: + ios=222-mload + +END +echo "===== forum =====" +cat << END + +[b]cfg v$(VERSION) ($(RELEASE))[/b] +[url="http://cfg-loader.googlecode.com/files/$(BINBASE).dol"]$(BINBASE).dol[/url] +[url="http://cfg-loader.googlecode.com/files/$(BINBASE)-222.dol"]$(BINBASE)-222.dol[/url] +[url="http://cfg-loader.googlecode.com/files/lang$(VERSION).zip"]lang$(VERSION).zip[/url] +(or online update) +[pre]Changes: + +cfg v$(VERSION) ($(RELEASE)) +$$CHANGES +[/pre] + +END +endef +export ANNOUNCE_SH + +announce: + @echo "$$ANNOUNCE_SH" | sh + +DOLS := $(BINBASE)-222.dol $(BINBASE).dol +ELFS := $(BINBASE)-222.elf $(BINBASE).elf +BINS := $(ELFS) $(DOLS) + +upload: + for F in $(DOLS); do echo uploading $$F ; $(UPLOAD) $(GAUTH) -p cfg-loader -s $$F $$F ; done + +upload_elf: + for F in $(ELFS); do echo uploading $$F ; $(UPLOAD) $(GAUTH) -p cfg-loader -s $$F $$F ; done + +upload_lang: + echo uploading Languages/lang$(VERSION).zip + $(UPLOAD) $(GAUTH) -p cfg-loader -s lang$(VERSION).zip Languages/lang$(VERSION).zip + +upload_full: + echo uploading ../Cfg_USB_Loader_$(VERSION).zip + $(UPLOAD) $(GAUTH) -p cfg-loader -s Cfg_USB_Loader_$(VERSION).zip ../Cfg_USB_Loader_$(VERSION).zip + + +install: + cp $(BINBASE).dol $(INSTDIR)/inSDRoot/apps/USBLoader/boot.dol + -rm $(INSTDIR)/*.dol + #cp $(BINBASE)-222.dol $(INSTDIR)/ + cp README-CFG.txt $(INSTDIR)/ + sed -i 's/.*'$(VERSION)' (release).*'`date +%Y%m%d000000`' wiiload usbloader.dol config21.txt +which will load sd:/usb-loader/config21.txt +And you can also pass config options directly: +C:\> wiiload usbloader.dol video=ntsc + +So you can have different configurations depending on where it +is started from. This can be useful so you can configure the +loader started from forwarder to be simple/childproff, while +if started from homebrew channel to include the installation and +removal options. +To achieve this, copy sd:/apps/USBLoader/ to sd:/apps/USBLoader_admin +and then set simple=1 in sd:/apps/USBLoader/config.txt and edit (or delete) +sd:/apps/USBLoader_admin/meta.xml to show a different name. + + +USB FAT for config and covers: +------------------------------ + +SD card is first searched for config.txt, if not found then USB HDD +with a FAT partition is mounted as usb:/ and searched, for config.txt. +in that case, all the paths mentioned above will be searched +on usb:/ instead of sd:/ +Advantage of using USB FAT for config and covers is faster loading of +covers. (Usually USB HDD devices are faster than SD cards) +If you want to load the application from SD card but use USB for config +and covers, then copy everything in inSDroot to your USB FAT partition, +but only the inSDroot/apps directory to SD card and +delete sd:/apps/USBLoader/config.txt and sd:/usb-loader/config.txt. + +Note: To create 2 partitions on SD or USB thumb drives you have to use +special tools like gparted: http://gparted.sourceforge.net/ +Create 2 primary partitions, format both as FAT32 and then inside the loader +re-format the SECOND partition to WBFS. Suggested partitions size if 200MB +for config & covers and the rest for WBFS. + +Note: If you have problems accessing the fat partition make sure it is +marked 'active' in the windows partition editor (or if you use gparted, +mark the partition with the 'boot' flag) + +Limitations: +- USB FAT is mounted only if config.txt is not found on SD. + + +Themes: +------- + +Another way to customize the loader is by using Themes. +A theme is defined by creating a config file in the location: +sd:/usb-loader/themes/THEME_NAME/theme.txt +And the background images: +sd:/usb-loader/themes/THEME_NAME/background.png +sd:/usb-loader/themes/THEME_NAME/background_wide.png + +The options that can be used in theme.txt are described below, +basically only options that define the looks are accepted, everything else +is ignored in theme.txt. Background image (background.png or specified with +background option) for a theme will be searched first in the theme +directory (sd:/usb-loader/themes/THEME_NAME/) and if not found +it will be searched in the base directory (sd:/usb-loader/) + +Other themable gui resources: + favorite.png + pointer.png + hourglass.png + font_uni.png (new type 512 letters unicode) + font.png (old type 128 letters ascii) + font_clock.png + window.png, page.png + button.png, radio.png, checkbox.png +Same search method as for the background image applies to these files. +There are some example files in: usb-loader/resources, but note that the program +does not search in that location, they need to be copied to the base or theme +directory to be used. + +Background overlay support: +additional images can be supplied that will be overlaid over the background: + - bg_overlay.png or bg_overlay_w.png for console background + - bg_gui_over.png or bg_gui_over_w.png for gui background +If *_w.png variant is not found then the normal is used. +If a theme is used then the overlay files are searched only in theme directories, +not in base directories. + +Auto-scaling of background image: +Since version 41 background width can be larger than 640 and will be either + - scaled if widescreen + - cropped if not widescreen +down to 640x480. Note: height must still be 480. + +GUI buttons, icons and windows theme images: +button images: button.png, radio.png, checkbox.png +window images: window.png, page.png +States: button images can contain a single button image +or multiple images for different states: +- normal, flash, hover, press, press hover, inactive +Any number of buttons can be present, if some are missing then +the normal state + coloring and effects are used to generate the missing state. +A button width:height must be 2:1, so that the number of states can be +calculated: num = height * 2 / width +Scaling: a button is divided to 3 areas horizontally: 1/4 1/2 1/4 the first +and last are scale proportionally while the middle is streched as much as required. +Round buttons only use the first and last parts. +Similarily a window image is divided also to 3 areas vertically and same rules apply. +The image width and height must be divisible by 4. + + +GUI Mode: +--------- + +By default console mode is started. +To switch to GUI mode, press B in main screen. +While in GUI mode, the following buttons are used by default (they can be remapped): + button A : start selected game + button B : return to console mode + button LEFT/RIGHT (Grid): previous/next page + button LEFT/RIGHT (Coverflow) : rotate to next cover - hold down for fast rotate. + button UP/Down (Grid) : switch number of rows (1-3) + button UP (Coverflow) : flip center cover to its back/front + button DOWN : change gui styles (grid -> flow -> flow-z -> coverflow 3d -> etc) + button 1, +, - : options, install, remove + button 2: favorites game list + button HOME : exit +The background image in GUI mode can be changed with the file: + sd:/usb-loader/themes/Theme_Name/bg_gui.png or + sd:/usb-loader/bg_gui.png + + +Downloading covers +------------------ + +Covers can be downloaded for all games at once or on a per-game basis. +To download the covers for all games go to Global Options screen, move +to "" and press button RIGHT or LEFT. +There is a difference between LEFT and RIGHT buttons: +- button RIGHT will download all MISSING covers. +- button LEFT will download ALL covers, even the ones that are already present. +To download the covers for the current selected game, go to Game Options screen, +move to "Cover Image:" option and press RIGHT or LEFT. The same difference between +RIGHT and LEFT buttons apply here as with download all covers. +The way how the covers are downloaded can be further customized with config options +download_* and url_*. See the documentation on these options below in the Config file section. + +Parental Control and Unlocking Admin Functionality +-------------------------------------------------- + +If the loader is configured with restricted functionality it can be unlocked +with a password - a "secret" wiimote button combination. Restricted functionality +means any of the options: "simple" or "disable_*" or "hide_game" are set, which is +usually used for "Parental Control". + +When admin mode is unlocked it will allow you to access all the previously +locked functionality. In addition: when unlocked, any games that are hidden +with the hide_game option will be displayed. + +To access the unlock screen, hold the 1 button down for 5 seconds and the screen +will appear. After you see the text "Enter Code:", press the wiimote buttons +in the correct order. If you were successful, the word SUCCESS will appear +on the screen. Otherwise the word LOCKED! will appear. The unlock screen has a 30 +second timeout limit so if an incorrect (or no) password is entered, it will +automatically lock. To set the lock back on with the original settings intact, hold +the 1 button for 5 seconds and the lock will automatically turn on. When the loader +is started, the lock will always be enabled. + +This functionality is controlled by the option: admin_unlock = [1], 0. +By default it is enabled. To disable the ability to unlock admin mode set +the option to: admin_unlock = 0 + +The default admin unlock password is: BUDAH12 +To change it, use the config option: unlock_password = [BUDAH12] +The password length is limited to 10 characters. Do NOT use quotes around the +password - just type what you want it to be. E.g. unlock_password = 12UDAB +The following are the button to letter mappings for the password: + D-Pad Up: U + D-Pad Down: D + D-Pad Right: R + D-Pad Left: L + B button: B + A Button: A + Minus button: M + Plus button: P + Home button: H + 1 button: 1 + 2 button: 2 + +To hide certain games, use the the Game Options screen and toggle the "Hide Game" +setting. This allows you to set which games are hidden when the admin mode is LOCKED. +In order to see this option, admin_unlock must be enabled (which it is by default) AND +the admin mode must be in an unlocked state! +- NOTE: this functionality completely replaces the hide_game option in config.txt, + but they CAN be used together. Any games currently listed in hide_game will be + shown when unlocked, but will ALWAYS be marked as hidden by default in Game Options + and cannot be changed to unhidden unless they are removed from config.txt +- NOTE 2: An easy way to convert all your games in hide_game to this new functionality + is to start the loader with your hide_game still in config.txt and then go into + Game Options in any game (you may have to unlock admin lock first) and change + something and save it. All your hide_game entries will automatically be saved. + Then you can remove the hide_game entry completely from config.txt. + + +Various Controllers Button Mapping: +There are 17 possible buttons, listed here with the controller mappings. +----------------------------------------------------------- +BUTTON WIIMOTE CLASSIC GC GUITAR NUNCHUK +----------------------------------------------------------- +UP UP UP UP STRUM UP +RIGHT RIGHT RIGHT RIGHT NONE +DOWN DOWN DOWN DOWN STRUM DOWN +LEFT LEFT LEFT LEFT NONE +- - - - ++ + + + +A A A A GREEN +B B B B RED +HOME HOME HOME START +1 1 +2 2 +X X X BLUE +Y Y Y YELLOW +Z ZL/ZR Z ORANGE Z +C C +L L L +R R R + +The actions of these buttons can be changed via the button_* options. + +Title Rename +------------ + +To rename a game title, edit the file in sd:/usb-loader/titles.txt +or sd:/usb-loader/custom-titles.txt +Format is: +GAMEID = Game Title +Example: +RSPP = Wii Sports + + + +Config file: +------------ + + +# lines starting with # are comments +# default values are in [ ] +# +# There are 2 categories of options: Global and Theme +# Theme options (theme.txt) only affect the looks, everything else is ignored +# Global options (config.txt) will accept all options, including theme options. +# +# +# Theme options: +# ============== +# +# background = [background.png] +# wbackground = [background_wide.png] +# file to use for the background image. +# file can be an absolute path (like sd:/somedir/myimage.png) +# if the specified background is not an absolute path it is searched +# in theme directory (sd:/usb-loader/themes/THEME_NAME/) +# and in default directory (sd:/usb-loader) +# +# background_base = [background.png] +# wbackground_base = [background_wide.png] +# base background image over which the bg_overlay* files are overlaid. +# actually this option is the same as normal background option +# the only difference is that it is understood by versions that +# support overlaid background, which makes it possible to create +# forward and backward compatible themes +# +# background_gui = [bg_gui.png] +# wbackground_gui = [bg_gui_wide.png] +# gui mode background file. +# +# layout = original2 original small medium large large2 [large3], +# ultimate1 ultimate2 ultimate3 kosaic +# original: 6 lines (wanikoko 1.0-1.1) +# original2: 12 lines (wanikoko 1.2-1.5) +# small: 9 lines (same background as original) +# medium: 17 lines +# large: 21 lines (nixx) +# large2: 21 lines (usptactical) +# large3: 21 lines (oggzee) +# ultimate1: 12 lines (WiiShizza) +# ultimate2: 12 lines (jservs7 / hungyip84) +# ultimate3: 12 lines (WiiShizza - cover on right) +# +# +# covers = [1], 0 +# (enable/disable covers) +# +# console_coords = x,y,width,height +# wconsole_coords = x,y,w,h (widescreen) +# +# console_color = foreground,background +# (color values: 0-15) +# Dark: 0=black, 1=red, 2=green, 3=yellow, 4=blue, 5=purple, 6=cyan, 7=bright grey +# Bright: 8=grey, 9=red, 10=green, 11=yellow, 12=blue, 13=purple, 14=cyan, 15=white +# see: http://www.silverpoint.com/leo/color/c-16.gif (0 at top to 15 at bottom) +# +# covers_coords = x,y +# wcovers_coords = x,y (widescreen) +# +# hide_header = [0],1 +# hide_hddinfo = 0,[1] +# hide_footer = [0],1 +# These options control the look of main menu. +# +# simple = [0], 1 +# Using the simple option in a theme will only affect the hide_footer option +# it will not change the disable_* options. Use the simple option in the +# global config.txt to change the disable_* options as well. +# +# colors = [dark], bright, mono +# will select a prefedines set of colors for a dark or bright background +# or normal 2 color text if mono is specified +# +# color_header, color_selected_fg, color_selected_bg, +# color_inactive, color_footer, color_help +# Will set individual text colors. Values are 0-15 +# +# cover_style = [standard], 3d, disc +# Support for 3d covers and disc covers +# This option selects which cover_url and cover_path is used +# +# console_transparent = [0], 1 +# Enable transparent console. +# +# covers_size = width, height +# default: covers_size = 160, 225 +# If the loaded image is if different size, it will be to the size specified. +# +# wcovers_size = width, height +# default: wcovers_size = 130, 225 +# used for widescreen setting. If not set it will use the covers_size +# and properly scaling it to widescreen size. +# +# cursor = ">>" +# Changes cursor shape, at max 2 characters are used. Can be empty. +# If you want spaces, so that the menu is not shifted use quotes +# like this: cursor = " " +# +# menu_plus = "[+] " +# Changes the [+] mark in the main, options and start menu. +# At max 4 characters are used. +# +# gui_text_color = [black], white, RRGGBBAA +# Set the text color in GUI mode +# Note: to use a color other than a black or white, it has to be +# represented as a HEX value with the following components: RRGGBBAA +# RR=red, GG=green, BB=blue, AA=alpha +# Example: red = FF0000FF, blue = 00FF00FF, yellow = 00FFFFFF +# +# gui_text_outline = [FF], AA, RRGGBBAA, black, white +# gui_text_shadow = [00], AA, RRGGBBAA, black, white +# Outline and shadow color for text in GUI mode +# If only alpha is specified, it will set the outline / shadow +# color to black or white depending if text color is bright or dark. +# +# gui_text2_color = [white], black, RRGGBBAA +# gui_text2_outline = [FF], AA, RRGGBBAA, black, white +# gui_text2_shadow = [00], AA, RRGGBBAA, black, white +# Text colors for coverflow modes, where background is darker +# +# gui_title_top = [0], 1 +# If 1, gui text appears above covers. If 0, below. +# +# coverflow_reflection = color_top, color_bottom +# color is hex rgba, 0,0 will disable reflections +# default: coverflow_reflection = 666666FF, AAAAAA33 +# +# gui_cover_area = x, y, w, h +# default: gui_cover_area = 20, 24, 600, 408 +# minimum accepted w, h: 480, 320 +# enabling debug will draw the area rectangle +# +# gui_title_area = x, y, w, h +# default: 0,0,0,0 meaning, position depends on gui_title_top +# min w, h: 320, 10 note: h is not yet used +# +# gui_clock_pos = x, y +# default: -1,-1 meaning, title position is used +# clock position, if set clock is displayed all the time +# +# gui_page_pos = x, y +# default: -1,-1 meaning, title position is used +# specifies the page indicator position +# +# preview_coords = x,y,width,height +# wpreview_coords = x,y,width,height (widescreen) +# Settings for the Theme Preview image. +# If X and/or Y is set to -1, the Cover X and/or Y position is used. +# Either W or H (or both) should be set to zero to maintain the correct +# aspect ratios. If both are set to zero, the Cover width is used and the +# height is scaled accordingly. +# +# home = [reboot], exit, hbc, screenshot, priiloader, wii_menu, +# , +# What to do when an exit button (typically home) is pressed in the menus +# Also changes the operation of button_H in the gui and game list. +# is a 4 letter magic word for Priiloader +# is a 4 letter channel ID in upper case to launch +# If home=screenshot then home has to be held for 1 second to make a screenshot +# otherwise it will exit to HBC +# +# buttons=[options_1], options_B, original +# (change button controls layout in the gui and game list.) +# +# The button layout "options_1" is: +# button B - GUI mode +# button 1 - options menu +# +# The button layout "options_B" is: +# button B - options menu +# button 1 - GUI mode +# +# The button layout "original" is obsolete, but is: +# button 1 - options menu +# button B - nothing +# +# button_B = [gui], , , +# button_- = [main_menu], , , +# button_+ = [install], , , +# button_H = [reboot], , , +# button_1 = [options], , , +# button_2 = [favorites], , , +# button_X = A, B, 1, [2], H, -, +, , , +# button_Y = A, B, [1], 2, H, -, +, , , +# button_Z = A, [B], 1, 2, H, -, +, , , +# button_C = [A], B, 1, 2, H, -, +, , , +# button_L = A, B, 1, 2, H, [-], +, , , +# button_R = A, B, 1, 2, H, -, [+], , , +# These buttons can be mapped to any of the following actions in the game list and gui: +# nothing # does nothing +# options # access game options +# gui # switch to/from GUI +# reboot # reboot to system menu +# exit # exit to launching app +# hbc # exit to HBC +# screenshot # take a screenshot +# install # install a game +# remove # remove a game +# main_menu # access main menu +# global_ops # access global options menu +# favorites # toggle favorites view +# boot_game # boot a game from the drive +# boot_disc # boot a game from disc +# theme # switch to next theme +# profile # switch to next profile +# unlock # access the unlock password dialog immediately +# sort # switch to next sort +# filter # access filter menu +# priiloader # access Priiloader via magic word Daco +# wii_menu # get Priiloader to launch Wii Menu via magic word Pune +# # is a 4 letter magic word for Priiloader +# # is a 4 letter channel ID in upper case to launch +# X, Y, Z, C, L & R can also be optionally targetted to emulate one of the buttons +# on the Wiimote (A, B, 1, 2, -, +, Home). This emulation will function everywhere. +# +# button_cancel = [B], +# Set which button(s) will act as the back button in menus +# Valid button values in the list are: +# B, 1, 2, -, M, Minus, +, P, Plus, H, Home, X, Y, Z, C, L, R +# +# button_exit = [Home], +# Set which button(s) will perform the 'home' action in menus +# Valid button values in the list are: +# B, 1, 2, -, M, Minus, +, P, Plus, H, Home, X, Y, Z, C, L, R +# +# button_other = [1], +# Set which button(s) will perform the other or alternate action in menus +# This covers switching between options and global options, choosing to +# download BCA during install, choosing to ignore meta.xml during upgrade etc. +# Valid button values in the list are: +# B, 1, 2, -, M, Minus, +, P, Plus, H, Home, X, Y, Z, C, L, R +# +# button_save = [2], +# Set which button(s) will perform the save action in menus +# Valid button values in the list are: +# B, 1, 2, -, M, Minus, +, P, Plus, H, Home, X, Y, Z, C, L, R +# +# gui_window_color_base = RRGGBBAA default: FFFFFF80 +# gui_window_color_popup = RRGGBBAA default: FFFFFFB0 +# +# gui_text_color_menu = COLOR / OUTLINE / SHADOW +# gui_text_color_info = COLOR / OUTLINE / SHADOW +# gui_text_color_title = COLOR / OUTLINE / SHADOW +# gui_text_color_button = COLOR / OUTLINE / SHADOW +# gui_text_color_radio = COLOR / OUTLINE / SHADOW +# gui_text_color_checkbox = COLOR / OUTLINE / SHADOW +# default: +# gui_text_color_menu = white / A0 / 44444444 +# gui_text_color_info = white / A0 +# each component can be "black", "white" or RRGGBBAA +# OUTLINE and SHADOW are optional +# Setting gui_text_color_menu will set all the colors except info +# Setting gui_text_color_button will also set radio and checkbox +# +# gui_button_NAME = X, Y, W, H, TextColor, image.png, Type, HoverZoom +# NAME can be: main settings quit style view sort filter favorites jump +# TextColor is same format as gui_text_color_menu +# (Seting TextColor to 0 will disable text on the button in case icons are used) +# Type: button or icon +# HoverZoom: 0-50 in % +# Paramteres after coordinates are optional. Default values: +# gui_button_NAME = X, Y, W, H, white/A0/44444444, button.png, button, 10 +# +# gui_bar = 0, [1], top, bottom +# Will disable / enable gui bar or enable only top or bottom. +# +# Global options: +# =============== +# +# gui = 0, 1, 2, 3, [4], start +# gui = 0 : GUI disabled +# gui = 1 : GUI enabled, GUI Menu disabled, start in console mode +# gui = 2 : GUI enabled, GUI Menu disabled, start in GUI mode +# gui = 3 : GUI Menu enabled, start in console mode +# gui = 4 : GUI Menu enabled, start in GUI mode +# gui = start : same as gui = 4 +# +# gui_transition = [scroll], fade +# Set GUI transition effect between console and gui mode +# +# gui_style = [grid], flow, flow-z, coverflow3d, coverflow2d, frontrow, vertical, carousel +# Set the default GUI style +# +# gui_rows = [2] (Valid values: 1-4) +# Set the default number of rows in GUI mode +# +# gui_lock = [0], 1 +# Disable changing gui style with button up/down +# +# gui_antialias = [4] {0-4} +# Tune coverflow mode antialias level +# +# gui_pointer_scroll = 0, [1] +# disable/enable pointer scrolling of the game list +# +# theme = Theme_Name +# Load a specified theme from sd:/usb-loader/themes/Theme_Name/theme.txt +# Note: a theme resets all theme related options, so if you want to override +# any theme option, do that in the lines after the 'theme =' option. +# +# covers_path = [sd:/usb-loader/covers] +# Changing covers_path will change all covers_path_* like this: +# covers_path_2d = covers_path/2d +# covers_path_3d = covers_path/3d +# covers_path_disc = covers_path/disc +# covers_path_full = covers_path/full +# If you need fine controll on the 2d/3d/disc/full paths use the +# covers_path_* options after covers_path. +# +# URL options: +# URL can contain any of the following tags which are then replaced +# with proper values: {REGION}, {WIDTH}, {HEIGHT}, {ID3}, {ID4}, {ID6}, {ID}, {CC}, {PUB} +# {ID} will find automatically the correct ID by trying ID6, ID4 and ID3 +# Note: {WIDTH} and {HEIGHT} don't work well if download_all_styles is enabled +# {PUB} os the last two digits of the ID and can be used to get covers from other +# regions. E.g., replace {ID6} with {ID3}P{PUB} to look for PAL covers. +# +# Multiple URLS can be specified for cover_url_* options +# They can be in the same line separated with space, example: +# cover_url = http://site1.com/{ID}.png http://site2.org/{ID}.png +# Or in multiple lines using =+ to add instead of =, example: +# cover_url = http://site1.com/{ID}.png +# cover_url =+ http://site2.org/{ID}.png +# cover_url =+ http://site3.net/{ID}.png +# +# Normal 4:3 mode aspect ratio url options: +# cover_url = URL (url for standard 2d flat covers) +# cover_url_3d = URL (url for 3d covers) +# cover_url_disc = URL (url for disc covers) +# cover_url_full = URL (url for full covers) +# cover_url_hq = URL (url for HQ full covers) +# +# defaults: +# +# cover_url = http://wiitdb.com/wiitdb/artwork/cover/{CC}/{ID6}.png +# +# cover_url_3d = http://wiitdb.com/wiitdb/artwork/cover3D/{CC}/{ID6}.png +# +# cover_url_disc = +# cover_url_disc =+ http://wiitdb.com/wiitdb/artwork/disc/{CC}/{ID6}.png +# cover_url_disc =+ http://wiitdb.com/wiitdb/artwork/disccustom/{CC}/{ID6}.png +# +# cover_url_full = http://wiitdb.com/wiitdb/artwork/coverfull/{CC}/{ID6}.png +# +# cover_url_hq = http://wiitdb.com/wiitdb/artwork/coverfullHQ/{CC}/{ID6}.png +# +# download_id_len = 4, [6] +# Specifies the downloaded cover ID length for the saved file name +# +# download_all_styles = [1], 0 +# Download all cover styles (flat,3d,disc), or just the current style +# +# download_cc_pal = [AUTO], EN, FR, DE, AU, ... +# The code is used as a replacement for {CC} tag for PAL regions. +# If AUTO is specified, then the code is set depending on the console +# country - if Australia: AU, Brasil: PT else, console language is used. +# If image is not found with the specified cc code, download is +# retried with EN code. +# +# titles_url = http://wiitdb.com/titles.txt?LANG={DBL} +# Url for updating titles.txt +# +# device = [ask], usb, sdhc +# Specifies which device to use when starting the loader +# +# partition = [auto], ask, WBFS1, ...WBFS4, FAT1, ...FAT9, NTFS1, ...NTFS9 +# Specifies which partition to use when starting the loader +# Note: the -222 version defaults to FAT1 instead. +# +# simple = [0], 1 +# (enable/disable simple/childsafe mode) +# setting simple=1 will change all disable_xxx options and +# hide_footer to 1 and confirm_start to 0. +# setting simple=0 will do the opposite. +# any of the disable_xxx, hide_xxx and confirm_start options can be set individually. +# +# confirm_start = [1], 0 +# Specifies if confirmation is required when starting a game +# +# disable_remove = [0],1 +# disable_install = [0],1 +# disable_options = [0],1 +# disable_format = [0],1 +# fine permissions control +# +# admin_unlock = [1], 0. +# If enabled it will allow unlock admin mode, so you will be able to access +# all the settings screens that simple or disable_* options disabled. +# +# unlock_password = [BUDAH12] +# Set the password for admin unlock +# +# install_partitions = [only_game], all, 1:1, iso +# controls which partitions from DVD are installed to HDD +# - 1:1 disable scrubbing, except for the last 256kb which are stil scrubbed +# because of a wbfs limitation: the wbfs block size not aligned to wii disc size +# - iso: On NTFS it creates an exact dump to an iso file +# On WBFS/FAT it will behave same as 1:1 +# +# fat_split_size = [4], 2, 0 +# Selects if the split is at 4: 4gb-32kb or 2: 2gb-32kb +# or 0: no splits - ntfs only +# +# fat_install_dir = [1], 0, 2, 3 +# Select install filename layout on fat: +# fat_install_dir = 0 /wbfs/GAMEID.wbfs +# fat_install_dir = 1 /wbfs/GAMEID_TITLE/GAMEID.wbfs +# fat_install_dir = 2 /wbfs/TITLE [GAMEID]/GAMEID.wbfs +# fat_install_dir = 3 /wbfs/TITLE [GAMEID].wbfs +# fs_install_layout is an alias for fat_install_dir +# +# ntfs_write = [0], 1, norecover +# 0 will disable and 1 will enable ntfs write support +# norecover will enable write but prevent mounting an uncleanly unmounted fs +# in case recovery on the PC is preferred. +# +# music = [1], 0, filename, PATH +# Play background music (only one option has to be specified) +# Examples: +# music = 1 +# (which is default) will randomly play all .mp3 or .mod +# files (whichever are found first) in sd:/usb-loader dir +# music = sd:/usb-loader/my_music.mp3 +# Plays my_music.mp3. The file name can be specified +# relative to sd:/usb-loader or an absolute pathname. +# music = sd:/music +# Will randomly play all .mp3 or .mod files in that folder. +# music = 0 +# Will disable music +# +# widescreen = [auto], 0, 1 +# * If widescreen is enabled (or autodetected) then the following options are used: +# - wbackground +# - wbackground_base +# - wbackground_gui +# - wconsole_coords +# - wcovers_coords +# - wcovers_size +# * when in widescreen mode, cover images will be searched first by +# the name GAMEID_wide.png and if not found then by GAMEID.png and +# will be resized properly. +# * some layouts will specify widescreen cooridinates automatically +# like: large3 and ultimate3, so there is no need to specify them manually, +# if one of these layouts are used. +# +# hide_game = [0], GAMEID1, GAMEID2, ... +# Hide games from list (can be used for parental control) +# Multiple games can be specified in one line separated by comma "," +# or each game in a separate hide_game = GAMEID line. +# setting hide_game = 0 will reset the hide list. +# GAMEID is a 4 letter game ID. +# Example: hide_game = RZZP, RDCP +# +# pref_game = [0], GAMEID1, GAMEID2, ... +# Preffered games, to be shown first in the list. +# Syntax is same as with hide_game. +# Example: pref_game = RHAP, RSSP +# +# confirm_ocarina = [0], 1 +# Specifies if confirmation is required when ocarina is enabled +# and codes have been found +# +# cursor_jump = [0] or N +# Sets how much moves left/right (if 0 do a end page / next page jump) +# +# start_favorites = [0], 1 +# Start with the favorites game list +# +# clock_style = [24], 12, 12am, 0 +# Set clock style, 0 will disable clock +# +# sort_ignore = [A,An,The], ... +# Which starting words to ignore when sorting titles +# Separate words via commas, do not incude the square brackets +# Set sort_ignore = 0 for old sort method - to not ignore anything +# +# debug = [0], 1, 8 +# Display some diagnostic messages when started +# Benchmark mode with debug = 8 and start or install a game +# +# debug_gecko = [0],1,2,3 +# write debug info to usb gecko (1=debug,2=console,3=both) +# +# profile_names = [default], name2, name3,... +# Profiles - aka multiple favorite groups +# (max profiles:10, max profile name length: 16) +# Profiles can be added, removed and reordered with this option. +# But if you want to rename it, you will need to change the profile +# name also in settings.cfg otherwise it will be considered as a new +# name and the old one will be forgotten next time you save settings. +# Profiles can be switched in the global options screen. Changing a +# game favorite setting is done as usual in the game options screen. +# +# profile = name +# will specify the default profile to use +# saving global settings will also save which profile is used +# +# profile_start_favorites = {[0], 1} ... +# specifies when each profile is switched to if favorites should be enabled or disabled. +# profile_start_favorites 0 1 1 1 1 1 1 1 1 1 +# +# db_url = [http://wiitdb.com/wiitdb.zip?LANG={DBL}] +# URL to download database from. +# +# db_language = [AUTO], EN, JA, German, etc +# Language to use for the {DBL} tag. If invalid or not able to be +# displayed by the loader this will default to English. +# Both country codes (EN) and languages (English) are valid. +# +# db_show_info = [1], 0 +# Show info loaded from the database. +# +# db_ignore_titles = [0], 1 +# Set this to ignore titles from the database when naming games. +# +# write_playstats = [1], 0 +# Write to the play history file. +# +# sort = [title-asc], etc +# Change the default sorting method. Default is Title Ascending. +# +# Valid sort options: +# "title" => Title +# "players" => Number of Players +# "online_players"=> Number of Online Players +# "publisher" => Publisher +# "developer" => Developer +# "release" => Release Date +# "play_count" => Play Count +# "install" => Install Date +# (This will only work with FAT or NTFS drives) +# "play_date => Last Play Date +# +# To use ascending add "-asc" to the option. +# ie: sort = players-asc +# +# To use descending add "-desc" to the option. +# ie: sort = players-desc +# +# translation = [AUTO], EN, custom, etc. +# Current auto values: JA, EN, DE, FR, ES, IT, NL, ZH, ZH, KO +# Any filename is supported as long as a corresponding translation +# file exists. Translation files currently exist for DE, DK, ES, FI, FR, +# GR, IT, JA, KO, NL, NO, PT_BR, PT_PT, TR, ZH_CN, ZH_CN-clamis, ZH_TW +# +# load_unifont = [0], 1 +# Specifies if unifont.dat should be loaded or not. unifont.dat contains +# all the unicode characters, required for ASIAN language support so that +# translation or wiitdb info shows up correctly. +# load_unifont automaticly switches to 1 if translation starts with JA, KO or ZH +# or if db_language is JA, KO, ZH +# Note: the LATIN unicode set is already embedded into the loader, +# so to display German, French, Spanish, etc... unifont.dat is not needed +# +# wiird = [0], 1, 2 +# 1 = enable debugger +# 2 = enable debugger and pause start +# +# select = [previous], start, middle, end, most, least, random +# Selects which game is picked by default on startup. +# The new default is the previous game played (to get old +# operation, set select=start). +# 'start', 'middle' and 'end' refer to position in the list. +# 'most' and 'least' refer to number of plays (in Cfg). +# 'random' selects a different game each time. +# +# adult_themes = [0], 1 +# determines if adult themes can be downloaded +# +# theme_previews = [1], 0 +# determines if the theme preview images should be downloaded and displayed +# +# return_to_channel = [0], auto, JODI, FDCL, ... +# Games will return to the selected channel ID +# e.g., JODI for HBC or a forwarder channel like FDCL or DCFG +# to reload Cfg. +# 0 is the default Wii Menu operation +# auto: will try to detect the channel id from where the loader was started. +# (although some forwarders are not auto detected properly, +# but the official one by FIX94 is) +# +# disable_nsmb_patch = [0],1 +# disable_pop_patch = [0],1 +# disable_dvd_patch = 0,[1] +# Optionally disable patches performed by the loader. +# +# nsmb = New Super Mario Bros. +# pop = Prince of Persia: The Forgotten Sands +# dvd = disc check patch for Hermes. +# +# PoP requires that you disable the dvd patch +# Set an option to 1 to disable (i.e., not patch) +# +# gamercard_url = URL +# gamercard_key = key +# These options work like the cover_url options +# and support the =+ operator to add multiple sites/keys. +# Keys and URLs are matched up in respective order. +# +# If you set a key in the list to 0, or leave out trailing keys, the +# respective sites from the URLs will not be tried. +# +# The tags {KEY} and {ID6} can be used in the URLs. +# +# Defaults are for WiinnerTag, NCard and DUTag in that order, but with blank keys: +# gamercard_url = http://www.wiinnertag.com/wiinnertag_scripts/update_sign.php?key={KEY}&game_id={ID6} +# gamercard_url =+ http://www.messageboardchampion.com/ncard/API/?cmd=tdbupdate&key={KEY}&game={ID6} +# gamercard_url =+ http://tag.darkumbra.net/{KEY}.update={ID6} +# gamercard_key = 0 0 0 +# +# intro = 0, 1, [2], 3 +# intro=0 : black - only allowed when direct game launching +# intro=1 : black bg with program name (small) +# intro=2 : color image [default] +# intro=3 : grey image +# This option only works if set in meta.xml +# +# nand_emu_path = [usb:/nand] +# sets the path whare a nand image is located. It must be a fat drive. +# +# Game Compatibility Options: +# =========================== +# +# These options can be set in the global config.txt +# The options can be set also per-game from inside the loader +# options screen and will be saved to settings.cfg +# +# video = [auto], game, system, pal50, pal60, ntsc +# (auto is same as game) +# +# language = [console], japanese, english, german, french, spanish, +# italian, dutch, s.chinese, t.chinese, korean +# +# video_patch = [0], 1, all, sneek, sneek+all +# 'video_patch = 1': This will patch NTSC -> PAL modes if console is PAL +# and PAL -> NTSC modes if console is NTSC +# This will not change interlaced / progressive mode +# 'video_patch = all' will force all modes to the selected video mode. +# This will also force progressive / interlaced mode, depending on what +# is configured in wii settings. This can be used for example to force +# progressive mode if the game will otherwise use interlaced mode (MPT) +# +# vidtv = [0], 1 +# Required by some games (Japanese,...) +# +# country_patch = [0], 1 +# Country Patch for better compatibility with some games +# +# fix_002 = [0], 1 +# This is the Anti 002 fix. It is not needed for new cIOSes (IOS249 rev14+) +# +# ios = 247, 248, [249], 222-mload, 223-mload, 224-mload, 222-yal, 223-yal, 250 +# Select Custom IOS +# Note: 222-yal is for kwiirk's cIOS +# Note: 222-mload is for Hermes's cIOS and is the default for the -222 version +# Note: a few seconds of delay when starting a game with custom ios is expected. +# +# block_ios_reload = 0, 1, [auto] +# Required by some games, works with d2x cios and in a limited way for hermes cios +# 0: disabled, 1: enabled, auto: enable if cios is d2x and ver >= 5 +# +# alt_dol = [0], 1, sd, disc +# Alternative .dol loading option (from NeoGamma by WiiPower) +# If set to 1 or sd it will load the .dol from +# the loader base dir + GAMEID.dol (4 letter ID) +# [ sd:/usb-loader/GAMEID.dol ] +# If set to disc it will prompt with a list of .dols on the wbfs iso image +# +# ocarina = [0], 1 +# enable/disable ocarina - cheating engine +# +# hooktype = nohooks, [vbi], wiipad, gcpad, gxdraw, gxflush, ossleep, axframe +# specify ocarina hook type +# +# write_playlog = [0], 1, 2, 3 +# Write gameplay stats to the Wii message board log. +# This option won't work when the Wii Menu is skipped before +# starting Cfg (e.g., using Priiloader or BootMii autoboot). +# 0 = off, do not write to the Wii Message Board log. +# 1 = on, set title based on the value of the language option unless +# the title in that language is blank where it then uses the +# English title or the Japanese title (whichever is valid). +# 2 = on, set title to the Japanese title. +# 3 = on, set title to the English title. +# In the per-game menu, these options show up as "Off", "On", +# "Japanese Title" and "English Title" respectively. +# +# clear_patches = [0],1,all +# When on (1) return_to_channel and the dvd check are disabled +# When 'all', then all .dol patches are disabled + +Config file sample: +------------------- + +# USBLoader config +background = background.png +layout = large3 +covers = 1 +hide_footer = 1 +# device = usb + + +Sample titles.txt file: +----------------------- + +RFNP = Wii Fit +RHAP = Wii Play +RSPP = Wii Sports +RMGP = Super Mario Galaxy + + + +Changelog: +---------- + +07-07-2011 cfg v70 (release) + * Version + * Full package changes: + - New default theme: Glass (by The-Magician) + +05-07-2011 cfg v70b6 (beta) + * Updated to rodries ehcmodule for hermes cios (222,223,224) + +03-07-2011 cfg v70b5 (beta) + * Fixed occasional crashes in GUI game select + especially when using WBFS partition + * Minor cleanups + +02-07-2011 cfg v70b4 (beta) + * Print game requested IOS in game info screen + (after the GAMEID and size) (tnx R2-D2199) + * Minor cleanups + +02-07-2011 cfg v70b3 (beta) + * Support for hdd with 4k sector size on fat/ntfs/wbfs filesystems + Thanks to: dimok (libs) davebaol (cios) and dexter (testing) + +28-06-2011 cfg v70b2 (beta) + * Fixed GUI game options when clear patches is set to all + * Improved exception stack dump output to show also the debug log and version + * Improved gui theme switch to reflect text and button color changes immediately + +26-06-2011 cfg v70b (beta) + * Updated devkitPPC to 23 + * Updated libogc to 1.8.7 + * When clear patches is set to all really disable all game patches, + previously some could still be enabled (anti 002 and some others) + * Fixed GUI Menu display of game options hook type when ocarina is enabled + +25-06-2011 cfg v70a (alpha) + * updated libraries for improved support of hdd with 4k sectors: (tnx dimok) + libfat r4700, libntfs r10 (ntfs-3g 2011.4.12), libext2fs r15 (v1.0.2) + +15-06-2011 cfg v69d (bugfix) + * Fixed wbfs on hdd with 4k sectors (broken by v69a6) + (And disabled fat & ntfs on hdd with 4k sec. since it doesn't work) + * Updated "about" credits and translators + +12-06-2011 cfg v69c (bugfix) + * Fixed crash when return_to_channel is set to an invalid value + (non-existing channel id) + +09-06-2011 cfg v69 (release) + * Updated about window with credits & translators list + * Full package changes: + - New default theme: Blue 2011 (by The-Magician) + - Added tetris.mp3 + +07-06-2011 cfg v69b3 (beta) + * changed option: block_ios_reload = 0, 1, [auto] + 'auto' will enable block ios reload if cios is d2x and ver >= 5 + 'auto' is now the default + * Removed detection of hermes ios installed with pimp my wii + since the hashes were incorrect. + +06-06-2011 cfg v69b2 (beta) + * Use modmii cios identification for d2x version (by R2-D2199) + * Detection of hermes cios v5.1 installed with pimp my wii (by xFede) + +04-06-2011 cfg v69b1 (beta) + * Support for ModMii cios identification (thanks XFlak, WiiPower) + * Support for Korean games (by damysteryman) + +03-06-2011 cfg v69a7 (alpha) + * revert to ntfs-3g-2010.10.2 (libntfs-wii r4+r7) + * Improved support for hdd with 4k sectors + +03-06-2011 cfg v69a6 (alpha) + * Improved support for hdd with 4k sectors + +02-06-2011 cfg v69a5 (alpha) + * Support for d2x v6 IOS reload block on FAT/NTFS/EXT2 fs. + +02-06-2011 cfg v69a4 (alpha) + * Fixed #001 error on hermes cios for fakesigned games (caused by + Sam&Max fix which is now disabled on hermes cios, Thanks Wiimm) + * Fixed We Dare (thanks airline38 and WiiPower) + * Minor cleanups + +28-05-2011 cfg v69a3 (alpha) + * Really fixed d2x v5+ ios reload block on wbfs + +28-05-2011 cfg v69a2 (alpha) + * Fixed d2x v5+ ios reload block on wbfs (tnx WiiPower & davebaol) + (Now Sam&Max fix is always enabled) + +28-05-2011 cfg v69a (alpha) + * detection of cios d2x v4 (tnx kamiro04 & FIX94) + * d2x v5+ ios reload block on wbfs (tnx WiiPower & davebaol) + * d2x v4+ return to channel method (tnx WiiPower & davebaol) + * added value: return_to_channel = auto + will try to detect the channel id from where the loader was started. + (although some forwarders are not auto detected properly, + but the official one by FIX94 is) + +28-04-2011 cfg v68d (bugfix) + * Fixed wrong banner sound playing (or hang or crash) + when using WBFS and the new GUI menu. + * Fixed missing text when a translation was missing + (Now english is used if a translation is missing) + +27-04-2011 cfg v68c (bugfix) + * Fixed occasional corrupted cover in coverflow modes + +24-04-2011 cfg v68 (release) + * Fixed "press any button" after cover download + * Fixed gamercard option in gui menu + +22-04-2011 cfg v68b4 (beta) + * Set custom buttons inactive when any window is opened + * New theme options: + gui_text_color_title + gui_text_color_button + gui_text_color_radio + gui_text_color_checkbox + Setting gui_text_color_menu will set the base gui menu color + and all of the above color options too. + Setting gui_text_color_button will also set radio and checkbox + default value is same as gui_text_color_menu + +21-04-2011 cfg v68b3 (beta) + * Detection of cios d2x v4 beta 2 + * If pointer is outside a defined gui_cover_area then don't scroll + * Fixed: wrong size of 2d&3d no cover image in gui menu game select + * Fixed: admin unlock in gui mode to show full game list + * Fixed: missing gui menu custom buttons after switching from console + * Cleanups + +19-04-2011 cfg v68b2 (beta) + * New theme option: + gui_button_NAME = X, Y, W, H, TextColor, image.png, Type, HoverZoom + NAME can be: main settings quit style view sort filter favorites + TextColor is same format as gui_text_color_menu + (Seting TextColor to 0 will disable text on the button in case icons are used) + Type: button or icon + HoverZoom: 0-50 in % + Paramteres after coordinates are optional. Default values: + gui_button_NAME = X, Y, W, H, white/A0/44444444, button.png, button, 10 + * New theme option: + gui_bar = 0, [1], top, bottom + Will disable / enable gui bar or enable only top or bottom. + * Minor fixes to gui admin unlock & left/right dpad with coverflow + * Changed default gui menu color to white/A0/44444444 + * Removed [FRAG] note at startup + +17-04-2011 cfg v68b (beta) + * Translation fixes + * Themable gui menu images: + buttons: button.png checkbox.png radio.png + windows: window.png page.png + Images can also be placed in usb-loader base dir + and if a theme doesn't provide it's own button.png + then images from the base dir are used. + * New theme options: + gui_window_color_base = RRGGBBAA default: FFFFFF80 + gui_window_color_popup = RRGGBBAA default: FFFFFFB0 + +13-04-2011 cfg v68a4 (alpha) + * Fixed Translation: Cover~~Front Cover~~Back + * Fixed switch from gui to console mode + * New themed options: + gui_text_color_menu = COLOR / OUTLINE / SHADOW + gui_text_color_info = COLOR / OUTLINE / SHADOW + default: + gui_text_color_menu = 6688FFFF / A0 / 44444444 + gui_text_color_info = white / A0 + each component can be "black", "white" or RRGGBBAA + OUTLINE and SHADOW are optional + * Bigger Start button + * Minor GUI menu updates + +11-04-2011 cfg v68a3 (alpha) + * Translation updates + New strings: Cover~~Front Cover~~Back Fav: Off Fav: On + * Changed gui option: + gui = 0, 1, 2, 3, [4], start + gui = 0 : GUI disabled + gui = 1 : GUI enabled, GUI Menu disabled, start in console mode + gui = 2 : GUI enabled, GUI Menu disabled, start in GUI mode + gui = 3 : GUI Menu enabled, start in console mode + gui = 4 : GUI Menu enabled, start in GUI mode + gui = start : same as gui = 4 + * Game dialog button changes: + Pressing button A on cover will start the game + To rotate the cover hold button 1 + To zoom use buttons +/- + buttons UP/DOWN: change cover style + buttons LEFT/RIGHT: prev/next game + * Cover cache fixes + +07-04-2011 cfg v68a2 (alpha) + * Skip game start confirmation when started from gui menu. + * When a game is selected and the wiimote is not pointing + to the screen move the pointer to the start button so + that pressing button A will start the game directly. + * Always start with cover view in game dialog + (previously it remembered the last state) + * Changed gui option: + gui = 0, 1, 2, [3], start + gui = 0 : GUI disabled + gui = 1 : GUI enabled but starts in console mode + gui = 2 : GUI enabled and starts in GUI mode + gui = 3 : GUI and GUI Menu enabled. Starts in GUI mode + gui = start : same as gui = 3 + * Translatable GUI menu + +06-04-2011 cfg v68a (alpha) + * GUI menu + +05-04-2011 cfg v67c (bugfix) + * Fixed blurred coverflow in 50 Hz video mode + * Fixed detection of missing covers in game options screen + +04-04-2011 cfg v67 (release) + * Fixed crash with intro=1 (Issue 123) + Possibly also fixes problems with game forwarder channels + * Other minor fixes and cleanups + +03-04-2011 cfg v67b (beta) + * Support for HQ covers (from Wiiflow, thanks to Hibern) + * new option: cover_url_hq + default: http://wiitdb.com/wiitdb/artwork/coverfullHQ/{CC}/{ID6}.png + * File cache of compressed Full and HQ covers for faster loading + (Saved to covers_path/cache) + +22-03-2011 cfg v67a (alpha) + * Improved cover cache - no more cover reloading + after a theme or style change or cover download + * Minor improvement in coverflow cover rendering + * Minor adjustment of stick movement (nunchuck or classic) + * Fixed slow movement in console mode game list + when games are NTFS and hdd space info is enabled + +15-03-2011 cfg v66c (bugfix) + * Improved detection of cios d2x v3 (r21003) (thanks kamiro04) + +13-03-2011 cfg v66 (release) + * Version + +11-03-2011 cfg v66b (beta) + * Added detection of cios d2x v2,v3 (thanks kamiro04) + +11-03-2011 cfg v66a (alpha) + * Added detection of cios r21-d2x-v1 (thanks kamiro04) + * Updated libfat to 1.0.9 + * ntfs-3g 2011.1.15 / libntfs-wii r7 (thanks Dimok) + * new option: gui_pointer_scroll = 0, [1] + disable/enable pointer scrolling of the game list + * cleanups + +25-01-2011 cfg v65 (release) + * version + +25-01-2011 cfg v65b8 (beta) + * cios base detection for r17b + +25-01-2011 cfg v65b7 (beta) + * Fixed gui theme specified font_clock.png + * cios base detection for r17 + +24-01-2011 cfg v65b6 (beta) + * Fixed: sort = install-desc in config.txt + * cios detection for base 57 r21+r19 modmii + * Improved detection of hybrid modmii cios for non-249 slot + +23-01-2011 cfg v65b5 (beta) + * Fix modmii cios detection (again) + +23-01-2011 cfg v65b4 (beta) + * Fix modmii cios detection + +23-01-2011 cfg v65b3 (beta) + * Detection for modmii hermes cios v4, v5 (thanks FIX94) + * Fixes for ios base detection + +23-01-2011 cfg v65b2 (beta) + * Detection for modmii ciosx rev19, 20, 21+19 + and hermes v4, v5, v5.1 (thanks FIX94) + * Added "Show cIOS info" to global options menu + +22-01-2011 cfg v65b (beta) + * Added detection for modmii ciosx rev21 (thanks FIX94) + * Print cios base and rev for slots 245-250 + in global options if button + is pressed + and to saved debug.log + * Word wrap game title in the game start confirmation screen + * Minor cleanups + +19-01-2011 cfg v65a (alpha) + * Updated libfat to svn-4520 which includes FSINFO (by Dimok) + FAT fsinfo stores the free space info to a designated sector + speeding up the time it takes to print the free space. + Previously the entire FAT table had to be scanned to get the + free space which could take up to a couple of minutes. + This was also the reason the option hide_hddinfo=1 is set + so that by default the free space is not displayed . + * Added a way to manually scan and sync the free space in fsinfo. + Go to global options / partition selection and press 2 + Then all FAT filesystems will be shown and the free space for each + By confirming with A the free space will be scanned again. If the + scanned free space mathces fsinfo OK is displayed otherwise the + correct free space, which is then stored to fsinfo. + It is enough to do this once, after that the fsinfo should be kept + in sync. (but using some other homebrew to write/delete data from + FAT will make the info unsynced again, until all other apps are upgraded + with the new libfat as well) + * Minor change to usbstorage mem allocation (back to v63) + (in case it fixes problems with v64 that were not in v63) + +17-01-2011 cfg v64 (release) + * Minor cosmetic fix in http error reporting + +16-01-2011 cfg v64b6 (beta) + * Fixed sort by install date + +15-01-2011 cfg v64b5 (beta) + * Improved USB timeout handling also when searching for config.txt on USB + +15-01-2011 cfg v64b4 (beta) + * Fixed BCA with hermes cios v5.1 + * Cleanups + +14-01-2011 cfg v64b3 (beta) + * Fixed theme download + * Fixed wiitdb game info + * Split (required) and [optional] controllers + +13-01-2011 cfg v64b2 (beta) + * Fixed hang in v64b when checking for updates + +13-01-2011 cfg v64b (beta) + * Fixed crash when downloading large cheat codes (Issue 100) + Possibly fixes also the occasionally corrupted downloaded cover images + * Added USBStorage_Deinit() to shutdown + * Turn off wiimotes after they are idle for 5 minutes + * Display supported controllers in wiitdb game info + * Cleanups + +12-01-2011 cfg v64a (alpha) + * Support for ID4 wiitdb entries (wiiware) + * Fixed unnecessary re-downloading of HQ full covers when they already exist + * Better HTTP error reporting when downloading cover fails + +10-01-2011 cfg v63 (release) + * Minor cleanups + * Full package changes: covers/2d, Languages -> languages + +09-01-2011 cfg v63b6 (beta) + * Fixed crash when missing wiitdb.zip + * Translatable page indicator for wiitdb game info + +09-01-2011 cfg v63b5 (beta) + * Fixed coverflow slowdowns when using wiitdb + * Improved switching between games in confirmation screen + (disabled background mp3 while switching because of banner sounds) + * Removed obsolete options cover_url_*_norm + * More info in debug.log (mem, time) + * Minor cleanups + +08-01-2011 cfg v63b4 (beta) + * Scrollable wiitdb game description (buttons UP/DOWN) + * Switch to prev/next game in the confirmation screen (buttons LEFT/RIGHT) + * Added DUTag to gamercard_url: http://tag.darkumbra.net/{KEY}.update={ID6} + * Fixed partition=ask + +04-01-2011 cfg v63b3 (beta) + * Changed covers_path_2d default value to /usb-loader/covers/2d + instead of /usb-loader/covers however both locations are still + looked up so existing setups should not be affected. Downloading + 2d covers will save to /usb-loader/covers/2d only if it exists, + otherwise /usb-loader/covers is used - same as before. + If the option covers_path_2d is set manually in config.txt then + it works same as before, the behaviour isn't changed. + * Optimized sort by install date + * Wiitdb optimisations + * Added uDraw to filter by controller + * Fixed WBFS partition on a 4k sector size drive + * Minor cleanups + +31-12-2010 cfg v63b2 (beta) + * Fixed device init timeout handling + +31-12-2010 cfg v63b (beta) + * Improved device init timeout handling: + If the device doesn't respond in 3 seconds one can + try reloading IOS or exiting to HBC or sys menu + timeout has been increased from 30 to 90 seconds + * Minor cleanups + +29-12-2010 cfg v63a3 (alpha) + * Fixed using of ios slots 245 and 246 + * Fixed hang when game fails to load (at "Press any button to exit") + +28-12-2010 cfg v63a2 (alpha) + * Updated dip+frag plugin for ciosx r21 + * Added ciosx r21 base detection (Thanks FIX94) + +28-12-2010 cfg v63a (alpha) + * Added 2 more slots for waninkoko cios: 245, 246 + * Added saving of debug.log and ioshash.txt from global options screen + * Fixed broken title in coverflow mode when no games are installed + +22-12-2010 cfg v62 (release) + * Updated libogc to 1.8.6 + +21-12-2010 cfg v62b3 (beta) + * More translatable strings (languages, playlog) + * Fixed: display of 3d cover when selecting a game in coverflow mode + when a 2d and full cover are missing + * Minor cleanups + +19-12-2010 cfg v62b2 (beta) + * Fixed gui_clock_pos alignment + * Fixed title in grid mode when no game is selected + * gui style change notification is now printed in title area + the same way as button actions (profile change, ...) + * Added option: gui_title_area = x, y, w, h + default: 0,0,0,0 meaning, position depends on gui_title_top + min w, h: 320, 10 note: h is not yet used + * renamed option gui_pager_pos to gui_page_pos + +19-12-2010 cfg v62b (beta) + * fixed partition=auto for wbfs + * new themable options: + - gui_clock_pos = x, y + default: -1,-1 meaning, title position is used + if set clock is displayed all the time + - gui_pager_pos = x, y + default: -1,-1 meaning, title position is used + * Translatable (Jabe & cambric request): + - button names + - partition types and header + - video and language options + - cover styles + +18-12-2010 cfg v62a5 (alpha) + * Fixed partition = auto + * Better formatting of wiitdb info + +18-12-2010 cfg v62a4 (alpha) + * fixed ext2fs support + * fixed error message about multiple wbfs partitions + when using a second fat or ntfs or ext2fs part. + +18-12-2010 cfg v62a3 (alpha) + * fixed partition selection and crash from 62a2 + +17-12-2010 cfg v62a2 (alpha) + * ext2fs support (Thanks to Dimok!) + * new theme option: coverflow_reflection = color_top, color_bottom + color is hex rgba, 0,0 will disable reflections + default: coverflow_reflection = 666666FF, AAAAAA33 + * new theme option: gui_cover_area = x, y, w, h + default: gui_cover_area = 20, 24, 600, 408 + minimum accepted w, h: 480, 320 + enabling debug will draw the area rectangle + * Improved coverflow to console transition + * Updated libntfs (sync with wiiflow) + * Updated grrlib: 4.0.0 -> 4.3.1 + * Updated intro 4 (smaller) + +07-12-2010 cfg v62a (alpha) + * Updated libs: + - libfat 1.0.5 -> 1.0.7 + - jpeg 8a -> 8b + - png 1.2.34 -> 1.4.4 + - zlib 1.2.4 -> 1.2.5 + * New default intro=4 : stripes themed (by abdias) + +04-12-2010 cfg v61 (release) + * In case a CODE DUMP happens the wii will reset in 60 seconds + instead of waiting on the code dump screen + * full package changes: + - added a new default theme: stripes by abdias + - removed themes: BlueMatrix, NXE, cfg_* + - removed noimage_wide which is not used anymore + - removed cfg61-222.dol (available as a separate download) + In addition to cfg61-222.dol (with default ios 222-mload) + there's now also: + cfg61-compat.dol built with the old devkit 17+ogc 1.7.1 + cfg61-dbg.dol with debugging enabled by default + +02-12-2010 cfg v61b9 (beta) + * fixed startup with multiple wiimotes + (libogc svn-4463 thanks to tueidj and tantric) + * fixed hourglass image in coverflow mode + +27-11-2010 cfg v61b8 (beta) + * Allow to disable splitting of installed games on NTFS with fat_split_size = 0 + * If home=screenshot, don't disable it after screenshot is made in main menu + +27-11-2010 cfg v61b7 (beta) + * Fixed RAW fs / part.table detection + +25-11-2010 cfg v61b6 (beta) + * back to libogc 1.8.5 with CODBO fix (USB deinit, thanks to tueidj) + +21-11-2010 cfg v61b5 (beta) + * Fixed theme switching (Issue 69) + * Increased max themes from 100 to 300 (Issue 60) + * updated game count when downloading covers to start from 1 (Issue 92) + * Removed boxart.rowdyruff.net from the list of cover urls + +21-11-2010 cfg v61b4 (beta) + * Use libogc 1.8.3 to fix COD:BO + * Enable pointer control with any wiimote (but only one at a time - + the one with the lowest number that points to screen is used) + * more improvements to raw fs detection: in case the partition table is + ambiguous - if it appears there is a raw fs and a valid part. table then + make a decision based on device type: for sd assume raw, for usb assume p. table + * Updated mp3 player (triple buffering from libogc) + +19-11-2010 cfg v61b3 (beta) + * Fixed .mod playing + +18-11-2010 cfg v61t3 (debug test) + * print more debug info, debug enabled by default + * if home=screenshot then make a screenshot if home is being held for 1 second + otherwise exit to hbc. So a short press on home will exit while holding home + for 1 second will make a screenshot + * possible fix for corrupted console text + +cfg v61t2 (test) + * init usb immediately after ios reload + +cfg v61t1 (test) + * libfat and libntfs build with devkit 17 and -Os + +31-10-2010 cfg v61b2 (beta) + * Updated libogc to 1.8.5 + * Fixed wiitdb synopsis for non-EN locale + * Print wiitdb download url when updating + * Reenabled loading a config file specified by args + +30-10-2010 cfg v61b (beta) + * Slightly faster startup time (by about 1-2 seconds) + (optimized loading of config and wiitdb) + * Selectable intro: + intro=0 : black - only allowed when direct game launching + intro=1 : black bg with program name (small) + intro=2 : color image [default] + intro=3 : grey image + This option only works if set in meta.xml + * Improved NTFS related error messages: + - when starting games from NTFS compressed or encrypted files + - when trying to install game or covers on ntfs with write disabled + +27-10-2010 cfg v61a3 (alpha) + * Upgraded devkitppc 17 to 22 (again) + * Fixed devkitppc 22 and net related crashes (hopefully) + * URL options will now accept also numeric IP address + +23-10-2010 cfg v61a2 (alpha) + * Reverted devkitppc from 22 back to 17 (libogc is still 1.8.4) + This seems to fix the wiitdb and net related crashes in v61a + * fixed: fat_install_dir = 3 + * better compatibility with some forwarders + (ignore drive number (usb1:) in argv[0]) + +19-10-2010 cfg v61a (alpha) + * Upgraded dev tools devkitppc 17 to 22 and libogc 1.7.1 to 1.8.4 + * Improved partition check for raw fs (v60t1 fix) + * cios 222 shadow mload proper version (5.1) check + * debug stuff: + pressing + in global options screen will report: + - devkitppc and libogc version + - mem stats + - startup timings + option debug=16 will report game launch timings + +12-09-2010 cfg v60 (release) + * Fixed install on ntfs to .wbfs file type + +12-09-2010 cfg v60b6 (beta) + * Fixed and improved support for wbfs/fat/ntfs on RAW device + +11-09-2010 cfg v60b5 (beta) + * Fixed: partition = auto + partition = auto is now the default value + for both normal .dol and -222.dol + +10-09-2010 cfg v60b4 (beta) + * Fixed SSBB+ SD card + * New option value: partition = auto + Will select first valid from: WBFS1, FAT1, NTFS1 + FAT or NTFS partition will only be valid if the /wbfs folder exists + +10-09-2010 cfg v60b3 (beta) + * Fixed install to ISO on NTFS + +09-09-2010 cfg v60b2 (beta) + * New option value: install_partitions = iso + On NTFS it creates an exact dump to an iso file + On WBFS/FAT it will behave same as 1:1 + +08-09-2010 cfg v60b (beta) + * Changed FS mountpoint handling: + USB drive: 'usb:' is first FAT 'ntfs:' is first NTFS partition + SD/SDHC card: 'sd:' is first FAT or NTFS partition if FAT is not found + game: is a temporary mount for games on FAT or NTFS and will be any partition + that is selected but is not already mounted as one of the above + config.txt is now searched also on ntfs:/usb-loader/config.txt + in addition to sd: and usb: + +02-09-2010 cfg v60a (alpha) + * NTFS write support + new option: ntfs_write = [0], 1, norecover + norecover will prevent mounting an uncleanly unmounted fs. + (thanks for the updated libntfs go to Dimok and Miigotu) + +01-09-2010 cfg v59 (release) + * version change + +31-08-2010 cfg v59b2 (beta) + * dip+frag plugin for cios 249 updated to rev20 + * Changed the default value of: disable_dvd_patch = 1 + Since now the dvd slot check is handled properly by + all cios the patch is disabled by default + * Enable shadow mload on hermes cios v5.1 + +30-08-2010 cfg v59b (beta) + * Loading games from SDHC for hermes cIOS + * Update modifies existing meta.xml instead of replacing it (gannon) + (So that any additional parameters or user edits will be retained, + only version and date are updated) + * Changed directory creation code to avoid errors on an incomplete path (Clipper) + +27-08-2010 cfg v59a (alpha) + * Improved support for Hermes cIOS v5.x + Updated ehcmodule to v5 and new odip plugin + Moved frag code from ehcmodule to odip + Removed support for Hermes cIOS 1,2,3 and external modules + (cios v4 is still supported) + Games on SD/SDHC don't work with hermes cios for the moment. + +26-08-2010 cfg v58 (release) + * preliminary cios rev21 support + +25-08-2010 cfg v58b2 (beta2) + * Gamercard support (Clipper, Daileon) + * New options: gamercard_url and gamercard_key + - These options work like the cover_url options + and support the =+ operator to add multiple sites/keys + - Keys and URLs are matched up in order + - If you set a key in the list to 0, or leave out trailing keys, the + respective sites from the URLs will not be tried. + - The tags {KEY} and {ID6} can be used in the URLs. + - Defaults are for WiinnerTag and NCard in that order, but with blank keys: + gamercard_url = http://www.wiinnertag.com/wiinnertag_scripts/update_sign.php?key={KEY}&game_id={ID6} + gamercard_url =+ http://www.messageboardchampion.com/ncard/API/?cmd=tdbupdate&key={KEY}&game={ID6} + gamercard_key = + +22-08-2010 cfg v58b (beta) + * Added wiird setting to global options screen. + (If the gecko is not connected the option is inactive) + * Improved IOS base detection (from NeoGamma - by WiiPower) + * New option: debug_gecko = [0],1,2,3 + write debug info to usb gecko + +01-08-2010 cfg v58a (alpha) + * Support multiple slots for Waninkoko's cios rev20: 247, 248, 249, 250 + Changed option: ios = 247, 248, [249], 222-mload, 223-mload, + 224-mload, 222-yal, 223-yal, 250 + +31-07-2010 cfg v57 (release) + * Added video patching mode from Sneek (by Crediar) + changed option: video_patch = [0], 1, all, sneek, sneek+all + +30-07-2010 cfg v57b9 (beta9) + * Added ID of 1.07 HBC to launch functions. (Clipper) + * Allowed hex values for options that can specify a channel ID + so the new HBC is supported. Value for HBC 1.07 is AF1BF516. (Clipper) + * Scrollable list of updates (oggzee) + +24-07-2010 cfg v57b8 (beta8) + * Added language selection to write_playlog (Clipper) + * Improved playlog title detection: if the title for selected game language is empty, + and playlog is enabled (but not set to a specific language) then try with english + or the first language for which a title exists. (oggzee) + * Fullscreen theme preview on button 1 (oggzee) + * Download of theme preview can be cancelled by holding B (oggzee) + * Moved theme preview images from themes/*.jpg to theme_previews/*.jpg (oggzee) + * Optimizations of coverflow cover selection (usptactical) + (realtime and compressed stencil buffer) + +13-06-2010 cfg v57b7 (beta7) + * Initial jpeglib support added (usptactical) + * Added jpeg error handling: bad jpegs will be renamed + to filename.bad (usptactical) + * Theme Preview images (usptactical) + option: theme_previews = [1], 0 + Determines if preview images can be downloaded and + displayed. + option: preview_coords = x,y,width,height + option: wpreview_coords = x,y,width,height + Set x/y to -1 to use Cover x/y + Set width/height to 0 to use Cover height/width + * Using select = random option in coverflow scrolls + covers to next game (usptactical) + * Bug fix for clicking on titles in theme download + menu. (Clipper) + +08-06-2010 cfg v57b6 (beta6) + * new option: select = [previous], start, middle, end, + most, least, random + Selects which game is picked by default on startup. + The new default is the previous game played (to get old + operation, set select=start). + 'start', 'middle' and 'end' refer to position in the list. + 'most' and 'least' refer to number of plays (in Cfg). + 'random' selects a different game each time. + * new button action: random + Selects a different game at random from the current list + To use, assign to a button, like button_Z=random + * Title/alphabetical sorting made case insensitive (oggzee) + * clear_patches save bug fixed + * cheat bug fixed + +25-05-2010 cfg v57b5 (beta5) + * .wip patches now working + * fix for .txt cheat detection (some cheats were incorrectly + identified as editable) + * New per-game option: + clear_patches = [0],1,all + When on (1) return_to_channel and the dvd check are disabled + When 'all', then all .dol patches are disabled + +20-05-2010 cfg v57b4 (beta4) + * Patch for Prince of Persia + * new option: disable_pop_patch = [0], 1 + * new option: disable_dvd_patch = [0], 1 + +20-05-2010 cfg v57b3 (beta3) + * Fix for big theme downloading + +16-05-2010 cfg v57b2 (beta2) + * Theme downloading (via global options) + * Themes provided by http://wii.spiffy360.com + * new option: adult_themes = [0], 1 + Adult themes only shown for download if switched on + +27-04-2010 cfg v57b (beta) + * Added warnings for stubbed IOSes (gannon, Wiipower) + * Changed warning for IOS249 ZH_CN.lang (Simplified Chinese) + CHT.lang -> ZH_TW.lang (Traditional Chinese) + +22-02-2010 cfg v53b3 (beta3) + + * Fixed crash with cios222 v5 + (happened with this combination: normal cfg.dol + with options: ios=222-mload & partition=wbfs) + * Fixed update progress ... notification + +21-02-2010 cfg v53b2 (beta2) + + * More fixes for handling of corrupted cover images (usptactical) + * Minor translation updates + +20-02-2010 cfg v53b (beta) + + * Better handling of corrupted cover images - they should not crash + the loader anymore and will be renamed to filename.bad (usptactical) + * Japanese / Chinese translation and wiitdb support (oggzee) + A new font file is required for this: unifont.dat + * Removed ISFS from playlog (Clipper) + * Scroll option screens if the console size is too small (Clipper) + * Removed wiiboxart from URLs. (Clipper) + * print cover download url and progress (oggzee) + * force fat freespace update when installing (oggzee) + +16-02-2010 cfg v53a (alpha) + + * cIOS 222/223/224 v5 support + Note: only use 222 for loader, 223 and 224 will freeze if used for loader, + however 223/224 work fine for games. That means, don't put ios=223-mload in + config.txt, but it's ok if it is set for a specific game in options screen. + * New option value for ios: 224-mload + * Support for HDDs with 4k sectors (WBFS partition only) + * Fixed option: home=hbc + +13-02-2010 cfg v52 (release) + + * Left/Right hold repeat in console + * GUI displays messages for sort, profile and theme switch. + * Minor cleanups + +12-02-2010 cfg v52b5 (beta5) + + * Fixed handling of multiline strings in .lang files + * Minor translation updates + +11-02-2010 cfg v52b4 (beta4) + + * New actions for buttons: sort (switch sort type), filter (filter menu) (Clipper) + * Button actions sort, profile and theme will display a message in the console (Clipper) + * Holding any of the buttons in button_other in the GUI will work for menu_unlock (Clipper) + * Fixed: title length 3 from folder names + * Fixed: WiiTDB update crash + * Handle & etc. in wiitdb titles + * fat_install_dir = 3 will use layout: /wbfs/Title [ID].wbfs + * new option: fs_install_layout is an alias for fat_install_dir + * Minor cleanups + + +09-02-2010 cfg v52b3 (beta3) + * Button remapping options (Dr. Clipper) + See below for information. + * Previous home option is now a theme option with overrides + * Reversion of boot disc to cIOS method (for real this time) + * Fix for switching between NTFS partitions (oggzee) + * Various translation and menu alignment fixes (oggzee) + * Support for new filenames on FAT/NTFS: (oggzee) + /wbfs/TITLE [GAMEID].wbfs or /wbfs/TITLE [GAMEID].iso + * option: db_ignore_titles = [0], 1 + Set this option to ignore titles from the database + + About button remapping: + Firstly, the guitar default mappings have changed slightly. + The new mappings are as follows: + RED = A; GREEN = B; YELLOW = X; BLUE = Y; ORANGE = Z. + + Each of the following buttons can now have its own action: + B, -, +, 1, 2, Home, X, Y, Z, C, L & R. + + These actions are valid for the console game list and the GUI + mode only. For options that affect the menus, see below. + The new options for this type of mapping are all theme options + with config.txt overrides and are as follows: + option: button_B = [gui], + button_- = [main_menu], + button_+ = [install], + button_H = [reboot], + button_1 = [options], + button_2 = [favorites], + button_X = A, B, 1, [2], H, -, +, + button_Y = A, B, [1], 2, H, -, +, + button_Z = A, [B], 1, 2, H, -, +, + button_C = [A], B, 1, 2, H, -, +, + button_L = A, B, 1, 2, H, [-], +, + button_R = A, B, 1, 2, H, -, [+], + These buttons can be mapped to any of the following actions: + nothing # does nothing + options # access game options + gui # switch to/from GUI + reboot # reboot to system menu + exit # exit to launching app + hbc # exit to HBC + screenshot # take a screenshot + install # install a game + remove # remove a game + main_menu # access main menu + global_ops # access global options menu + favorites # toggle favorites view + boot_game # boot a game from the drive + boot_disc # boot a game from disc + theme # switch to next theme + profile # switch to next profile + unlock # access the unlock password dialog immediately + As shown, X, Y, Z, C, L & R can also be optionally targetted to + emulate one of the buttons on the Wiimote (A, B, 1, 2, -, +, Home). + If used this way, this emulation will also work in menus. + + As stated, the other options allow you to select the default + action in the game list and GUI mode only. The menus can be + remapped by specifying which buttons affect which commands. + These options take a commas separated list of button names from the + following list: + B, 1, 2, -, M, Minus, +, P, Plus, H, Home, X, Y, Z, C, L, R + + The following are the mappable commands. All the options are theme + options with overrides in config.txt. + + option: button_cancel = [B], + Set which button(s) will act as the back button in menus + + option: button_exit = [Home], + Set which button(s) will perform the 'home' action in menus + + option: button_other = [1], + Set which button(s) will perform the other or alternate action in menus + This covers switching between options and global options, choosing to + download BCA during install, choosing to ignore meta.xml during upgrade etc. + + option: button_save = [2], + Set which button(s) will perform the save action in menus + + EXAMPLES: + To switch buttons B & 1 around so that 1 operates as GUI while + B operates as back: + button_B = options + button_1 = gui + button_other = B + button_cancel = 1 + To make both the L and R buttons on a GameCube controller + operate as back buttons in the menus in addition to B: + button_cancel = B, L, R + Plug in the Classic controller and you can have any twelve + different actions available at once (with A being boot_game): + button_B = gui + button_1 = options + button_2 = favorites + button_- = profile + button_+ = theme + button_H = exit + button_L = remove + button_R = install + button_X = main_menu + button_Y = global_ops + button_Z = boot_disc + +06-02-2010 cfg v52b2 (beta2) + * File custom-titles.txt in the base directory is searched + for game titles. + * Titles are extracted from wiitdb.zip but can be overridden + with either titles file. + * The titles precedence (highest to lowest) is as follows: + - custom-titles.txt + - titles.txt + - wiitdb.zip + - directory name (FAT & NTFS only) + - game image + * When saving global options, the saved settings are listed. + * Console color fixes (Dr. Clipper) + * Play time logging to message board (marc_max & Dr. Clipper) + When enabled, this option will put the correct title + and play time into the Wii Message Board log and will + also be read by the Nintendo Channel. However, this will + usually fail if you skip the Wii Menu via BootMii or + Priiloader autoboot. + * option: write_playlog = [0],1 + Note, it is disabled by default as this fix changes your + Wii's NAND and cannot be used via autoboot methods. + +31-01-2010 cfg v52b (beta) + * Gamecube disc loading + Just like Wii discs, only original discs supported! + * Wii disc loading now uses the disc specified IOS. + This should increase game compatibility. + * Console font outline and shadow fix by Dr. Clipper + * Many translatable strings have been improved. + * Cover URLs updated + +23-01-2010 cfg v52a2 (alpha 2) + * Fixed options. + option: language_path has been removed. + The path is now fixed at /usb-loader/languages/ + option: language has been changed to translation to + prevent conflicts with the game language setting. + option: translation = [AUTO], EN, custom, etc + +23-01-2010 cfg v52a (alpha) + * Translation files now supported. + option: language_path = path to language files + Default: USBLOADER_PATH/languages + option: language = filename without extension + Default: Current the wii language from the following list + JA, EN, DE, FR, ES, IT, NL, ZH, ZH, KO + * Fixed crash issue if booting from disc failed + * Database can now be named wiitdb.zip. + The old naming scheme is still supported however. + +17-01-2010 cfg v51 (release) + * New Sort: last play date + option: sort = play_date + * Removed empty line from game list when showing database info + * Secondary sort using titles added. Lists should be consistent + when there are matching values now + +13-01-2010 cfg v51b3 (beta3) + * Fixed the ambiguity with the game dir layouts (ID_TITLE or TITLE [ID]) + * fat_install_dir = 2 will use the new layout (TITLE [ID]) when + installing + * Removed redundant options from main menu. + * Cleaned up the sort and filter menus. + Improved sort menu. Ascending / descending options for current + sort are remembered. + * Color of database info now changed. + * Install and disc boot menus will show [?] cover before a disc is + read, and game cover for disc if found. + +11-01-2010 cfg v51b2 (beta2) + * More bug fixes + Loader no longer crashes when trying to sort or filter without + a database. + Accented characters now show up in the synopsis. + Display of synopsis cleaned up and improved. + Entities now converted in the synopsis. (", etc) + Main menu will respect the disable_options configuration. + sort=play_count now works properly without reloading the game list. + +10-01-2010 cfg v51b (beta) + * Minor bug fixes + Loader will not wait for a button press in the event a database + is not found. + Disc boot menu will show the proper database information. + * Changed db_url option and db_language option slightly + option: db_url = [http://wiitdb.com/wiitdb.zip?LANG={DBL}] + {DBL} will be replaced by the db_language value + option: db_language = [AUTO], EN, JA, German, etc + * option: "-asc" is no longer necessary to specify a sort as ascending. + * db_show_info no longer hides the hdd info or footer in the console. + * Added more game directory layouts: (by oggzee) + /wbfs/TITLE_[GAMEID]/GAMEID.wbfs + /wbfs/TITLE [GAMEID]/GAMEID.wbfs + /wbfs/TITLE[GAMEID]/GAMEID.wbfs + When loading games from FAT or NTFS + * Added {PUB} to cover url options. + {PUB} will be replaced by the last two characters of the ID + (the publisher) + This can be used to do things like forcing NTSC covers for + PAL games by replacing {CC} with US and {ID6} with {ID3}E{PUB} + +09-01-2010 cfg v51a (alpha) (gannon) + * Wiitdb support. Can be downloaded inside the loader on the + global options screen. + * Enhanced nunchuk support: C mapped to A, Z mapped to B + * Filtering of games based on certain criteria + * Sorting of games based on certain criteria + * Gameplay history + * Disc Loading + * New: Main menu accessible by pressing - or going to the + global options screen. + Disc loading, sorting, filtering, and more options are located here. + * option: db_url = [http://wiitdb.com/wiitdb.zip?LANG={db_language}] + URL to download database from. + * option: db_language = [Console Language], EN, JA, German, etc + Language to use for the database. If invalid or not able to be + displayed by the loader this will default to English. + Both country codes (EN) and languages (English) are valid. + * option: db_show_info = [1], 0 + Show info loaded from the database. + * option: write_playstats = [1], 0 + Write to the play history file. + * option: sort = [title-asc], etc + Change the default sorting method. Default is Title Ascending. + + Valid sort options: + "title" => Title + "players" => Number of Players + "online_players"=> Number of Online Players + "publisher" => Publisher + "developer" => Developer + "release" => Release Date + "play_count" => Play Count + "install" => Install Date + (This will only work with FAT or NTFS drives) + To use ascending add "-asc" to the option. + ie: sort = players-asc + + To use descending add "-desc" to the option. + ie: sort = players-desc + + +21-12-2009 cfg v50c (bugfix) + + * Fixed starting games from SD Card with FAT or NTFS + +16-12-2009 cfg v50 (release) + + * Optimizations for highly fragmented files (either fat or ntfs) + +15-12-2009 cfg v50b2 (beta2) + + * Fixed crash when using flat /wbfs file layout without subdirectories + * Fixed crashes when starting HBC forwarder discs + And added safety checks of memory regions when loading disc + * Raised number of fragments limit to 20000 + * Properly identify dual-layer iso + * Removed obsolete ehcmodules for IOS 222 revisions 2 and 3 + External ehcmodules for these versions are still supported + * Use the new ehcmodule with fat/ntfs support also for wbfs + (but still uses wbfs mode for wbfs partition) + Can be overriden using an external ehcmodule4 + External module for fat/wbfs has been renamed + from ehcmodule_fat.elf to ehcmodule_frag.elf + * In case the new fragments method fails for any reason for FAT + it will fallback to the old method + * Other cleanups + +14-12-2009 cfg v50b (beta) + + * .iso files on NTFS support + The file name layout is the same as for .wbfs files: + /wbfs/gameid.iso or /wbfs/gameid_title/gameid.iso + * Fixes and cleanups for NTFS support (fixed ntfs getf -1 error) + * option: partition=ntfs1 accepted + + About NTFS support: + + FAT support in ehcmodule has been rewritten with a new + generic wii disc emulation system that is: + - light weight / zero overhead + - filesystem independent + - fileformat independent + It works by supplying the ehcmodule with a list of sector fragments + that specify the location of data using direct sector addressing. + To see the list of fragments one can use debug=1 and they will + be printed out before starting the game. + The number of fragments if limited to 5000, that number is also + the max theoretical number of fragments on a wbfs partition (actually + 4600, for a dual layer disc with a 2mb wbfs block size). In normal + conditions the number of fragments should be a lot lower most commonly + just a single big block. Fragments are used to describe both physical + address on hdd and virtual adress on wii disc so if a .wbfs file is used + the list will be composed of 3 fragments - disc header, update partition + and game partition. + libntfs however doesn't seem stable enough for write access at the moment, + so the ntfs partition is mounted read-only meaning install and remove can't + be done from inside the loader for now. + + Credits: WiiPower for libntfs modification which returns the list of fragments. + +12-12-2009 cfg v50a (alpha) + + * Fix for PeppaPig (from NeoGamma by WiiPower) + * Fixes and cleanups for NTFS + +10-12-2009 cfg v50x (experimental) + + * Rewritten FAT support in ehcmodule with a generic system + * NTFS support + * Improved gui speed with large number of games + (most noticable in grid, flow and flow-z gui styles) + * Print on the intro screen if the ios is reloaded a second time + (in case the setting from config.txt is different from default) + * The -fat version 'simple' option does not change 'hide_hddinfo' + * Changed WBFS ERROR: read error while installing a game to a WARNING. + Note: the read error check has been introduced in v47, all previous + versions including the original loader 1.5 and all other loaders + silently ignore it. + * Changed default value of install_partition=only_game + To avoid errors caused by modchips when trying to copy the update partition. + * Minor cosmetic changes to cover download when trying different urls. + +08-12-2009 cfg v49 (release) + + * Fixed install game on SD/FAT + * Override [w]covers_size theme option with config.txt + * simple=x will always set hide_hddinfo when using -fat version + * Only one "#GAMEID" string inside binary - for direct starting + +07-12-2009 cfg v49b2 (beta2) + + * Improved speed of loading game list when using FAT and /wbfs/id_title/ subdirs + * Changed default: fat_install_dir=1 + * When downloading titles.txt and wii region is JA or KO force EN in titles_url {CC} + * Allow specifying alt_dol=name (on disc) when using direct start + * Accept GAMEID without # as argument for direct start (RHAP01 instead of #RHAP01) + * Override some theme options in base config.txt. + The options that can be overriden are those that don't + have a major effect on the theme looks and layout: + - hide_header + - hide_hddinfo + - hide_footer + - buttons + - simple + - cover_style + - cursor + - menu_plus + - gui_text_* + - gui_text2_* + - gui_title_top + * Save cfg loader version when saving gamelist.txt + +03-12-2009 cfg v49b (beta) + + * Added BCA dump to file from install menu + (Press + to install and then press 1 to dump BCA) + +01-12-2009 cfg v49a (alpha) + + * Games on SDHC with IOS 222/223 for both FAT or WBFS partition + * Games in subdirs on FAT: /wbfs/GAMEID_TITLE/GAMEID.wbfs + option: fat_install_dir = [0], 1 + * Rename old boot.dol to boot.dol.bak when upgrading + * If the loader is used to start a game directly + (from a channel created with crap or similar tools) + and option: intro=0 is specified then no intro + and no progress is displayed while the game is being loaded + * Support for .wip game patches (by WiiPower) + Loaded from: sd:/usb-loader/GAMEID.wip (text format) + * Support for BCA data (by Hermes) + Loaded from: sd:/usb-loader/GAMEID.bca (binary data of size 64 bytes) + (updated dip plugin from uloader 3.2) + * option: disable_nsmb_patch=[0],1 will disable the builtin nsmb patches + (in case someone wants to use/test the external .wip patch or .bca data) + * Enable WiiRD if usb gecko is connected and ocarina is enabled + even if not codes are found (by Rfrf) + * Ocarina url fix: /R/ID6 instead of /ID1/ID6 (for SNM*) + + +21-11-2009 cfg v48 (release) + + * External ehcmodule improvements (by WiiZ) + * Fixed titles.txt with custom game ids (proper ID6 lookup) + +20-11-2009 cfg v48b2 (beta2) + + * FAT loading speed optimisations + * Support for 4gb .wbfs files on FAT - fixed the 2gb limit + * Changed the default split size when installing to 4gb-32kb + * Option: fat_split_size = [4], 2 + * Benchmark mode with debug = 8 and start or install a game + * Allow space in profile name, so the names must be separated by a comma (,) + * Raised max favorites to 100 + * Removed obsolete cover sites: gateflorida.com, awiibit.com + * Cleanups + +16-11-2009 cfg v48b (beta) + + * NSMB NTSC patch + * Make updating meta.xml optional by pressing button 1 in the + online update screen to skip it. + * Specifying titles url allows the use of {CC} tag for the country code. + changed default: titles_url = http://wiitdb.com/titles.txt?LANG={CC} + * Presing button A on global options screen works too (same as right) + +15-11-2009 cfg v48a (alpha) + + * Profiles - aka multiple favorite groups + option: profile_names = [default], name2, name3,... + (max profiles:10, max profile name length: 16) + Profiles can be added, removed and reordered with this option. + But if you want to rename it, you will need to change the profile + name also in settings.cfg otherwise it will be considered as a new + name and the old one will be forgotten next time you save settings. + Profiles can be switched in the global options screen. Changing a + game favorite setting is done as usual in the game options screen. + option: profile = name will specify the default profile to use + saving global settings will also save which profile is used + * Minor fix to favorites switching in console mode + * Update /apps/.../meta.xml when downloading an update. + So that the correct version is visible in HBC + * Added 'Update titles.txt' to global options screen. + option: titles_url = [http://wiitdb.com/titles.txt] + Can be changed to a localized version like this: + titles_url = http://wiitdb.com/titles.txt?LANG=FR + +14-11-2009 cfg v47 (release) + + * Minor coverflow fixes + * Cleanups + +12-11-2009 cfg v47b3 (beta3) + + * Support for guitar controller in the loader (by gannon) + * More banner sound fixes (opening.bnr case) + * Changed the default value of the option: hide_hddinfo=1 But only for the -fat + variant, because checking the disc free space on fat takes a few moments. + * 4GB partition offset fixes + * Increased the updates list length to 8 + * Minor coverflow fixes + +09-11-2009 cfg v47b2 (beta2) + + * NSMB main.dol patch by WiiPower + * minor coverflow gui fixes + +09-11-2009 cfg v47b (beta) + + * GC and classic controller and nunchuk joystick support (by gannon) + * option: install_partitions = 1:1 + disable scrubbing when installing, except for the last 256kb which are + stil scrubbed because of the wbfs block size not aligned to wii disc size + * Fixed banner sounds with games that have upper case OPENING.BNR + (Thanks to RolloS60 from wiicoverflow) + * Cleanups + +06-11-2009 cfg v47a (alpha) + + * Better compatibility with weird WBFS partition setups: + - WBFS on RAW disk device, without a partition table + With such a setup the partition table will look like this: + P# Size(GB) Type Mount Used + ----------------------------- + RAW 500.00 WBFS1 [USED] + + - WBFS on EXTENDED partition. Normally a filesystem should + reside on either a primary or logical partition, never on + an extended partition. Extended is just a container of logical + partitions. However older version allowed this setup and so + the support for it is back. The partition table will look like: + P# Size(GB) Type Mount Used + ----------------------------- + P#1 500.00 EXTEND/WBFS1 [USED] + And the loader will also let you fix the partition table by + pressing 1, which will change the partition type from EXTEND to + a known data type (0x0b, which is also used for fat) + +01-11-2009 cfg v46 (release) + + * Support for direct launching of games from fat + (Useful for game channel launching, using loadstructor or similar tool) + To specify direct loading from fat, the parameter has to be in form: #GAMEID-X + Where X is 0-3 for wbfs and 4-9 for fat partitions (4 means first fat partition) + * Automatically switch to ios222 in case ios249 is used for starting games from fat + * Only allow IOS 222 and 223 in game options if fat partition is selected + * Improved drawing speed in some coverflow gui modes + * Cleanups + +30-10-2009 cfg v46b (beta) + + * Added option: partition = [WBFS1], ..., WBFS4, FAT1, ..., FAT9, ask + * Saving global settings will also save current selected partition + * Increased fat cache size in IOS module + * Load FAT module in IOS early in case config has: + ios=222-mload (or ios=223-mload) and partition=FAT* + So that IOS does not need to be reloaded another time before + starting the game from a FAT partition + * Added indication in global options if [FAT] module is loaded in IOS + * Cleanups + +26-10-2009 cfg v46a2 (alpha2) + + * Faster loading of game list from a fat partition - should be instant now. + The only thing that has a slight delay is showing the hdd free space + in console mode game list (if it is enabled) + * Create game info file after installing on a FAT partition named: + usb:/wbfs/GAMEID_TITLE.txt This makes it easier to tell the titles + of installed games when using a filesystem explorer + * Rephrased the warning message when no WBFS partition found + * Allow to select partition if no wbfs partition is found and disable_format is on. + * Minor cleanups in partition selection + +25-10-2009 cfg v46a (alpha) + + * Loading games from a FAT32 partitions. The game file has to + be located and named like this: usb:/wbfs/GAMEID.wbfs + +16-10-2009 cfg v45 (release) + + * full package, no code changes + +12-10-2009 cfg v45b2 (beta2) + + * Fixed multiple WBFS partition support for drives larger than 1TB + * Fixed "DVD in drive check" by patching the game (thanks giantpune) + (only if IOS249 before rev12 or IOS222/223) + +12-10-2009 cfg v45b (beta) + + * Updated ehcmodule for IOS222-mload from uLoader 3.1 + * Fixed disable_format option + * Show game info in options screen while scanning for alt.dols + * More IOS related warning notes: (thanks WiiPower) + - cIOS before rev14: possible 001 error source on new games (Wii Fit Plus) + - cIOS before rev13: need a disc in the drive + - cIOS before rev10: no sd/sdhc support + - cIOS before rev9: no usb support + * Changed the default for: download_id_len = [6] + * Changed IOS order - moved *-yal after *-mload + * Show version on intro screen + * Partition selection screen improvements + * The usual minor cleanups + +11-10-2009 cfg v45a (alpha) + + * Multiple WBFS partitions support (from uLoader) + Limitation: max 4 WBFS partitions on same drive supported by ios 222/223 + * Print game size, dual-layer and wbfs free space in install screen + * Save gamelist.txt when saving settings in global options screen + * option: home = hbc will exit to Homebrew Channel (thanks to giantpune) + (similar to home = exit, but might work better with forwarders) + * Stability fixes: + - Corrupted cover images should no longer crash the loader + - Fixed hdd spin down/up at startup for some drive models (since v43, tnx Narolez) + - Don't print error if opening.bnr not found + - mp3 playing stability fixes (fixes stuttering and slowdowns in rare occasions) + - fixed the few notes of music between banner sound and game start + - minor cleanups + * Added CIOS related warning notices: (thanks Clipper) + - CIOS249 rev13 : unsupported "return to menu" to exit game + - CIOS249 rev14 : unsupported dual layer (start, install) + - CIOS249 : unsupported multiple WBFS + - CIOS222 : unsupported SDHC WBFS + * New intro screen (by Milcoi) + +01-10-2009 cfg v44 (release) + + * Full package + * Updated resources/fonts* with unicode and clock fonts + * Updated titles.txt (from wiitdb.com) + * Added localized resources/titles-XX.txt (EN, FR, DE, ES, IT, NL, PT) + +28-09-2009 cfg v44b6 (beta6) + + * More banner sound fixes (again) + +28-09-2009 cfg v44b5 (beta5) + + * More banner sound fixes (thanks to Dr.Clipper) + +28-09-2009 cfg v44b4 (beta4) + + * More banner sound fixes + all 3 audio formats are implemented, so the few rare + games that previously didn't play should work now. + * Clock font can be changed with font_clock.png + (either from theme or base dir) + +27-09-2009 cfg v44b3 (beta3) + + * Banner sound fixes + +27-09-2009 cfg v44b2 (beta2) + + * Play banner sound in the game start confirmation screen + * option: clock_style=[24],12,12am,0 + +26-09-2009 cfg v44b (beta) + + * Updated ehcmodule for CIOS 222/223 rev4 to uloader 3.0C + * Fixed ios argument from forwarder + * Option: sort_ignore=A,An,The + (sort_ignore=0 for old sort method) + * Show clock in GUI mode if wiimote doesn't point at screen for 5 seconds + * Skip gui transition at start + * changed default: gui_text_outline=FF + * Minor unicode fixes + +22-09-2009 cfg v44a (alpha) + + * Improved coverflow gui mode: + - better transitions between modes and console + - fluid movement of covers + + * Latin unicode support for titles.txt (EN,ES,FR,DE,PT,IT) + (not supported: Japanese, Korean or Chinese) + New font name: font_uni.png (can be created with the Confugrator) + Localized titles.txt can be downloaded from: + http://wiitdb.com/titles.txt?LANG=FR + + * parse cmd line for ios=... before ios reload + (used by forwarders) + + * Changed message for device init error to: + Make sure USB port 0 is used! + (The one nearest to the edge) + + * Scale intro to fullscreen in 576i mode + + +16-09-2009 cfg v43 (release) + + * minor code cleanups + +13-09-2009 cfg v43b (beta) + + * Improved antialiasing in coverflow (sharper) + * Reorganized game options screen: + - Allow to change saved options + - Unsaved changes are private per-game, not global + - Moved alt.dol selection to options menu + * Minor gui fixes: style switching, screen scale glitch + +09-09-2009 cfg v42c (bugfix) + + * Fixed green bar when loading game with a different cios (222...) + +08-09-2009 cfg v42 (release) + + * Fixed scrolling glitch in options screen + with themes that have small console size + * Modified intro screen (based on Milcoi design) + * Moved larger themes to a separate package + +07-09-2009 cfg v42b2 (beta2) + + * Fixed coverflow antialiasing in PAL 576i (50Hz) mode + * Fixed occasional flashing in options screen + * Intro screen (thanks to Milcoi) + +03-09-2009 cfg v42b (beta) + + * Antialiasing in CoverFlow GUI modes. + Can be tuned with option: gui_antialias = [4] {0-4} + * cios 222/223 rev4 support: + - using new dip_plugin and ehcmodule from uloader 3.0B + - external ehcmodule for rev4 has to be named: ehcmodule4.elf + - note: rev2&3 modules are still integrated and used appropriately. + * Changed url tag CC value for portugal/brasil from PO to PT + * Removed obsolete options: cover_url_*_wide, download_wide + * Show 6 letter ID in game options screen + * Better support for custom games in titles.txt - use full 6 letter IDs + if available, otherwise 4 letter IDs still work same as before. + Note: 6 letter ID in titles.txt was supported before but just 4 were used. + * Show a note in the global options screen if there are multiple config.txt + files used (one in base and another in apps/... dir) + * Fixed occasional crash with the combination of + options: video = game, video_patch = all + * Minor fixes (thanks to wiimm) + +01-09-2009 cfg v42a (alpha) + + * Ability to force progressive mode: (from NeoGamma) + - Split 'patch' video mode to a separate option: + video_patch = [0], 1, all + - 'video_patch = all' will force all modes to the selected video mode. + This will also force progressive / interlaced mode, depending on what + is configured in wii settings. This can be used for example to force + progressive mode if the game will otherwise use interlaced mode (MPT) + - The equivalent of the old video = patch is now: + video = system + video_patch = 1 + This will patch NTSC -> PAL modes if console is PAL + and PAL -> NTSC modes if console is NTSC + This will not change interlaced / progressive mode + + * Allow wiird to work if ocarina is enabled and no codes are found. + + +21-08-2009 cfg v41 (release) + + * Fixed minor glitch with resized overlays + * Increased hide game limit to 500 + * Updated all themes in the package to use overlays + (while still being backward compatible) + * New themes included: (all using overlays) + - NXE_transparent by Blue-K + - Nature 3D by DonTlaloc + - Black Cat by Milcoi + +18-08-2009 cfg v41b (beta) + + * Streamlined steps in game install screen + * Fixed green screen before starting game (from cloader) + +17-08-2009 cfg v41a (alpha) + + * Improved theming: + + * Background overlay support + additional images can be supplied that will be overlaid over the background: + - bg_overlay.png or bg_overlay_w.png for console background + - bg_gui_over.png or bg_gui_over_w.png for gui background + If *_w.png variant is not found then the normal is used. + New options: + - background_base + - wbackground_base + - background_gui + - wbackground_gui + Used to specify the background base over which the overlays are applied. + + * background width can be larger than 640 and will be either + - scaled if widescreen + - cropped if not widescreen + down to 640x480. Note: height must still be 480. + + * Option layout no longer re-sets the covers_size and wcovers_size + So that a cover_style or [w]cover_size in front of layout works as expected. + + +09-08-2009 cfg v40 (release) + + * Increased timeout for entering password to 30 seconds + * Renamed option admin_lock to admin_unlock as that better describes + what it does - it allows you to unlock admin functionality + * Fixed option unlock_password + +08-08-2009 cfg v40b3 (beta3) + + * uLoader cIOS 222/223 rev3 support + - both rev2 and rev3 are supported + - ehcmodule for rev2 is updated to uloader 2.5 + - ehcmodule for rev3 is from uloader 2.8D + - external ehcmodule for rev2 has to be named: ehcmodule.elf + - external ehcmodule for rev3 has to be named: ehcmodule3.elf + + * Minor GUI speed optimizations of rendering and cover loading + + * Admin unlock by password + + * Hide games from settings screen + + * Added new config option (admin_lock = 0,[1]) for admin locking (i.e. Parental Mode). + When this setting is enabled, it will allow all screens normally locked by the + simple or disable_* settings to be unlocked via a "secret" wiimote button combination. + In addition: when unlocked, any covers that are hidden with the hide_game option will + be displayed. To access the unlock screen, hold the 1 button down for 5-10 seconds + and the screen will appear. After you see the text "Enter Code:", press the wiimote + buttons in the correct order. If you were successful, the word SUCCESS will appear + on the screen. Otherwise the word LOCKED! will appear. The unlock screen has a 15 + second timeout limit so if an incorrect (or no) password is entered, it will + automatically lock. To set the lock back on with the original settings intact, hold + the 1 button for 5-10 seconds and the lock will automatically turn on. When the loader + is started, the lock will always be enabled, so there is no need to manually set the + lock before you let the kiddies play. :) + - NOTE: this option is enabled by default. + * Added new config option (unlock_password = [BUDAH12]) to allow a custom button + combination to be used for the admin_lock password. + - NOTE: The password length is limited to 10 characters. Do NOT use quotes around the + password - just type what you want it to be. E.g. unlock_password = 12UDAB + - The following are the button mappings for the password: + D-Pad Up: U + D-Pad Down: D + D-Pad Right: R + D-Pad Left: L + B button: B + A Button: A + Minus button: M + Plus button: P + Home button: H + 1 button: 1 + 2 button: 2 + * Automatically hide uLoader's CFG entry, so hide_game=__CF is no longer needed. + * Added new option on the Game Options screen called "Hide Game". This allows you + to set which games are hidden when the admin lock is LOCKED. In order to see this + option, admin_lock must be enabled (which it is by default) AND the admin lock must + be in an unlocked state! + - NOTE: this functionality completely replaces the hide_game option, but they CAN + be used together. Any games currently listed in hide_game will ALWAYS be marked + as hidden by default in Game Options and cannot be unhidden until they are removed + from hide_game. + - NOTE 2: An easy way to convert all your games in hide_game to this new functionality + is to start the loader with your hide_game still in config.txt and then go into + Game Options in any game (you may have to unlock admin lock first) and change + something and save it. All your hide_game entries will automatically be saved. + Then you can remove the hide_game entry completely from config.txt. + + +06-08-2009 cfg v39c (bugfix) + + * Changed cheats url to: geckocodes.org + * Fixed SD card access in games + * GUI page number alignment + +06-08-2009 cfg v39 (release) + + * Fixed running games from SDHC + * GUI Text improvments: + - Fixed gui_text_color=black + - gui_text_outline and gui_text_shadow accept black and white too + +05-08-2009 cfg v39b (beta) + + * Improved the covers reflection + * GUI Text improvments: + - Moved gui_text_outline and gui_text_shadow to theme section + - Select the outline color automatically if only alpha specified + (now AA and 000000AA are different) + - Added gui_text2_* options for text on darkened background in coverflow mode + (faded background or reflections) + - Center title in gridflow modes + - Adjusted title position in gui for overscan + - Added option: gui_title_top = [0], 1 + - Fixed sometimes unreadable text + * Added option: gui_lock = [0], 1 + (locks gui style changes with up/down buttons) + * Increase max title length in titles.txt to 64 + * url tag CC=ZH For W region game id (Taiwan) + +01-08-2009 cfg v38 (release) + + * Fixed searching for .mp3 files + * Fine tuned wiimote pointer scrolling in coverflow + * Bigger default gui font + * Added options: gui_text_outline=AA gui_text_shadow=AA + * Added a better selection of fonts + * Removed a few old themes (ultimate, stars) + * Updated titles.txt + +20-07-2009 cfg v38b (beta) + + * Added WiiMote pointer scrolling in coverflow modes - as you move the pointer towards + the edge of the screen, the covers automatically move. Also, the movement speed + increases as the pointer approaches the edge. + * Added random music playing - all .mp3 or .mod files in the base + folder will play randomly (sd:/usb-loader by default) + * Added new parameter (PATH) to the "music" option in config.txt to allow + any folder to be specified for music playing (e.g. sd:/mp3s or usb:/mp3s) + * Pause music while installing new games. + * Changed option: download_cc_pal = [AUTO], EN, FR, DE, AU, ... + If AUTO is specified, then the code is set depending on the console + region - if Australia: AU, Brasil: PO else, console language is used. + This is now the default, but can still be specified as before. + * Support for msdos and utf8 formatted ocarina TXT files + * Always mount USB FAT partition, not just when there is no config.txt on SD. + * Minor cleanups + +25-07-2009 cfg v37 (release) + + * Fixed USB FAT detection on drives larger than 1TB + (Also FAT detection shold no longer require that the partition is marked active) + * Fixed covers not showing in favorites mode + * Minor cleanups + +24-07-2009 cfg v37b2 (beta 2) + + * Fixed some issues with the new libfat + * Display which covers are already present when downloading + missing covers for a single game. + +23-07-2009 cfg v37b (beta) + + * Improved covers loading speed - using new libfat. + (fixed the issues with usb hdd fat partition in v37ax) + * Cover downloading improvements: when downloading missing covers, + it will now check if it's missing for all cover styles not just the current. + (so that full covers can be easily downloaded, without the need to download everything) + Also note: pressing RIGHT will download only missing covers, while pressing LEFT will + force download all covers. This is valid for per-game cover download and for downloading + covers for all games. + * Changed WiiMote rotation functionality in coverflow: rotate 90 degrees to flip to the + back cover and then rotate back up to 0 degrees to flip to the front. + +22-07-2009 cfg v37ax (alpha-experimental ***SEE NOTE!) + + * Same changes as v37a, but with new libfat for improved cover loading speed. + - NOTE: DO NOT USE THIS if your usb-loader directory is on a USB drive! It will NOT mount your FAT + partition. This is just a preview release for people who have all their covers / themes / + config.txt on SD. + +22-07-2009 cfg v37a (alpha) (usptactical) + + * Full cover image support in all coverflow gui modes + - NOTE: Full covers will load in all coverflow modes by default. If no local full cover is found, + the 2D flat cover will be used. + * Full cover download support - choose in Global Options to download + - NOTE: Full covers go in the "usb-loader/covers/full" directory. + * Added URL tags for full cover downloads. + * Twist the WiiMote 90 degrees (right or left) while pointing at the screen to flip the center cover + over to display the back. Up on the D-Pad continues to do the same. + * Mouseover on cover in coverflow mode highlights cover and shows game title. Pressing A on the + selected cover will load the game, 1 will go to Game Settings, etc. + * Visual improvements to cover objects in coverflow mode + +22-07-2009 cfg v36d (bugfix) + + * Fixed error when accessing network and after that using IOS 222/223-mload + * Improved url tag {CC} region detection for: IT, ES and NL + * Changed default urls to use {ID6} instead of {ID} + +22-07-2009 cfg v36c (bugfix) + + * Fixed url {CC} tag for US region + * added option: download_cc_pal = [EN], FR, DE, AU, ... + The code is used as a replacement for {CC} tag for PAL regions. + If image is not found with the supplied cc code, download is + retried with EN code. + * Changed default urls to wiitdb.com site. + +20-07-2009 cfg v36 (release) + + * Minor cleanups of the Cheat Manager + Increased limits for cheat codes (per game): + max 256 cheats, max 1000 total code lines + + * added {CC} tag for urls (Country Code) based on + game region id expands to one of: EN FR DE JA KO + +19-07-2009 cfg v36b (beta) + + * Ocarina TXT (Cheat Code Manager) + (based on wiicmpnc and wiiflow) + * Loadstructor support (launch a game directly) + (either by binary gameid patch or forwarder param) + * Online update: size check + +18-07-2009 cfg v35d (bugfix) + +* Fixed crash after installing a game + +15-07-2009 cfg v35c (bugfix) + +* Stability improvements for games that require alt.dol. + Fixes freezes and glitches in MOH2, FIFA08 (maybe others as well) + +10-07-2009 cfg v35 (release) + + * Force video mode fixes + * Online update: progress indicator, fixed crash, compact display + * Reduced size of embedded graphics + * Minor cleanups + +08-07-2009 cfg v35rc (release candidate) + + * Stability improvements + * Added all coverflow modes to gui_style option: + - coverflow3d coverflow2d frontrow vertical carousel + * Save gui style and rows settings in global options save + * Online update: improved detection of boot.dol location + * Fixed garbaged display when printing out a code dump + * Increase max favorites to 64 + * Updated covers urls + +07-07-2009 cfg v35b (beta) + + * Save global settings: theme and device + * Save selection of alt.dol from disc + +06-07-2009 cfg v35a6 (alpha6) + + * Online Update + * minor fixes + +06-07-2009 cfg v35a5 (alpha5) + + * Fixed theme switching + * Stability fixes + * Disabled console game list markings. + Can be re-enabled with options: + console_mark_page = [0], 1 + console_mark_favorite = [0], 1 + console_mark_saved = [0], 1 + +05-07-2009 cfg v35a4 (alpha4) + + * Disabled ttf font rendering to speed up cover loading and resolve issue when music + is playing. + +04-07-2009 cfg v35a3 (alpha3) + + * Integrated Coverflow Gui mode + - NOTE: This mode renders the game covers in true 3D so 2D (flat) cover images are needed. + If "fake" 3D/disk cover images are currently being used (in console or Grid mode), moving to Coverflow + will automatically switch to 2D covers and then switch back when leaving. + - To access Coverflow mode (from console mode) press B and then down several times. + Each subsequent Down button press will iterate through each coverflow style. + - Currently only 2D flat front covers are supported. Full cover image (front, spine, + back) support will be added in a future release. + * Favorites in console mode are now marked with a '*' + * Games with saved options are now marked with a '#' in the last column of the console. + +03-07-2009 cfg v35a2 (alpha2) + + * Fixed =+ with cover_url options + * Changed default urls to this: + +cover_url = +cover_url =+ http://wiicover.gateflorida.com/sites/default/files/cover/2D%20Cover/{ID}.png +cover_url =+ http://boxart.rowdyruff.net/flat/{ID}.png +cover_url =+ http://awiibit.com/BoxArt160x224/{ID}.png +cover_url =+ http://wiitdb.com/wiitdb/artwork/cover/EN/{ID}.png +cover_url =+ http://wiitdb.com/wiitdb/artwork/cover/US/{ID}.png + +#3d +cover_url_3d = +cover_url_3d =+ http://wiicover.gateflorida.com/sites/default/files/cover/3D%20Cover/{ID}.png +cover_url_3d =+ http://boxart.rowdyruff.net/3d/{ID}.png +cover_url_3d =+ http://awiibit.com/3dBoxArt176x248/{ID}.png +cover_url_3d =+ http://wiitdb.com/wiitdb/artwork/cover3D/EN/{ID}.png +cover_url_3d =+ http://wiitdb.com/wiitdb/artwork/cover3D/US/{ID}.png + +#disc +cover_url_disc = +cover_url_disc =+ http://wiicover.gateflorida.com/sites/default/files/cover/Disc%20Cover/{ID}.png +cover_url_disc =+ http://boxart.rowdyruff.net/fulldisc/{ID}.png +cover_url_disc =+ http://awiibit.com/WiiDiscArt/{ID}.png +cover_url_disc =+ http://wiitdb.com/wiitdb/artwork/disc/EN/{ID}.png +cover_url_disc =+ http://wiitdb.com/wiitdb/artwork/disc/US/{ID}.png + + * option: download_all_styles = [1], 0 + Downloading all styles (flat,3d,disc) of covers, or just the current style + +02-07-2009 cfg v35a (alpha) + + * Load Alternative .dol from disc (from NeoGamma by WiiPower) + +01-07-2009 cfg v34 (release) + + * Changed default URLS to sites: + 1. wiicover.gateflorida.com + 2. boxart.rowdyruff.net + + * Retry downloading from all available servers if cover not found + + * Multiple URLS can be specified for cover_url_* options + They can be in the same line separated with space, example: + cover_url = http://site1.com/{ID}.png http://site2.org/{ID}.png + Or in multiple lines using =+ to add instead of =, example: + cover_url = http://site1.com/{ID}.png + cover_url =+ http://site2.org/{ID}.png + cover_url =+ http://site3.net/{ID}.png + + * Added another URL tag: {ID} which will try automatically to + find the correct ID by trying ID6, ID4 and ID3 + + * Download all cover styles at the same time: 2d, 3d and disc + + * option: download_id_len = [4], 6 + Specifies the downloaded cover ID length for the saved file name + + * Print info when loading external ehcmodule + +29-06-2009 cfg v34b (beta) + + * Changed the default urls from theotherzone.com to wiiboxart.com + although, automatic downloading of covers from that site now requires payment, + which also requires changing of URL to include /USERNAME/PASSWORD/ + + * Changed the way cover_url options work: + - cover_url* are now global instead of theme options. + - Added per cover style url options: + cover_url - standard (2d) covers + cover_url_3d - 3d covers + cover_url_disc - disc covers + - Note: These options still work and do the same: + cover_url_norm, cover_url_3d_norm, cover_url_disc_norm + - Downloading covers in widescreen mode will no longer download resized + widescreen covers, instead full covers are downloaded and then resized + when they are being displayed. In other words, cover_url_*_norm is used + always instead of cover_url_*_wide in widescreen mode. + To revert this to previous behaviour, use the option: + download_wide = [0], 1 Which will use cover_url_*_wide options in widescreen mode: + cover_url_wide + cover_url_3d_wide + cover_url_disc_wide + - Note: ID_wide.png covers are still used if found in widescreen mode. + If not found then ID.png is used, same as before. + + * Changed cover_style to not change the cover_url values as was before + but just selectes the proper url from one of the specified options. + + * Console mode game list improvements: + - Added '+' indicators if there are more games in up or down directions. + - Added page indicator + + * Use bg_gui_wide.png for widescreen gui background. + If not found bg_gui.png is used instead. + + * Themable gui resources: favorite.png, pointer.png, hourglass.png, font.png + See inSDRoot/usb-loader/resources for example files: + - favorite0.png - turns off the favorite star + - favorite32.png - half sized favorite star + - favorite64.png - full sized favorite star + Copy one of the above to sd:/usb-loader/favorite.png to change the favorite star. + + * option: start_favorites = [0], 1 + Start with the favorites game list + + * Ocarina codes are now searched in the following directories: + 1. sd:/usb-loader/codes/ + 2. sd:/data/gecko/codes/ + 3. sd:/codes/ + + * Updated ehcmodule to uloader 2.3 (used by ios 222/223-mload)(by Hermes) + Load external ehcmodule if found in: sd:/usb-loader/ehcmodule.elf + + * Updated Nature theme (by DonTlaloc) + + +26-06-2009 cfg v33 (release) + + - When switching favorites in gui mode, move to start of the game list + +26-06-2009 cfg v33b3 (beta3) + + - Fixed game: Wii Sports Resort + (by disabling "Sam & Max" fix, which was not working properly anyway) + +26-06-2009 cfg v33b2 (beta2) + + - fixed ocarina + +26-06-2009 cfg v33b (beta) + + - Favorite Games + Favorite game is marked in the game options screen. To switch between + all games and favorite games, press 2 in either gui or console mode. + + - Split game and global options screens + - More memory for alt.dol - higher loader start address (same as v32t1) + - Possible alt.dol stability improvement (bss init) + - If the loader is started from usb drive (fat partition) it will look + for the configuration first on the usb drive and if not found on sd card. + - Properly renamed "002 fix" to "Anti 002 fix" in game options screen + - Fixed crash if ocarina is enabled and MK is started + +22-06-2009 cfg v32 (release) + + - New default theme: BlueMatrix (by Narolez) + - New HBC icon (by Matriculated) + +22-06-2009 cfg v32b2 (beta2) + +- fix for alt.dol out of memory issues + +22-06-2009 cfg v32b (beta) + + - 002 fix option (002b variant) + - Updated ios222/223-mload ehci module and dip plugin to uLoader 2.1 version + - Split VIDTV option from video modes + - new game compatibility options: + vidtv = [0], 1 + fix_002 = [0], 1 + block_ios_reload = [0], 1 + alt_dol = [0], 1 + - Remeber saved settings for the above game options + +20-06-2009 cfg v32a2 (alpha2) + + - Alternative .dol loading option (from NeoGamma by WiiPower) + Loaded from loader base dir GAMEID.dol (4 letter ID) + [ default: sd:/usb-loader/GAMEID.dol ] + - Sam & Max fix (by WiiPower) + - Updated ios22[23]-mload ehci module from uLoader 2.0 + - Init wpad after ios reload, so that confirm_ocarina doesn't hang + +17-06-2009 cfg v32a1 (alpha1) + + - Block IOS Reload option + (works only with IOS 222-mload or 223-mload) + +02-06-2009 cfg v31c (bug fixes / minor improvements) + + - cIOS 249 rev13 - 002 fix (by WiiPower) + - Fixed: power off button in GUI mode + - Fixed: gui style no longer resets when switching to console and back to gui + - option gui=start will switch to gui directly after device init, + without first displaying the game list in console mode + - print IOS version and revision in device selection menu + +01-06-2009 cfg v31 (release) + + - option: gui_style = [grid], flow, flow-z + Set the default GUI style + - option: gui_rows = [2] (Valid values: 1-4) + Set the default number of rows in GUI mode + - gui_text_color = [black], white, HEX + Set the text color in GUI mode + Note: to use a color other than a black or white, it has to be + represented as a HEX value with the following components: RRGGBBAA + RR=red, GG=green, BB=blue, AA=alpha + Example: red = FF0000FF, blue = 00FF00FF, yellow = 00FFFFFF + - Return directly back to GUI mode if any action is started from GUI mode + Also return to GUI if the action is canceled. + Action refers to one of: install, remove, options, run game + - Re-enabled buttons=options_B + If options_B is used then GUI mode switching is done with button 1 + - Changed option covers_path to global instead of theme option + In addition the following options are added: + option: covers_path_2d + option: covers_path_3d + option: covers_path_disc + cover_style will then select which of the above paths is used + Changing covers_path will change all covers_path_* like this: + covers_path_2d = covers_path + covers_path_3d = covers_path/3d + covers_path_disc = covers_path/disc + If you need fine controll on the 2d/3d/disc paths use the + covers_path_* after covers_path. + - Per-game save settings: country_patch, ios + + +31-05-2009 cfg v31b2 (beta2) + + - GUI: New style: "flow-z" + - Country Patch for better compatibility with some games (by WiiPower) + (for Punch Out & EA Sports on JPN consoles, ...) + Settings menu option: "Country Patch" + config option: country_patch = [0], 1 + - Fixes to IOS 222-mload/223-mload + (fixed occasional hang at start when ios was set in config.txt) + - Hide uLoader's CFG entry (hide_game=__CF) in default config.txt + - Updated titles.txt + - Updated Wolf_3D theme gui background + +28-05-2009 cfg v31b: (beta) + + - GUI: New style: "grid flow" + To change gui style mode use button up when in 4 rows mode + or button down when in 1 row mode + - GUI: Animated transition between rows change and style change + - Fixes to IOS 222-mload/223-mload + - If custom IOS is specified, load it before storage device init + - Added game loading progress indication when using custom IOS + +25-05-2009 cfg v31a: (alpha) + + - Support USB drive with a FAT partition for configuration and covers + (If config.txt is not found on SD, then uses USB FAT partition) + - Support for kwiirk and hermes cIOS 222 + option: ios = [249], 222-yal, 222-mload, 223-yal, 223-mload, 250 + Note: 222-yal is for kwiirk's cIOS + Note: 222-mload is for Hermes's cIOS + Note: a few seconds of delay when starting a game with custom ios is expected. + - Added IOS selection to options screen + - GUI Mode screenshot enabled with option: home = screenshot + - GUI: added fade transition effect from console + - option: gui_transition = [scroll], fade + Set GUI transition effect between console and gui mode + - GUI: animated transition when changing number of rows + - minor GUI fixes + + +20-05-2009 cfg v30: (release) + + - Fixed crash when switching theme after gui mode + - Other minor GUI fixes + +20-05-2009 cfg v30b: (beta) + + - GUI Mode (beta) + By default console mode is started. + To switch to GUI mode, press B in main screen. + While in GUI mode, the following buttons are used: + button A : start selected game + button B : return to console mode + button LEFT/RIGHT : previous/next page + button UP/DOWN : switch number of rows (1-3) + button 1, +, - : options, install, remove + button HOME : exit + The background image in GUI mode can be changed with the file: + sd:/usb-loader/themes/Theme_Name/bg_gui.png or + sd:/usb-loader/bg_gui.png + - option: gui = [1], 0, start + Enable or disable GUI mode. + Using gui = start will start directly in GUI mode when loader is started. + - Start music only after usb hdd device is opened, to avoid stuttering + while initializing the usb device. + - Other minor fixes + + +19-05-2009 cfg v30x: (experimental) + + - GUI mode (experimental) + - fixed crash when using some forwarders + +15-05-2009 cfg v29d: + + - Fixed option: hide_game=... + +12-05-2009 cfg v29c: + + - Fixed minor glitch in 576i mode + +12-05-2009 cfg v29: + + - Theme support (See README-CFG.txt for details on how to create themes) + - option: theme = Theme_Name + Load a specified theme from sd:/usb-loader/themes/Theme_Name/theme.txt + - Runtime theme change from the options menu + - Fixed mp3 stuttering + - Now mp3 plays fine also while installing a new game. + - Faster loading of game covers and scrolling through the game list + - Now it works fast also with large (1000+) collection of covers in same directory + - Fixed: searching for music.mp3 if base directory is not sd:/usb-loader + - removed obsolete option: buttons=ultimate + +05-05-2009 cfg v28: + + - Screenshot is saved to screenshot[1..99].png - the first which doesn't exist. + + - More flexible base path - it will search for config.txt in: + sd:/usb-loader/, sd:/USBLoader/ and app dir which by default + is: sd:/apps/USBLoader, but can be something else if started with + homebrew channel from a different location, for example: sd:/apps/my_usb_loader/ + The location where config.txt is first found is then used as a base for all other + files such as: titles.txt, settings.cfg, music, backgrounds, covers and screenshots. + Note1: the config.txt and titles.txt in appdir will be read in addition even + if the base path is one of the global paths like sd:/usb-loader. + Note2: background and covers paths can still be set to any path + using appropriate config options. + + - option: cursor = ">>" + Changes cursor shape, at max 2 characters are used. Can be empty. + If you want spaces, so that the menu is not shifted use quotes + like this: cursor = " " + + - option: menu_plus = "[+] " + Changes the [+] mark in the main, options and start menu. + At max 4 characters are used. + + +03-05-2009 cfg v27: + + - Clear button status before formatting, so it always asks for confirmation + - Allow to exit from device menu with button B, if device has already been + selected previously + - Fixed crash on device retry-on-failure timeout (no ios reload) + - option: ios = [249], 222 + +02-05-2009 cfg v26: + + - Fixed PNG transparency + - Clear button status after install, so it always asks for confimration + +01-05-2009 cfg v25: + + - Automatic resize of covers (4:3 -> wide) if wide cover not found. + It can actually resize any size to specified size with options: + covers_size = width, height + default: covers_size = 160, 225 + wcovers_size = width, height + default: wcovers_size = 130, 225 + used for widescreen setting. If not set it will use the covers_size + and properly scaling it to widescreen size. + With these changes in place the loader is now compatible with + the 3d cover pack by NeoRame: + http://rs777.rapidshare.com/files/227304261/3D_Cover_Update_29_April_2009.rar + - The supplied noimage.png images in covers/3d and covers/disc + now use transparency (tnx narolez) + +30-04-2009 cfg v24: + + - Support transparent PNG for covers (used by 3d and disc covers) + - Changed builtin background and nocover images to match the default provided inSDroot. + - Changed default layout to large3 (to match builtin background) + +29-04-2009 cfg v23: + + - Game compatibility fixes: + - Fixed harvest moon & nunchuck problem + - Added video mode: VIDTV + (It was previously enabled by default, but is not compatible with all games + so it is now a separate mode. Select it in game options / video mode. + Required for Japanese and maybe some other games) + - Fixed network problems in games when downloading covers from loader. + - Restart music if it was stopped by usb device retry on failure + +27-04-2009 cfg v22: + + - option: console_transparent = [0], 1 + Enable transparent console. + - Auto repeat Up and Down movement if button is held + - Allow re-download of a cover if it exists + - Added layout=kosaic + + +27-04-2009 cfg v21: + + - Fixed downloading of NTSC covers. + - Check if downloaded file is a valid PNG + - Check for a valid HTTP status of downloaded file + - New set of noimage.png for 3d and disc covers (tnx Narolez) + - Clear console on init (Narolez) + - option: home = screenshot + make a screenshot when home button is pressed (only in main and options screens) + - option: confirm_ocarina = [0], 1 + - option: cursor_jump = [0] or N + Sets how much moves left/right (if 0 do a end page / next page jump) + + +25-04-2009 cfg v20: + + - Added option to 'Download All Missing Covers' in the options menu. + If selected with dpad-right, only missing covers will be donwloaded + If selected with dpad-left, ALL covers of installed games will be downloaded + + - Support for 3d covers and disc covers + option: cover_style = [standard], 3d, disc + This option also changes the cover_url, covers_path and cover_size + covers_path will be set to: + standard: sd:/usb-loader/covers + 3d: sd:/usb-loader/covers/3d + disc: sd:/usb-loader/covers/disc + + - New set of backgrounds suitable for 3d covers + + - Custom urls. + URL can contain any of the following tags which are then replaced + with proper values: {REGION}, {WIDTH}, {HEIGHT}, {ID6}, {ID4}, {ID3} + + option: cover_url_norm = URL + (url for normal 4:3 covers) + default: + cover_url_norm = http://www.theotherzone.com/wii/{REGION}/{ID6}.png + + option: cover_url_wide = URL + (url for widescreen covers) + default: + cover_url_wide = http://www.theotherzone.com/wii/widescreen/{REGION}/{ID6}.png + + option: cover_url = URL + (This changes the url for both normal and widescreen covers) + + - Debugging can be enabled with option: debug=1 + It will also show some music related info + + +24-04-2009 cfg v19: + + - Fixed music stuttering + - Fixed mp3 loop crash + +23-04-2009 cfg v18: + + - Improved USB and SD retry initialize on fail (tnx: Narolez) + - Improved mp3 loading + - Added support for mod files as background music + - option: music = [1], 0, filename + option music will now accept a filename (.mp3 or .mod) which + can be relative to sd:/usb-loader or an absolute pathname + if option is set to: music = 1 then it will search for music.mp3 + or music.mod whichever is found first in sd:/usb-loader + +22-04-2009 cfg v17: + + - Eject DVD after install is complete if button A is pressed + - option: hide_game = [0], GAMEID1, GAMEID2, ... + Hide games from list (can be used for parental control) + Multiple games can be specified in one line separated by comma "," + or each game in a separate hide_game = GAMEID line. + setting hide_game = 0 will reset the hide list. + GAMEID is a 4 letter game ID. + Example: hide_game = RZZP, RDCP + - option: pref_game = [0], GAMEID1, GAMEID2, ... + Preffered games, to be shown first in the list. + Syntax is same as with hide_game. + Example: pref_game = RHAP, RSSP + - Show number of games after hdd info + +21-04-2009 cfg v16: + + - fixed music sometimes not working + - widescreen support + option: widescreen=[auto], 0, 1 + If widescreen is enabled (or autodetected) then the following options are used: + option: wbackground=filename.png + option: wconsole_coords=x,y,w,h + option: wcovers_coords=x,y + Note 1: widescreen will be enabled only if the file specified by wbackground + is found, otherwise it will fall back to normal mode. + Note 2: cover images have to be named GAMEID_wide.png + downloading covers will save them with this name automatically. + Note 3: some layouts will specify widescreen cooridinates automatically + like: large3 and ultimate3, so there is no need to specify them manually, + if one of these layouts are used. + +20-04-2009 cfg v15: + + - Added Force PAL50, PAL60, NTSC video modes (tnx Narolez) + option: video=pal50, pal60, ntsc + - Light up DVD slot when install finished (Dteyn/Bool) + - BETA: mp3 background music - plays a sd:/usb-loader/music.mp3 if found. + NOTE: sometimes the audio stutters, not sure how to fix it + Can be disabled with option: music=0 + + +19-04-2009 cfg v14: + + - Colored console text: + option: colors=[dark], light, mono + will select a predefined set of colors for a dark or light background + or normal 2 color text if mono is specified + Individual text colors can be specified with options: + color_header, color_selected_fg, color_selected_bg, + color_inactive, color_footer, color_help + - Fixed individual disable_xxx settings. (tnx. Narolez) + +18-04-2009 cfg v13: + + - Support running games from a SD/SDHC Card with a WBFS and FAT32 partition. + So you can have the loader, covers, background and other configuration files + on the FAT partition and games on WBFS partition. + Note: this worked in waninkoko original 1.4 but seems broken in 1.5. + - Minor fixes to download covers from internet (tnx: Don Killah) + - Fixed simple=1 option + +18-04-2009 cfg v12: + + - fine granularity of simple options: + added options: disable_remove, disable_install, disable_options, disable_format + by default none of this is set. + setting simple=1 will change all of these disable_xxx options and + hide_hddinfo and hide_footer to 1 and confirm_start to 0. + setting simple=0 will do the opposite. + any of the disable_xxx, hide_xxx and confirm_start options can be set individually. + - allow absolute paths for background (like sd:/somedir/myimage.png) + if the specified background is not an absolute path it is searched + in default directory (sd:/usb-loader) + - option: install_partitions = [all], only_game + - minor stuff: background version, fixed default apps/USBLoader/config.txt + + + +17-04-2009 cfg v11: + + - Rebase code to waninkoko SDUSB Loader 1.5 + +16-04-2009 cfg v10: + + - load game covers from the options menu (by hungyip84/forsaken) + - fixed Japan (and other regions) games (tnx: Narolez) + - support for covers with 4 letter ID like: RHAP.png + (6 letter ID file names like: RHAP01.png are of course still supported.) + Covers that are downloaded from the options menu will be automatically saved + to a file with 4 letter ID instead of 6. + +16-04-2009 cfg v9: + + - new set of backgrounds to match the new buttons=options mode + - option: layout=large3 to match the new backgrounds + - automatically set number of entries per page + and number of characters from the console size + - buttons=options_B for alternative button layout (press B to enter options menu) + - option: layout=ultimate3 to match WiiShiza backgrounds from ultimate V 3-7 + note: ultimate 7 background use in combination with buttons=options_B + - options to control the look of main menu: + option: hide_header = [0],1 + option: hide_hddinfo = [0],1 + option: hide_hfooter = [0],1 + - included titles.txt from: http://wiki.gbatemp.net/wiki/index.php/USB_Loader_titles.txt + - cleaned up the sample config files to match the sample backgrounds + - remove confirmation for Ocarina, since it is configurable + - fixed display of titles longer than console + - fixed a crash with odd x coordinates + - other minor fixes + +15-04-2009 cfg v8: + + - option: device=[ask], usb, sdhc + - option: confirm_start=[1], 0 + - option: buttons=[options], original + With buttons=options mode, you can edit options in a menu: + direction buttons - select and change options + button 1 - enter options menu + button 2 - save/discard game options + +14-04-2009 cfg v7: + + - Added per-game saved settings (video, language, ocarina) + Per-game settings can be saved/forgotten in the start game + screen with button 2. + The settings are saved to sd:/usb-loader/settings.cfg + +13-04-2009 cfg v6: + + - option: buttons=[original], ultimate (change button controls layout.) + The button layout "ultimate" is: + BUTTON 1 - force video option + BUTTON 2 - ocarina option + BUTTON B - language option + +13-04-2009 cfg v5: + + - merge changes from hungyip84 Ultimate V6: + - language selection (config option: language=...) + - video modes (config option: video=system, game, patch) + +13-04-2009 cfg v4: + + - Rebase code to waninkoko SDUSB Loader 1.4 + +12-04-2009 cfg v3: + + - Rebase code to waninkoko SDUSB Loader 1.3 + - standard SD mode for background and cover access still works + (waninkoko's requires new cios with SDHC support) + - changed default base path from sd:/USBLoader to sd:/usb-loader + so it uses the same path as waninkoko. + (If new path is not found, it reverts to the old one) + - fix ocarina path + - fix loading config from HBC app dir (/app/usbloader_cfg/config.txt) + - option: console_entries=N (number of shown games in list) + - option: layout=original2 (waninkoko 1.2-1.3) + +11-04-2009 cfg v2: + + - option: layout=large2 matches usptactical cover coordinates + - option: layout=ultimate1 (WiiShizza) + - option: layout=ultimate2 (jservs7 / hungyip84) + - option: console_coords=x,y,width,height + - option: console_color=foreground,background (color values: 0-15) + - option: covers_coords=x,y + - option: covers_path=path + - show noimage.png if cover missing + - sd bug fix (56Killer) + +11-04-2009 cfg v1: + +Based on Wanikoko & Kwiirk USB Loader 1.1 + nIxx mod (USBLoader1.1ssorgmod+cover) +including: + - Sorg mod1.02 (Sorg) + - Ocarina (fishears) + - Cover (usptactical) + - Video Force + - Simple / childproof + - Config, Title rename (oggzee) + diff --git a/USBLoader.pnproj b/USBLoader.pnproj new file mode 100644 index 0000000..289abe6 --- /dev/null +++ b/USBLoader.pnproj @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cios/dip_frag/LICENSE.txt b/cios/dip_frag/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/cios/dip_frag/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/cios/dip_frag/Makefile b/cios/dip_frag/Makefile new file mode 100644 index 0000000..373cb83 --- /dev/null +++ b/cios/dip_frag/Makefile @@ -0,0 +1,58 @@ +# devkitARM path +DEVKITARM ?= /opt/devkitARM + +# Prefix +PREFIX = $(DEVKITARM)/bin/arm-eabi- + +# Executables +CC = $(PREFIX)gcc +LD = $(PREFIX)gcc +STRIP = ../stripios/stripios + +# Flags +ARCH = -mcpu=arm926ej-s -mthumb -mthumb-interwork -mbig-endian +CFLAGS = $(ARCH) -I. -fomit-frame-pointer -Os -Wall +LDFLAGS = $(ARCH) -nostartfiles -Wl,-T,link.ld,-Map,$(TARGET).map -Wl,--gc-sections -Wl,-static + +# Libraries +LIBS = + +# Target +TARGET = dip-plugin + +# Objects +OBJS = dip.o \ + dip_calls.o \ + dma.o \ + file.o \ + ipc.o \ + main.o \ + patches.o \ + plugin.o \ + start.o \ + swi_mload.o \ + syscalls.o \ + tools.o \ + wbfs.o \ + usbstorage.o \ + sdhc.o \ + frag.o \ + + +$(TARGET).elf: $(OBJS) + @echo -e " LD\t$@" + @$(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.orig + @$(STRIP) $@.orig $@ + +%.o: %.s + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o: %.c + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -c -o $@ $< + +clean: + @echo -e "Cleaning..." + @rm -f $(OBJS) $(TARGET).elf $(TARGET).elf.orig $(TARGET).map + diff --git a/cios/dip_frag/dip.c b/cios/dip_frag/dip.c new file mode 100644 index 0000000..00ce1c0 --- /dev/null +++ b/cios/dip_frag/dip.c @@ -0,0 +1,188 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include + +#include "dip.h" +#include "dip_calls.h" +#include "dma.h" +#include "errno.h" +#include "ioctl.h" +#include "syscalls.h" + +/* Constants */ +#define MAX_READ_RETRIES 16 + + +s32 __DI_ReadA8(void *outbuf, u32 len, u32 offset) +{ + u32 dic[8]; + + /* Clear buffer */ + memset(dic, 0, sizeof(dic)); + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_READ_A8 << 24; + dic[1] = len; + dic[2] = offset; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + /* Call command */ + return DI_HandleCmd(dic, outbuf, len); +} + +s32 __DI_ReadD0(void *outbuf, u32 len, u32 lba) +{ + u32 cnt; + s32 ret = DIP_EIO; + + /* Do DVD read */ + for (cnt = 0; ret && (cnt < MAX_READ_RETRIES); cnt++) { + u32 dic[8]; + + /* Clear buffer */ + memset(dic, 0, sizeof(dic)); + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_READ_D0 << 24; + dic[1] = 0; + dic[2] = 0; + dic[3] = len >> 11; + dic[4] = lba; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + /* Call command */ + ret = DI_HandleCmd(dic, outbuf, len); + } + + return ret; +} + +s32 __DI_ReadFromSector(void *outbuf, u32 len, u32 pos, u32 lba) +{ + u8 *buf = NULL; + s32 ret; + + /* Check length */ + if ((len + pos) > SECTOR_SIZE) + return IPC_EINVAL; + + /* Allocate memory */ + buf = DI_Alloc(SECTOR_SIZE, 32); + if (!buf) + return IPC_ENOMEM; + + /* Read sector */ + ret = __DI_ReadD0(buf, SECTOR_SIZE, lba); + + /* Extract data */ + if (!ret) + memcpy(outbuf, buf + pos, len); + + /* Free memory */ + DI_Free(buf); + + return ret; +} + + +u32 DI_CustomCmd(void *inbuf, void *outbuf) +{ + u32 *diRegs = (u32 *)0x0D006000; + + /* Set registers */ + memcpy(diRegs, inbuf, 32); + + /* Wait */ + while (diRegs[7] & 1); + + /* Copy registers */ + if (outbuf) + memcpy(outbuf, diRegs, 32); + + return diRegs[8]; +} + +s32 DI_StopMotor(void) +{ + u32 dic[8]; + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_STOP_MOTOR << 24; + dic[1] = 0; + dic[2] = 0; + + /* Call command */ + return DI_HandleCmd(dic, NULL, 0); +} + +s32 DI_ReadDvd(u8 *outbuf, u32 len, u32 offset) +{ + u32 cnt, lba, size; + s32 ret = 0; + + /* Initial LBA */ + lba = offset >> 9; + + /* Do reads */ + for (cnt = 0; cnt < len; cnt += size) { + u32 dmasize, offlba, pos; + + /* Get offset/size */ + size = len - cnt; + offlba = lba << 9; + + /* Position in sector */ + pos = (offset > offlba) ? (offset - offlba) << 2 : 0; + + /* Check DMA range */ + dmasize = DMA_CheckRange(outbuf + cnt, size, SECTOR_SIZE); + + /* Use proper read method */ + if (!dmasize || pos) { + /* Check block size limit */ + if ((size + pos) > SECTOR_SIZE) + size = SECTOR_SIZE - pos; + + /* Read sector */ + ret = __DI_ReadFromSector(outbuf + cnt, size, pos, lba); + } else { + /* Set block size */ + size = (dmasize > MAX_SECTOR_SIZE) ? MAX_SECTOR_SIZE : dmasize; + + /* Read data */ + ret = __DI_ReadD0(outbuf + cnt, size, lba); + } + + /* Next LBA */ + lba += (size + pos) >> 11; + } + + return ret; +} + +s32 DI_ReadWod(void *outbuf, u32 len, u32 offset) +{ + /* Read data */ + return __DI_ReadA8(outbuf, len, offset); +} diff --git a/cios/dip_frag/dip.h b/cios/dip_frag/dip.h new file mode 100644 index 0000000..50b3a06 --- /dev/null +++ b/cios/dip_frag/dip.h @@ -0,0 +1,40 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DIP_H_ +#define _DIP_H_ + +#include "types.h" + +/* Disc lengths */ +#define DVD5_LENGTH 0x46090000 +#define DVD9_LENGTH 0x7ED38000 + +/* Disc sector */ +#define SECTOR_SIZE 0x800 +#define MAX_SECTOR_SIZE 0x7F8000 + + +/* Prototypes */ +u32 DI_CustomCmd(void *inbuf, void *outbuf); +s32 DI_StopMotor(void); +s32 DI_ReadDvd(u8 *outbuf, u32 len, u32 offset); +s32 DI_ReadWod(void *outbuf, u32 len, u32 offset); + +#endif diff --git a/cios/dip_frag/dip_calls.h b/cios/dip_frag/dip_calls.h new file mode 100644 index 0000000..d07c769 --- /dev/null +++ b/cios/dip_frag/dip_calls.h @@ -0,0 +1,35 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DIP_CALLS_H_ +#define _DIP_CALLS_H_ + +#include "types.h" + +/* Prototypes */ +s32 DI_ReadHash(void); +void *DI_Alloc(u32 size, u32 align); +void DI_Free(void *ptr); +void DI_Printf(const char *fmt, ...); + +/* DIP handlers */ +s32 DI_HandleIoctl(void *buffer, u32 fd); +s32 DI_HandleCmd(void *inbuf, const void *outbuf, u32 size); + +#endif diff --git a/cios/dip_frag/dip_calls.s b/cios/dip_frag/dip_calls.s new file mode 100644 index 0000000..a7e266d --- /dev/null +++ b/cios/dip_frag/dip_calls.s @@ -0,0 +1,104 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + + .align 4 + +/* + * DIP functions + */ + .code 32 + .global DI_ReadHash +DI_ReadHash: + stmfd sp!, {r7, lr} + ldr r7, =addr_readHash + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Alloc +DI_Alloc: + stmfd sp!, {r7, lr} + ldr r7, =addr_alloc + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Free +DI_Free: + stmfd sp!, {r7, lr} + ldr r7, =addr_free + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Printf +DI_Printf: + stmfd sp!, {r7, lr} + ldr r7, =addr_printf + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 +_call_di: + bx r7 + + +/* + * DIP handlers + */ + .code 16 + .thumb_func + + .global DI_HandleIoctl +DI_HandleIoctl: + push {r4-r7, lr} + mov r7, r10 + mov r6, r8 + push {r6, r7} + ldr r5, [r0] + mov r10, r1 + + ldr r3, =addr_handleIoctl + ldr r3, [r3] + bx r3 + + .code 16 + .thumb_func + + .global DI_HandleCmd +DI_HandleCmd: + push {r4-r7, lr} + mov r7, r11 + mov r6, r10 + mov r5, r9 + mov r4, r8 + push {r4-r7} + + ldr r3, =addr_handleCmd + ldr r3, [r3] + bx r3 diff --git a/cios/dip_frag/dma.c b/cios/dip_frag/dma.c new file mode 100644 index 0000000..f2aa751 --- /dev/null +++ b/cios/dip_frag/dma.c @@ -0,0 +1,55 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "types.h" + +/* DMA constants */ +#define DMA1_START_ADDRESS 0x00000000 +#define DMA1_END_ADDRESS 0x01800000 +#define DMA2_START_ADDRESS 0x10000000 +#define DMA2_END_ADDRESS 0x13618000 + + +s32 DMA_CheckRange(void *outbuf, u32 size, u32 alignment) +{ + u32 mem; + s32 ret = 0; + + /* Output buffer address */ + mem = (u32)outbuf; + + /* Check for memory alignment */ + if (!(mem & 31)) { + u32 dmalen = 0; + + /* DMA1 range check */ + if ((mem >= DMA1_START_ADDRESS) && (mem < DMA1_END_ADDRESS)) + dmalen = (DMA1_END_ADDRESS - mem); + + /* DMA2 range check */ + if ((mem >= DMA2_START_ADDRESS) && (mem < DMA2_END_ADDRESS)) + dmalen = (DMA2_END_ADDRESS - mem); + + if (dmalen >= alignment) + ret = (dmalen < size) ? dmalen : size; + ret -= (ret & (alignment - 1)); + } + + return ret; +} diff --git a/cios/dip_frag/dma.h b/cios/dip_frag/dma.h new file mode 100644 index 0000000..ae8b2b2 --- /dev/null +++ b/cios/dip_frag/dma.h @@ -0,0 +1,28 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DMA_H_ +#define _DMA_H_ + +#include "types.h" + +/* Prototypes */ +s32 DMA_CheckRange(void *outbuf, u32 size, u32 alignment); + +#endif diff --git a/cios/dip_frag/errno.h b/cios/dip_frag/errno.h new file mode 100644 index 0000000..3b8b943 --- /dev/null +++ b/cios/dip_frag/errno.h @@ -0,0 +1,30 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _ERRNO_H_ +#define _ERRNO_H_ + +/* Return codes */ +#define DIP_EIO 0xA000 + +/* Error codes */ +#define ERROR_BLOCK_RANGE 0x52100 +#define ERROR_WRONG_DISC 0x53100 + +#endif diff --git a/cios/dip_frag/file.c b/cios/dip_frag/file.c new file mode 100644 index 0000000..3bb83f1 --- /dev/null +++ b/cios/dip_frag/file.c @@ -0,0 +1,61 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "syscalls.h" + +/* Variables */ +static s32 fd = -1; + + +s32 File_Open(const char *path) +{ + /* Open file */ + fd = os_open(path, 0); + if (fd < 0) + return fd; + + return 0; +} + +void File_Close(void) +{ + /* Close file */ + if (fd >= 0) + os_close(fd); + + /* Reset descriptor */ + fd = -1; +} + +s32 File_Read(void *buffer, u32 len, u32 offset) +{ + s32 ret; + + /* Seek file */ + ret = os_seek(fd, offset << 2, 0); + if (ret < 0) + return ret; + + /* Read file */ + ret = os_read(fd, buffer, len); + if (ret < 0) + return ret; + + return 0; +} diff --git a/cios/dip_frag/file.h b/cios/dip_frag/file.h new file mode 100644 index 0000000..e9c9a38 --- /dev/null +++ b/cios/dip_frag/file.h @@ -0,0 +1,30 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _FILE_H_ +#define _FILE_H_ + +#include "types.h" + +/* Prototypes */ +s32 File_Open (const char *path); +void File_Close(void); +s32 File_Read (void *buffer, u32 len, u32 offset); + +#endif diff --git a/cios/dip_frag/frag.c b/cios/dip_frag/frag.c new file mode 100644 index 0000000..c92e46d --- /dev/null +++ b/cios/dip_frag/frag.c @@ -0,0 +1,263 @@ +/* + * DIP plugin for Custom IOS (FRAG mode) + * + * Copyright (C) 2010 Waninkoko, WiiGator, oggzee. + * + * This program is free software: you can 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 . + */ + +// frag list by oggzee + +#include + +#include "dip_calls.h" +#include "syscalls.h" +#include "usbstorage.h" +#include "sdhc.h" +#include "frag.h" + +#define DEV_NONE 0 +#define DEV_USB 1 +#define DEV_SDHC 2 + +#define MAX_IDX 640 // 640*16MB = 10GB +#define IDX_CHUNK 32768 // 16MB in sectors units: 16*1024*1024/512 +#define IDX_SHIFT 15 // 1<<15 = 32768 + +static u16 frag_idx[MAX_IDX] = { 0 }; +static u32 frag_init = 0; +static u32 frag_dev = 0; +static FragList fraglist_data = { 0 }; +static FragList *frag_list = 0; +static u8 sector_buf[512] __attribute__ ((aligned (32))); + +void optimize_frag_list(void); + +// NOTE: all local variables are declared static +// because stack size is rather limited + +// NOTE2: hmm, memcpy and char array writing seems +// to only work if length is word aligned... cache issue? + +s32 Frag_Init(u32 device, void *fraglist, int size) +{ + static int ret; + if (frag_init) Frag_Close(); + if (!device || device > 2) return -1; + if (!size) return -1; + if (size > sizeof(FragList)) return -2; + + if (device == DEV_USB) { + ret = usbstorage_Init(); + } else { + ret = sdhc_Init(); + } + if (ret) return ret; + frag_dev = device; + frag_list = &fraglist_data; + os_sync_before_read(fraglist, size); + memset(frag_list, 0, sizeof(FragList)); + memcpy(frag_list, fraglist, size); + os_sync_after_write(fraglist, size); + optimize_frag_list(); + frag_init = 1; + return size; +} + +void Frag_Close(void) +{ + frag_init = 0; + frag_list = 0; + frag_dev = 0; +} + + +void optimize_frag_list(void) +{ + int i; + u32 off; + u16 idx = 0; + for (i=0; i= frag_list->frag[idx].offset + frag_list->frag[idx].count) + && (idx + 1 < frag_list->num)) + { + idx++; + } + frag_idx[i] = idx; + } +} + +// in case a sparse block is requested, +// the returned poffset might not be equal to requested offset +// the difference should be filled with 0 +int frag_get(FragList *ff, u32 offset, u32 count, + u32 *poffset, u32 *psector, u32 *pcount) +{ + static int i; + static u32 delta; + static u32 idx_off; + static u32 start_idx; + + // optimize seek inside frag list + // jump to a precalculated index + idx_off = offset >> IDX_SHIFT; + if (idx_off > MAX_IDX) idx_off = MAX_IDX - 1; + start_idx = frag_idx[idx_off]; + + //printf("frag_get(%u %u)\n", offset, count); + for (i=start_idx; inum; i++) { + if (ff->frag[i].offset <= offset + && ff->frag[i].offset + ff->frag[i].count > offset) + { + delta = offset - ff->frag[i].offset; + *poffset = offset; + *psector = ff->frag[i].sector + delta; + *pcount = ff->frag[i].count - delta; + if (*pcount > count) *pcount = count; + goto out; + } + if (ff->frag[i].offset > offset + && ff->frag[i].offset < offset + count) + { + delta = ff->frag[i].offset - offset; + *poffset = ff->frag[i].offset; + *psector = ff->frag[i].sector; + *pcount = ff->frag[i].count; + count -= delta; + if (*pcount > count) *pcount = count; + goto out; + } + } + // not found + if (offset + count > ff->size) { + // error: out of range! + return -2; + } + // if inside range, then it must be just sparse, zero filled + // return empty block at the end of requested + *poffset = offset + count; + *psector = 0; + *pcount = 0; + out: + //printf("=>(%u %u %u)\n", *poffset, *psector, *pcount); + return 0; +} + + +// offset and len in sectors +int frag_read_sect(u32 offset, u8 *data, u32 len) +{ + static u32 off_ret; + static u32 sector; + static u32 count; + static u32 delta; + static int ret; + + while (len) { + //int frag_get(FragList *ff, u32 offset, u32 count, + // u32 *poffset, u32 *psector, u32 *pcount) + ret = frag_get(frag_list, offset, len, + &off_ret, §or, &count); + if (ret) return ret; // err + delta = off_ret - offset; + if (delta) { + // sparse block, fill with 0 + memset(data, 0, delta << 9); + offset += delta; + data += delta << 9; + len -= delta; + } + if (count) { + //int read_sector(void *ign, u32 lba, u32 count, void*buf) + //ret = read_sector(0, sector, count, data); + //if (ret) return ret; + //bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) + //ret = usbstorage_ReadSectors(sector, count, data); + if (frag_dev == DEV_USB) { + ret = __usbstorage_Read(sector, count, data); + } else { + ret = sdhc_Read(sector, count, data); + } + if (!ret) return -3; + offset += count; + data += count << 9; + len -= count; + } + if (delta + count == 0) { + // (should never happen) + return -4; + } + } + + return 0; +} + +// offset is pointing 32bit words to address the whole dvd, len is in bytes +int frag_read_partial(u32 offset, u8 *data, u32 len, u32 *read_len) +{ + static int ret; + static u32 off_sec; + static u32 mod; + static u32 rlen; + + off_sec = offset >> 7; // word to sect + mod = (offset & ((512-1) >> 2)) << 2; // offset from start of sector in bytes + rlen = 512 - mod; // remaining len from mod to end of sector + if (rlen > len) rlen = len; + if (rlen == 512) rlen = 0; // don't read whole sectors + if (rlen) { + ret = frag_read_sect(off_sec, sector_buf, 1); + if (ret) return ret; + memcpy(data, sector_buf + mod, rlen); + } + *read_len = rlen; + return 0; +} + +// woffset is pointing 32bit words to address the whole dvd, len is in bytes +s32 Frag_Read(void *data, u32 len, u32 woffset) +{ + static int ret; + static u32 rlen; + static u32 off_sec; + static u32 len_sec; + + // read leading partial non sector aligned data + ret = frag_read_partial(woffset, data, len, &rlen); + if (ret) return ret; + woffset += rlen >> 2; + data += rlen; + len -= rlen; + if (len >= 512) { + // read sector aligned data + off_sec = woffset >> 7; // word to sect + len_sec = len >> 9; // byte to sect + ret = frag_read_sect(off_sec, data, len_sec); + if (ret) return ret; + woffset += len_sec << 7; + data += len_sec << 9; + len -= len_sec << 9; + } + if (len) { + // read trailing partial non sector aligned data + ret = frag_read_partial(woffset, data, len, &rlen); + if (ret) return ret; + len -= rlen; + } + if (len) return -5; // should never happen + // success + return 0; +} + diff --git a/cios/dip_frag/frag.h b/cios/dip_frag/frag.h new file mode 100644 index 0000000..9355b4a --- /dev/null +++ b/cios/dip_frag/frag.h @@ -0,0 +1,33 @@ + +#define FRAG_MAX 20000 + +typedef struct +{ + u32 offset; // file offset, in sectors unit + u32 sector; + u32 count; +} Fragment; + +typedef struct +{ + u32 size; // num sectors + u32 num; // num fragments + u32 maxnum; + Fragment frag[FRAG_MAX]; +} FragList; + +int set_frag_list(FragList *p, int size); + +// in case a sparse block is requested, +// the returned poffset might not be equal to requested offset +// the difference should be filled with 0 +int frag_get(FragList *ff, u32 offset, u32 count, + u32 *poffset, u32 *psector, u32 *pcount); + +// woffset is pointing 32bit words to address the whole dvd, len is in bytes +int frag_read(u32 woffset, u8 *data, u32 len); + +s32 Frag_Init(u32 device, void *fraglist, int size); +void Frag_Close(void); +s32 Frag_Read(void *data, u32 len, u32 woffset); + diff --git a/cios/dip_frag/ioctl.h b/cios/dip_frag/ioctl.h new file mode 100644 index 0000000..2d68563 --- /dev/null +++ b/cios/dip_frag/ioctl.h @@ -0,0 +1,62 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _IOCTL_H_ +#define _IOCTL_H_ + +/* IOCTL commands */ +#define IOCTL_DI_LOW_READ 0x71 +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_WAITCVRCLOSE 0x79 +#define IOCTL_DI_COVER_REG 0x7A +#define IOCTL_DI_COVER_CLEAR 0x86 +#define IOCTL_DI_COVER_STATUS 0x88 +#define IOCTL_DI_COVER_SET 0x89 +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_STATUS_REG 0x95 +#define IOCTL_DI_REPORT_KEY 0xA4 +#define IOCTL_DI_READ_A8 0xA8 +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_READ_D0 0xD0 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_READBCA 0xDA +#define IOCTL_DI_REQCOVER 0xDB +#define IOCTL_DI_REQERROR 0xE0 +#define IOCTL_DI_STOP_MOTOR 0xE3 +#define IOCTL_DI_AUDIO_CONFIG 0xE4 + +/* Custom commands */ +#define IOCTL_DI_OFFSET_SET 0xF0 +#define IOCTL_DI_OFFSET_GET 0xF1 +#define IOCTL_DI_CRYPT_SET 0xF2 +#define IOCTL_DI_CRYPT_GET 0xF3 +#define IOCTL_DI_WBFS_SET 0xF4 +#define IOCTL_DI_WBFS_GET 0xF5 +#define IOCTL_DI_RESET_DISABLE 0xF6 +#define IOCTL_DI_FILE_SET 0xF7 +#define IOCTL_DI_FILE_GET 0xF8 +#define IOCTL_DI_FRAG_SET 0xF9 +#define IOCTL_DI_MODE_GET 0xFA +#define IOCTL_DI_HELLO 0xFB +#define IOCTL_DI_CUSTOMCMD 0xFF + +#endif + + diff --git a/cios/dip_frag/ipc.c b/cios/dip_frag/ipc.c new file mode 100644 index 0000000..f9c361f --- /dev/null +++ b/cios/dip_frag/ipc.c @@ -0,0 +1,30 @@ +#include "ipc.h" +#include "syscalls.h" +#include "types.h" + + +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = 0; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Invalidate cache */ + os_sync_before_read(buffer, len); + } +} + +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = inlen; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Flush cache */ + os_sync_after_write(buffer, len); + } +} diff --git a/cios/dip_frag/ipc.h b/cios/dip_frag/ipc.h new file mode 100644 index 0000000..c6b39d2 --- /dev/null +++ b/cios/dip_frag/ipc.h @@ -0,0 +1,44 @@ +#ifndef _IPC_H_ +#define _IPC_H_ + +#include "types.h" + +/* IPC error codes */ +#define IPC_ENOENT -6 +#define IPC_ENOMEM -22 +#define IPC_EINVAL -101 +#define IPC_EACCESS -102 +#define IPC_EEXIST -105 +#define IPC_NOENT -106 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTLV vector */ +typedef struct iovec { + void *data; + u32 len; +} ioctlv; + +/* IOCTL structure */ +typedef struct { + u32 command; + + u32 *inbuf; + u32 inlen; + u32 *iobuf; + u32 iolen; +} ioctl; + + +/* Prototypes */ +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen); +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen); + +#endif diff --git a/cios/dip_frag/link.ld b/cios/dip_frag/link.ld new file mode 100644 index 0000000..70c43a5 --- /dev/null +++ b/cios/dip_frag/link.ld @@ -0,0 +1,70 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +/* Sections area */ +/* +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x13800000, LENGTH = 0x1000 + ram(rw) : ORIGIN = 0x13801000, LENGTH = 0x3000 +} + +__exe_start_phys__ = 0x13800000; +__ram_start_phys__ = 0x13801000; +*/ + +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x13700000, LENGTH = 0x2000 + ram(rw) : ORIGIN = 0x13702000, LENGTH = 0x40000 +} + +__exe_start_phys__ = 0x13700000; +__ram_start_phys__ = 0x13702000; + +SECTIONS { + .ios_info_table : { + KEEP (*(.ios_info_table)) + } > table + + .init : AT (__exe_start_phys__) { + *(.init) + . = ALIGN(4); + } > exe + + .text : { + *(.text*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } > exe + + .data : AT (__ram_start_phys__) { + *(.data*) + *(.data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(4); + } > ram + + .rodata : { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .bss : { + *(.dynsbss) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(COMMON) + KEEP(*(.ios_bss)) + . = ALIGN(4); + } > ram +} diff --git a/cios/dip_frag/main.c b/cios/dip_frag/main.c new file mode 100644 index 0000000..9da1157 --- /dev/null +++ b/cios/dip_frag/main.c @@ -0,0 +1,71 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "main.h" +#include "patches.h" +#include "swi_mload.h" +#include "syscalls.h" +#include "tools.h" +#include "types.h" +#include "wbfs.h" + +/* IOS information */ +iosInfo ios = { 0 }; + + +s32 __DI_System(u32 arg1, u32 arg2) +{ + u32 perms; + + /* Invalidate cache */ + ICInvalidate(); + + /* Apply permissions */ + perms = Perms_Read(); + Perms_Write(0xFFFFFFFF); + + /* Patch modules */ + Patch_DipModule(ios.dipVersion); + + /* Restore permissions */ + Perms_Write(perms); + + return 0; +} + +s32 __DI_Initialize(void) +{ + /* Get IOS info */ + Swi_GetIosInfo(&ios); + + /* Prepare system */ + Swi_CallFunc((void *)__DI_System, NULL, NULL); + + return 0; +} + + +int main(void) +{ + /* Print info */ + write("$IOSVersion: DIPP: " __DATE__ " " __TIME__ " 64M$\n"); + + /* Initialize plugin */ + return __DI_Initialize(); +} diff --git a/cios/dip_frag/main.h b/cios/dip_frag/main.h new file mode 100644 index 0000000..2a7407f --- /dev/null +++ b/cios/dip_frag/main.h @@ -0,0 +1,40 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include "types.h" + +/* IOS info structure */ +typedef struct { + /* Syscall base */ + u32 syscall; + + /* Module versions */ + u32 dipVersion; + u32 esVersion; + u32 ffsVersion; + u32 iopVersion; +} iosInfo; + +/* Externs */ +extern iosInfo ios; + +#endif diff --git a/cios/dip_frag/patches.c b/cios/dip_frag/patches.c new file mode 100644 index 0000000..4f9de31 --- /dev/null +++ b/cios/dip_frag/patches.c @@ -0,0 +1,115 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "plugin.h" +#include "tools.h" + +/* Macros */ +#define Write8(addr, val) \ + *(u8 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u8)); + +#define Write16(addr, val) \ + *(u16 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u16)); + +#define Write32(addr, val) \ + *(u32 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u32)); + + +/* Addresses */ +u32 addr_handleIoctl = 0; +u32 addr_handleCmd = 0; +u32 addr_alloc = 0; +u32 addr_free = 0; +u32 addr_readHash = 0; +u32 addr_printf = 0; +u8 *dip_readctrl = 0; + + +void Patch_DipModule(u32 version) +{ + switch (version) { + /** 07/11/08 14:34:26 **/ + case 0x48776F72: + /* Patch IOCTL handler */ + Write32(0x20200400, 0x4B004718); + Write32(0x20200404, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200EF8, 0x4B004718); + Write32(0x20200EFC, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202A70 + 1; + addr_handleIoctl = 0x2020040C + 1; + addr_handleCmd = 0x20200F04 + 1; + addr_alloc = 0x2020096C + 1; + addr_free = 0x2020093C + 1; + addr_printf = 0x2020387C + 1; + dip_readctrl = (u8 *)0x2022DD60; + + break; + + /** 07/24/08 20:08:44 **/ + case 0x4888E14C: + /* Patch IOCTL handler */ + Write32(0x202003B8, 0x4B004718); + Write32(0x202003BC, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200D2C, 0x4B004718); + Write32(0x20200D30, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202874 + 1; + addr_handleIoctl = 0x202003C4 + 1; + addr_handleCmd = 0x20200D38 + 1; + addr_alloc = 0x202008C4 + 1; + addr_free = 0x20200898 + 1; + addr_printf = 0x2020365C + 1; + dip_readctrl = (u8 *)0x2022CDAC; + + break; + + /** 11/24/08 15:39:09 **/ + /** 06/03/09 07:49:09 **/ + case 0x492ACA9D: + case 0x4A262AF5: + /* Patch IOCTL handler */ + Write32(0x20200400, 0x4B004718); + Write32(0x20200404, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200EF8, 0x4B004718); + Write32(0x20200EFC, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202944 + 1; + addr_handleIoctl = 0x2020040C + 1; + addr_handleCmd = 0x20200F04 + 1; + addr_alloc = 0x2020096C + 1; + addr_free = 0x2020093C + 1; + addr_printf = 0x20203750 + 1; + dip_readctrl = (u8 *)0x2022CD60; + + break; + } +} diff --git a/cios/dip_frag/patches.h b/cios/dip_frag/patches.h new file mode 100644 index 0000000..6a45867 --- /dev/null +++ b/cios/dip_frag/patches.h @@ -0,0 +1,29 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _PATCHES_H_ +#define _PATCHES_H_ + +#include "types.h" + +/* Prototypes */ +void Patch_DipModule(u32 version); + +#endif + diff --git a/cios/dip_frag/plugin.c b/cios/dip_frag/plugin.c new file mode 100644 index 0000000..2f16f64 --- /dev/null +++ b/cios/dip_frag/plugin.c @@ -0,0 +1,618 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "dip.h" +#include "dip_calls.h" +#include "errno.h" +#include "file.h" +#include "ioctl.h" +#include "plugin.h" +#include "syscalls.h" +#include "wbfs.h" +#include "frag.h" +#include "string.h" + +/* Global config */ +struct dipConfig config = { 0 }; + + +s32 __DI_CheckOffset(u32 offset) +{ + u32 offmax; + + /* Check disc type */ + switch (config.type) { + /* Single layer */ + case DISC_DVD5: + offmax = DVD5_LENGTH; + break; + + /* Dual layer */ + case DISC_DVD9: + offmax = DVD9_LENGTH; + break; + + default: + return 0; + } + + /* Check offset */ + if (offset >= offmax) { + /* Set error */ + config.error = ERROR_BLOCK_RANGE; + + /* I/O error */ + return DIP_EIO; + } + + return 0; +} + +s32 __DI_ReadUnencrypted(void *outbuf, u32 len, u32 offset) +{ + s32 ret; + + /* Check offset */ + ret = __DI_CheckOffset(offset); + if (ret) + return ret; + + /* Update offset */ + offset += (config.offset[0] + config.offset[1]); + + /* Frag read */ + if (DI_ChkMode(MODE_FRAG)) { + ret = Frag_Read(outbuf, len, offset); + return ret; + } + + /* File read */ + if (DI_ChkMode(MODE_FILE)) + return File_Read(outbuf, len, offset); + + /* WBFS read */ + if (DI_ChkMode(MODE_WBFS)) + return WBFS_Read(outbuf, len, offset); + + /* DVD read */ + if (DI_ChkMode(MODE_DVDROM)) + return DI_ReadDvd(outbuf, len, offset); + + /* WOD read */ + return DI_ReadWod(outbuf, len, offset); +} + +s32 __DI_ReadDiscId(u32 *outbuf, u32 len) +{ + s32 ret; + + /* Read ID (first sector) */ + ret = __DI_ReadUnencrypted(outbuf, len, 0); + if (ret < 0) + return ret; + + /* Check WOD magic word */ + if (outbuf[6] == WOD_MAGIC) { + extern u8 *dip_readctrl; + + /* Set read control */ + dip_readctrl[0] = 1; + + /* Read hash */ + if (!dip_readctrl[1]) + ret = DI_ReadHash(); + } + + return ret; +} + +void __DI_CheckDisc(void) +{ + void *buffer; + s32 ret; + + /* Allocate buffer */ + buffer = DI_Alloc(SECTOR_SIZE, 32); + if (!buffer) + return; + + /* Read second layer */ + ret = __DI_ReadUnencrypted(buffer, SECTOR_SIZE, 0x50000000); + + /* Set disc type */ + config.type = (!ret) ? DISC_DVD9 : DISC_DVD5; + + /* Free buffer */ + DI_Free(buffer); +} + +void __DI_ResetConfig(void) +{ + /* Reset modes */ + DI_DelMode(MODE_CRYPT); + DI_DelMode(MODE_DVDROM); + + /* Reset offsets */ + config.offset[0] = 0; + config.offset[1] = 0; + + /* Reset variables */ + config.type = 0; + config.error = 0; + config.cover = 0; + config.noreset = 0; +} + +s32 DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 size) +{ + u32 cmd = (inbuf[0] >> 24); + + s32 res; + s32 ret = 0; + + /* Reset error */ + if (cmd != IOCTL_DI_REQERROR) + config.error = 0; + + switch(cmd) { + /** Reset drive **/ + case IOCTL_DI_RESET: { + /* Check reset flag */ + if (!config.noreset) { + /* Reset DIP config */ + __DI_ResetConfig(); + + /* Non-DVD mode */ + if (DI_ChkMode(MODE_EMUL)) { + /* Stop motor */ + DI_StopMotor(); + + /* Set cover register */ + BIT_SET(config.cover, 4); + } else { + /* Reset drive */ + ret = DI_HandleCmd(inbuf, outbuf, size); + } + } + + break; + } + + /** Read disc ID **/ + case IOCTL_DI_READID: { + u32 offset = (config.offset[0] | config.offset[1]); + + /* Read disc ID */ + if (!DI_ChkMode(MODE_EMUL)) { + /* Call command */ + ret = DI_HandleCmd(inbuf, outbuf, size); + + /* Set DVD mode */ + if (ret) + DI_SetMode(MODE_DVDROM); + } + + /* Manual read */ + if (DI_ChkMode(MODE_DVDROM | MODE_EMUL) || offset) + ret = __DI_ReadDiscId(outbuf, size); + + /* Check disc type */ + if (!ret) + __DI_CheckDisc(); + + break; + } + + /** Encrypted disc read **/ + case IOCTL_DI_LOW_READ: { + /* Crypted read */ + if (DI_ChkMode(MODE_CRYPT)) { + u32 len = inbuf[1]; + u32 offset = inbuf[2]; + + /* Do unencrypted read */ + ret = __DI_ReadUnencrypted(outbuf, len, offset); + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Unencrypted disc read **/ + case IOCTL_DI_UNENCREAD: + case IOCTL_DI_READ_A8: + case IOCTL_DI_READ_D0: { + u32 len = inbuf[1]; + u32 offset = inbuf[2]; + + /* Change values unit */ + if (cmd == IOCTL_DI_READ_D0) { + len <<= 11; + offset <<= 9; + } + + /* Unencrypted read */ + ret = __DI_ReadUnencrypted(outbuf, len, offset); + + break; + } + + /** Disc BCA read **/ + case IOCTL_DI_READBCA: { + /* Read disc BCA */ + ret = __DI_ReadUnencrypted(outbuf, size, 0x40); + + break; + } + + /** Set drive offset **/ + case IOCTL_DI_OFFSET: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_EMUL); + + /* Set disc offset */ + if (res) { + /* Calculate offset */ + u32 offset = (inbuf[1] << 30) | inbuf[2]; + + /* Set drive offset */ + config.offset[1] = (offset & -0x8000); + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Seek disc **/ + case IOCTL_DI_SEEK: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_EMUL); + + /* Seek disc */ + if (!res) + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Audio config **/ + case IOCTL_DI_AUDIO_CONFIG: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_EMUL); + + /* Set audio config */ + if (!res) + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Report DVD key **/ + case IOCTL_DI_REPORT_KEY: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_EMUL); + + /* Report DVD key */ + if (res) { + /* Wrong disc */ + config.error = ERROR_WRONG_DISC; + + /* I/O error */ + ret = DIP_EIO; + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Request cover status **/ + case IOCTL_DI_REQCOVER: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Request cover status */ + if (res) + *outbuf = 0; + else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Request error code **/ + case IOCTL_DI_REQERROR: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Request error code */ + if (res || config.error) + *outbuf = config.error; + else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Set offset base **/ + case IOCTL_DI_OFFSET_SET: { + u32 offset = inbuf[1]; + + /* Set base offset */ + config.offset[0] = offset; + + break; + } + + /** Get offset base **/ + case IOCTL_DI_OFFSET_GET: { + /* Return offset base */ + *outbuf = config.offset[0]; + + break; + } + + /** Set crypt mode **/ + case IOCTL_DI_CRYPT_SET: { + u32 mode = inbuf[1]; + + /* Enable crypt mode */ + if (mode) + DI_SetMode(MODE_CRYPT); + else + DI_DelMode(MODE_CRYPT); + + break; + } + + /** Get crypt mode **/ + case IOCTL_DI_CRYPT_GET: { + /* Check crypt bit */ + *outbuf = DI_ChkMode(MODE_CRYPT); + + break; + } + + /** Set WBFS mode **/ + case IOCTL_DI_WBFS_SET: { + u32 device = inbuf[1]; + + /* Close WBFS */ + WBFS_Close(); + + /* Disable mode */ + DI_DelMode(MODE_WBFS); + + /* Check device */ + if (device) { + u8 *discid = (u8 *)&inbuf[2]; + + /* Open device */ + ret = WBFS_Open(device-1, discid); + + /* Enable mode */ + if (!ret) + DI_SetMode(MODE_WBFS); + } + + break; + } + + /** Get WBFS mode **/ + case IOCTL_DI_WBFS_GET: { + /* Check WBFS bit */ + *outbuf = DI_ChkMode(MODE_WBFS); + + break; + } + + /** Set file mode **/ + case IOCTL_DI_FILE_SET: { + char *filename = (char *)inbuf[1]; + + /* Close file */ + File_Close(); + + /* Disable mode */ + DI_DelMode(MODE_FILE); + + /* Check flag */ + if (filename) { + /* Convert address */ + filename = VirtToPhys(filename); + + /* Open file */ + ret = File_Open(filename); + + /* Enable mode */ + if (!ret) + DI_SetMode(MODE_FILE); + } + + break; + } + + /** Get file mode **/ + case IOCTL_DI_FILE_GET: { + /* Check file bit */ + *outbuf = DI_ChkMode(MODE_FILE); + + break; + } + + /** Set FRAG mode **/ + case IOCTL_DI_FRAG_SET: { + u32 device = inbuf[1]; + void *fraglist = (void*)inbuf[2]; + int size = inbuf[3]; + + /* Close frag */ + Frag_Close(); + + /* Disable mode */ + DI_DelMode(MODE_FRAG); + + /* Check device */ + if (device && fraglist && size) { + /* Convert address */ + fraglist = VirtToPhys(fraglist); + + /* Open device */ + ret = Frag_Init(device, fraglist, size); + *outbuf = ret; + + /* Enable mode */ + if (ret > 0) + DI_SetMode(MODE_FRAG); + + ret = 0; + } + break; + } + + /** Get IO mode **/ + case IOCTL_DI_MODE_GET: { + /* return all mode bits */ + *outbuf = config.mode; + break; + } + + /** debug stuff **/ + case IOCTL_DI_HELLO: { + memcpy(outbuf,"HELO",4); + outbuf[1] = config.mode; + outbuf[2] = config.type; + break; + } + + /** Disable reset **/ + case IOCTL_DI_RESET_DISABLE: { + u32 value = inbuf[1]; + + /* Disable reset */ + config.noreset = value; + + break; + } + + /** Send custom DVD command **/ + case IOCTL_DI_CUSTOMCMD: { + void *buffer = (void *)inbuf[1]; + + /* Convert address to physical */ + buffer = VirtToPhys(buffer); + + /* Send custom DI command */ + ret = DI_CustomCmd(buffer, outbuf); + + break; + } + + default: + /* Call command */ + ret = DI_HandleCmd(inbuf, outbuf, size); + } + + return ret; +} + +s32 DI_EmulateIoctl(ioctl *buffer, s32 fd) +{ + u32 *outbuf = buffer->iobuf; + u32 cmd = buffer->command; + + s32 res; + s32 ret = 1; + + /* Parse command */ + switch (cmd) { + /** Wait for cover close **/ + case IOCTL_DI_WAITCVRCLOSE: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Wait for cover close */ + if (!res) + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get cover register **/ + case IOCTL_DI_COVER_REG: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Get cover register */ + if (res) + *outbuf = config.cover; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Clear cover interrupt **/ + case IOCTL_DI_COVER_CLEAR: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Clear cover interrupt */ + if (res) + BIT_DEL(config.cover, 4); + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get cover status **/ + case IOCTL_DI_COVER_STATUS: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Get cover status */ + if (res) + *outbuf = 0x02; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get status register **/ + case IOCTL_DI_STATUS_REG: { + /* Check modes */ + res = DI_ChkMode(MODE_EMUL); + + /* Get status register */ + if (res) + *outbuf = 0x0A; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + default: + /* Call IOCTL */ + ret = DI_HandleIoctl(buffer, fd); + } + + return ret; +} diff --git a/cios/dip_frag/plugin.h b/cios/dip_frag/plugin.h new file mode 100644 index 0000000..38c8911 --- /dev/null +++ b/cios/dip_frag/plugin.h @@ -0,0 +1,77 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +#include "ipc.h" +#include "tools.h" +#include "types.h" + +/* WOD magic word */ +#define WOD_MAGIC 0x5D1C9EA3 + +/* Mode codes */ +#define MODE_DVDROM 0x01 +#define MODE_CRYPT 0x02 +#define MODE_WBFS 0x04 +#define MODE_FILE 0x08 +#define MODE_FRAG 0x10 +#define MODE_EMUL ( MODE_FILE | MODE_WBFS | MODE_FRAG ) + +/* Macros */ +#define DI_SetMode(bit) BIT_SET(config.mode, (bit)) +#define DI_DelMode(bit) BIT_DEL(config.mode, (bit)) +#define DI_ChkMode(bit) BIT_CHK(config.mode, (bit)) + +/* Disc types */ +enum { + DISC_UNKNOWN = 0, + DISC_DVD5, + DISC_DVD9, +}; + + +/* Config structure */ +struct dipConfig { + /* Modes */ + u32 mode; + + /* Type */ + u32 type; + + /* Offsets */ + u32 offset[2]; + + /* Last error */ + u32 error; + + /* Misc variables */ + u32 cover; + u32 noreset; +}; + +/* Prototypes */ +s32 DI_EmulateIoctl(ioctl *buffer, s32 fd); +s32 DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 size); + +/* Extern */ +extern struct dipConfig config; + +#endif diff --git a/cios/dip_frag/sdhc.c b/cios/dip_frag/sdhc.c new file mode 100644 index 0000000..88eb2f4 --- /dev/null +++ b/cios/dip_frag/sdhc.c @@ -0,0 +1,99 @@ + +#include "syscalls.h" +#include "types.h" +#include "sdhc.h" + +#include +#include + +/* IOCTL commands */ +#define IOCTL_SDHC_INIT 0x01 +#define IOCTL_SDHC_READ 0x02 +#define IOCTL_SDHC_WRITE 0x03 +#define IOCTL_SDHC_ISINSERTED 0x04 + +/* Constants */ +#define SDHC_SECTOR_SIZE 0x200 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/sdhc"; +static ioctlv io_vector[3] ATTRIBUTE_ALIGN(32); +static u32 io_buffer[3] ATTRIBUTE_ALIGN(32); +static s32 fd = -1; +static u32 sectorSz = SDHC_SECTOR_SIZE; + + +int sdhc_Init(void) +{ + //s32 ret; + + /* Already open */ + if (fd >= 0) + return 0; + + /* Open USB device */ + fd = os_open(fs, 1); + if (fd < 0) + return -11; + + /* Initialize USB storage */ + os_ioctlv(fd, IOCTL_SDHC_INIT, 0, 0, NULL); + + sectorSz = SDHC_SECTOR_SIZE; + + return 0; +} + + +bool sdhc_Read(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + io_buffer[0] = sector; + io_buffer[1] = numSectors; + + /* Setup io_vector */ + io_vector[0].data = &io_buffer[0]; + io_vector[0].len = sizeof(u32); + io_vector[1].data = &io_buffer[1]; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = (sectorSz * numSectors); + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Read data */ + ret = os_ioctlv(fd, IOCTL_SDHC_READ, 2, 1, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} + + +bool sdhc_Shutdown(void) +{ + if (fd >= 0) { + /* Close USB device */ + os_close(fd); + /* Remove descriptor */ + fd = -1; + } + return true; +} + + diff --git a/cios/dip_frag/sdhc.h b/cios/dip_frag/sdhc.h new file mode 100644 index 0000000..0f2abf0 --- /dev/null +++ b/cios/dip_frag/sdhc.h @@ -0,0 +1,11 @@ +#ifndef _SDHC_H_ +#define _SDHC_H_ + +#include "types.h" + +/* Prototypes */ +int sdhc_Init(void); +bool sdhc_Read(u32 sector, u32 numSectors, void *buffer); + +#endif + diff --git a/cios/dip_frag/start.s b/cios/dip_frag/start.s new file mode 100644 index 0000000..6a7d939 --- /dev/null +++ b/cios/dip_frag/start.s @@ -0,0 +1,71 @@ +/* + Custom IOS module for Wii. + Copyright (C) 2008 neimod. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .arm + + .EQU ios_thread_arg, 4 + .EQU ios_thread_priority, 0x48 + .EQU ios_thread_stacksize, 0x1000 + + + .global _start +_start: + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bx r3 + + + +/* + * IOS bss + */ + .section ".ios_bss", "a", %nobits + + .space ios_thread_stacksize + .global ios_thread_stack /* stack decrements from high address.. */ +ios_thread_stack: + + +/* + * IOS info table + */ + .section ".ios_info_table", "ax", %progbits + + .global ios_info_table +ios_info_table: + .long 0x0 + .long 0x28 @ numentries * 0x28 + .long 0x6 + + .long 0xB + .long ios_thread_arg @ passed to thread entry func, maybe module id + + .long 0x9 + .long _start + + .long 0x7D + .long ios_thread_priority + + .long 0x7E + .long ios_thread_stacksize + + .long 0x7F + .long ios_thread_stack diff --git a/cios/dip_frag/swi_mload.c b/cios/dip_frag/swi_mload.c new file mode 100644 index 0000000..1f6bbae --- /dev/null +++ b/cios/dip_frag/swi_mload.c @@ -0,0 +1,58 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "tools.h" +#include "types.h" + + +void Swi_Memcpy(void *dst, void *src, s32 len) +{ + /* Wrong length */ + if (len <= 0) + return; + + /* Call function */ + Swi_MLoad(2, (u32)dst, (u32)src, (u32)len); +} + +void Swi_uMemcpy(void *dst, void *src, s32 len) +{ + /* Wrong length */ + if (len <= 0) + return; + + /* Call function */ + Swi_MLoad(9, (u32)dst, (u32)src, (u32)len); +} + +s32 Swi_CallFunc(s32 (*func)(void *in, void *out), void *in, void *out) +{ + /* Call function */ + return Swi_MLoad(16, (u32)func, (u32)in, (u32)out); +} + +u32 Swi_GetSyscallBase(void) +{ + return Swi_MLoad(17, 0, 0, 0); +} + +u32 Swi_GetIosInfo(void *buffer) +{ + return Swi_MLoad(18, (u32)buffer, 0, 0); +} diff --git a/cios/dip_frag/swi_mload.h b/cios/dip_frag/swi_mload.h new file mode 100644 index 0000000..07f7c6a --- /dev/null +++ b/cios/dip_frag/swi_mload.h @@ -0,0 +1,32 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _SWI_MLOAD_H_ +#define _SWI_MLOAD_H_ + +#include "types.h" + +/* Prototypes */ +void Swi_Memcpy(void *dst, void *src, s32 len); +void Swi_uMemcpy(void *dst, void *src, s32 len); +s32 Swi_CallFunc(s32 (*func)(void *in, void *out), void *in, void *out); +u32 Swi_GetSyscallBase(void); +u32 Swi_GetIosInfo(void *buffer); + +#endif diff --git a/cios/dip_frag/syscalls.h b/cios/dip_frag/syscalls.h new file mode 100644 index 0000000..f567139 --- /dev/null +++ b/cios/dip_frag/syscalls.h @@ -0,0 +1,69 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "ipc.h" +#include "types.h" + +/* IOS syscalls */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +s32 os_open(const char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, ioctlv *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* ARM syscalls */ +void write(const char *str); + +#endif diff --git a/cios/dip_frag/syscalls.s b/cios/dip_frag/syscalls.s new file mode 100644 index 0000000..6a29633 --- /dev/null +++ b/cios/dip_frag/syscalls.s @@ -0,0 +1,281 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + +.macro syscall vec_sys + .long 0xE6000010 + (\vec_sys<<5) + bx lr +.endm + + +/* + * IOS syscalls + */ + .code 32 + .global os_thread_create +os_thread_create: + syscall 0x0 + + .code 32 + .global os_thread_joint +os_thread_joint: + syscall 0x1 + + .code 32 + .global os_thread_cancel +os_thread_cancel: + syscall 0x2 + + .code 32 + .global os_get_thread_id +os_get_thread_id: + syscall 0x3 + + .code 32 + .global os_get_parent_thread_id +os_get_parent_thread_id: + syscall 0x4 + + .code 32 + .global os_thread_continue +os_thread_continue: + syscall 0x5 + + .code 32 + .global os_thread_stop +os_thread_stop: + syscall 0x6 + + .code 32 + .global os_thread_yield +os_thread_yiel: + syscall 0x7 + + .code 32 + .global os_thread_get_priority +os_thread_get_priority : + syscall 0x8 + + .code 32 + .global os_thread_set_priority +os_thread_set_priority: + syscall 0x9 + + .code 32 + .global os_message_queue_create +os_message_queue_create: + syscall 0xa + + .code 32 + .global os_message_queue_destroy +os_message_queue_destroy: + syscall 0xb + + .code 32 + .global os_message_queue_send +os_message_queue_send: + syscall 0xc + + .code 32 + .global os_message_queue_send_now +os_message_queue_send_now: + syscall 0xd + + .code 32 + .global os_message_queue_receive +os_message_queue_receive: + syscall 0xe + + .code 32 + .global os_register_event_handler +os_register_event_handler: + syscall 0xf + + .code 32 + .global os_unregister_event_handler +os_unregister_event_handler: + syscall 0x10 + + .code 32 + .global os_create_timer +os_create_timer: + syscall 0x11 + + .code 32 + .global os_restart_timer +os_restart_timer: + syscall 0x12 + + .code 32 + .global os_stop_timer +os_stop_timer: + syscall 0x13 + + .code 32 + .global os_destroy_timer +os_destroy_timer: + syscall 0x14 + + .code 32 + .global os_timer_now +os_timer_now: + syscall 0x15 + + .code 32 + .global os_heap_create +os_heap_create: + syscall 0x16 + + .code 32 + .global os_heap_destroy +os_heap_destroy: + syscall 0x17 + + .code 32 + .global os_heap_alloc +os_heap_alloc: + syscall 0x18 + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_device_register +os_device_register: + syscall 0x1b + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + .code 32 + .global os_ioctl +os_ioctl: + syscall 0x21 + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global os_open_async +os_open_async: + syscall 0x23 + + .code 32 + .global os_close_async +os_close_async: + syscall 0x24 + + .code 32 + .global os_read_async +os_read_async: + syscall 0x25 + + .code 32 + .global os_write_async +os_write_async: + syscall 0x26 + + .code 32 + .global os_seek_async +os_seek_async: + syscall 0x27 + + .code 32 + .global os_ioctl_async +os_ioctl_async: + syscall 0x28 + + .code 32 + .global os_ioctlv_async +os_ioctlv_async: + syscall 0x29 + + .code 32 + .global os_message_queue_ack +os_message_queue_ack: + syscall 0x2a + + .code 32 + .global os_software_IRQ +os_software_IRQ: + syscall 0x34 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3f + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_virt_to_phys +os_virt_to_phys: + syscall 0x4f + + .code 32 + .global os_syscall_50 +os_syscall_50: + syscall 0x50 + + +/* + * ARM syscalls + */ + .code 32 + .global write +write: + mov r2, lr + adds r1, r0, #0 + movs r0, #4 + svc 0xab + bx r2 diff --git a/cios/dip_frag/tools.h b/cios/dip_frag/tools.h new file mode 100644 index 0000000..eba35c4 --- /dev/null +++ b/cios/dip_frag/tools.h @@ -0,0 +1,51 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _TOOLS_H_ +#define _TOOLS_H_ + +#include "types.h" + +/* Macros */ +#define BIT_SET(x, y) (x |= y) +#define BIT_DEL(x, y) (x &= ~y) +#define BIT_CHK(x, y) (x & y) + + +/* Direct syscalls */ +void DCInvalidateRange(void* ptr, int size); +void DCFlushRange(void* ptr, int size); +void ICInvalidate(void); + +/* MLoad syscalls */ +s32 Swi_MLoad(u32 arg0, u32 arg1, u32 arg2, u32 arg3); + +/* ARM permissions */ +u32 Perms_Read(void); +void Perms_Write(u32 flags); + +/* MEM2 routines */ +void MEM2_Prot(u32 flag); + +/* Tools */ +void *VirtToPhys(void *address); +void *PhysToVirt(void *address); + +#endif + diff --git a/cios/dip_frag/tools.s b/cios/dip_frag/tools.s new file mode 100644 index 0000000..7f06838 --- /dev/null +++ b/cios/dip_frag/tools.s @@ -0,0 +1,85 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + .text + + .align 4 + .code 32 + +/* Direct syscalls */ + .global DCInvalidateRange +DCInvalidateRange: + mcr p15, 0, r0, c7, c6, 1 + add r0, #0x20 + subs r1, #1 + bne DCInvalidateRange + bx lr + + .global DCFlushRange +DCFlushRange: + mcr p15, 0, r0, c7, c10, 1 + add r0, #0x20 + subs r1, #1 + bne DCFlushRange + bx lr + + .global ICInvalidate +ICInvalidate: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + + +/* MLoad syscalls */ + .global Swi_MLoad +Swi_MLoad: + svc 0xcc + bx lr + + +/* ARM permissions */ + .global Perms_Read +Perms_Read: + mrc p15, 0, r0, c3, c0 + bx lr + + .global Perms_Write +Perms_Write: + mcr p15, 0, r0, c3, c0 + bx lr + + +/* MEM2 routines */ + .global MEM2_Prot +MEM2_Prot: + ldr r1, =0xD8B420A + strh r0, [r1] + bx lr + + +/* Tools */ + .global VirtToPhys +VirtToPhys: + and r0, #0x7fffffff + bx lr + + .global PhysToVirt +PhysToVirt: + orr r0, #0x80000000 + bx lr diff --git a/cios/dip_frag/types.h b/cios/dip_frag/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/dip_frag/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/dip_frag/usbstorage.c b/cios/dip_frag/usbstorage.c new file mode 100644 index 0000000..b68dc5a --- /dev/null +++ b/cios/dip_frag/usbstorage.c @@ -0,0 +1,300 @@ +/*------------------------------------------------------------- + +usbstorage_starlet.c -- USB mass storage support, inside starlet +Copyright (C) 2009 Kwiirk + +If this driver is linked before libogc, this will replace the original +usbstorage driver by svpe from libogc +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. + +-------------------------------------------------------------*/ + +//#include "mem.h" +#include "syscalls.h" +//#include "timer.h" +#include "types.h" +#include "usbstorage.h" + +#include +#include + +#define DEVICE_TYPE_WII_USB (('W'<<24)|('U'<<16)|('S'<<8)|'B') + +/* IOCTL commands */ +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) + +/* Constants */ +#define USB_MAX_SECTORS 64 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/usb2"; +static ioctlv io_vector[3] ATTRIBUTE_ALIGN(32); +static u32 io_buffer[3] ATTRIBUTE_ALIGN(32); +static s32 fd = -1; +static u32 sectorSz = 0; + + + +bool __usbstorage_Read(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + io_buffer[0] = sector; + io_buffer[1] = numSectors; + + /* Setup io_vector */ + io_vector[0].data = &io_buffer[0]; + io_vector[0].len = sizeof(u32); + io_vector[1].data = &io_buffer[1]; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = (sectorSz * numSectors); + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Read data */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_READ_SECTORS, 2, 1, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} + +#if 0 +bool __usbstorage_Write(u32 sector, u32 numSectors, void *buffer) +{ + STACK_ALIGN(u32, _sector, 1, 32); + STACK_ALIGN(u32, _numSectors, 1, 32); + + u32 cnt, len = (sectorSz * numSectors); + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + *_sector = sector; + *_numSectors = numSectors; + + /* Setup io_vector */ + io_vector[0].data = _sector; + io_vector[0].len = sizeof(u32); + io_vector[1].data = _numSectors; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = len; + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Write data */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_WRITE_SECTORS, 3, 0, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} +#endif + +s32 __usbstorage_GetCapacity(u32 *_sectorSz) +{ + s32 ret; + if (fd >= 0) { + + /* Setup io_vector */ + io_vector[0].data = io_buffer; + io_vector[0].len = sizeof(u32); + + os_sync_after_write(io_vector, sizeof(ioctlv)); + + /* Get capacity */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_GET_CAPACITY, 0, 1, io_vector); + + os_sync_after_write(io_buffer, sizeof(u32)); + + /* Set sector size */ + sectorSz = io_buffer[0]; + + if (ret && _sectorSz) + *_sectorSz = sectorSz; + + return ret; + } + + return 0; +} + +int usbstorage_Init(void) +{ + //s32 ret; + + /* Already open */ + if (fd >= 0) + return 0; + + /* Open USB device */ + fd = os_open(fs, 1); + if (fd < 0) + return -11; + + /* Initialize USB storage */ + os_ioctlv(fd, USB_IOCTL_UMS_INIT, 0, 0, NULL); + + sectorSz = 0x200; // 512 + + return 0; +#if 0 + /* Get device capacity */ + ret = __usbstorage_GetCapacity(NULL); + if (ret == 0) + goto err; + + return 0; + +err: + /* Close USB device */ + usbstorage_Shutdown(); + return -22; +#endif +} + +bool usbstorage_Shutdown(void) +{ + if (fd >= 0) { + /* Close USB device */ + os_close(fd); + + /* Remove descriptor */ + fd = -1; + } + + return true; +} + +bool usbstorage_IsInserted(void) +{ + s32 ret; + + /* Get device capacity */ + ret = __usbstorage_GetCapacity(NULL); + + return (ret > 0); +} + +#if 0 + +bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt = 0; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + while (cnt < numSectors) { + void *ptr = (char *)buffer + (sectorSz * cnt); + + u32 _sector = sector + cnt; + u32 _numSectors = numSectors - cnt; + + /* Limit sector count */ + if (_numSectors > USB_MAX_SECTORS) + _numSectors = USB_MAX_SECTORS; + + /* Read sectors */ + ret = __usbstorage_Read(_sector, _numSectors, ptr); + if (!ret) + return false; + + /* Increase counter */ + cnt += _numSectors; + } + + return true; +} + +bool usbstorage_WriteSectors(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt = 0; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + while (cnt < numSectors) { + void *ptr = (char *)buffer + (sectorSz * cnt); + + u32 _sector = sector + cnt; + u32 _numSectors = numSectors - cnt; + + /* Limit sector count */ + if (_numSectors > USB_MAX_SECTORS) + _numSectors = USB_MAX_SECTORS; + + /* Write sectors */ + ret = __usbstorage_Write(_sector, _numSectors, ptr); + if (!ret) + return false; + + /* Increase counter */ + cnt += _numSectors; + } + + return true; +} + +bool usbstorage_ClearStatus(void) +{ + return true; +} + +#endif + + diff --git a/cios/dip_frag/usbstorage.h b/cios/dip_frag/usbstorage.h new file mode 100644 index 0000000..9415503 --- /dev/null +++ b/cios/dip_frag/usbstorage.h @@ -0,0 +1,16 @@ +#ifndef _USBSTORAGE_H_ +#define _USBSTORAGE_H_ + +#include "types.h" + +/* Prototypes */ +int usbstorage_Init(void); +bool usbstorage_Shutdown(void); +bool usbstorage_IsInserted(void); +bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer); +//bool usbstorage_WriteSectors(u32 sector, u32 numSectors, void *buffer); +bool usbstorage_ClearStatus(void); +bool __usbstorage_Read(u32 sector, u32 numSectors, void *buffer); + +#endif + diff --git a/cios/dip_frag/wbfs.c b/cios/dip_frag/wbfs.c new file mode 100644 index 0000000..803aaa2 --- /dev/null +++ b/cios/dip_frag/wbfs.c @@ -0,0 +1,97 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include + +#include "dip_calls.h" +#include "syscalls.h" + +/* Commands */ +#define IOCTL_WBFS_BASE (('W'<<24) | ('F'<<16) | ('S'<<8)) +#define IOCTL_WBFS_OPEN (IOCTL_WBFS_BASE + 0x1) +#define IOCTL_WBFS_READ (IOCTL_WBFS_BASE + 0x2) + +/* Variables */ +static char *devFs[] = { "/dev/usb2", "/dev/sdio/sdhc" }; +static s32 devFd = -1; + + +/* I/O buffer */ +static ioctlv vector[4] ATTRIBUTE_ALIGN(32); +static u32 buffer[9] ATTRIBUTE_ALIGN(32); + + +s32 WBFS_Open(u32 device, u8 *discid) +{ + /* Open device */ + devFd = os_open(devFs[device], 1); + if (devFd < 0) + return devFd; + + /* Copy disc ID */ + memcpy(buffer, discid, 6); + + /* Setup vector */ + vector[0].data = buffer; + vector[0].len = 6; + + /* Open disc */ + return os_ioctlv(devFd, IOCTL_WBFS_OPEN, 1, 0, vector); +} + +void WBFS_Close(void) +{ + /* Close device */ + if (devFd >= 0) + os_close(devFd); + + /* Reset descriptor */ + devFd = -1; +} + +s32 WBFS_Read(void *outbuf, u32 len, u32 offset) +{ + s32 ret; + + /* Set buffers */ + buffer[0] = offset; + buffer[8] = len; + + /* Setup vector */ + vector[0].data = &buffer[0]; + vector[0].len = 4; + vector[1].data = &buffer[8]; + vector[1].len = 4; + vector[2].data = outbuf; + vector[2].len = len; + + /* Flush cache */ + os_sync_after_write(vector, sizeof(vector)); + os_sync_after_write(buffer, sizeof(buffer)); + + /* Read data */ + ret = os_ioctlv(devFd, IOCTL_WBFS_READ, 2, 1, vector); + if (ret) + return ret; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + return 0; +} diff --git a/cios/dip_frag/wbfs.h b/cios/dip_frag/wbfs.h new file mode 100644 index 0000000..67c376e --- /dev/null +++ b/cios/dip_frag/wbfs.h @@ -0,0 +1,28 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _WBFS_H_ +#define _WBFS_H_ + +/* Prototypes */ +s32 WBFS_Open (u32 device, u8 *disid); +void WBFS_Close(void); +s32 WBFS_Read (void *outbuf, u32 len, u32 offset); + +#endif diff --git a/cios/dip_orig/LICENSE.txt b/cios/dip_orig/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/cios/dip_orig/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/cios/dip_orig/Makefile b/cios/dip_orig/Makefile new file mode 100644 index 0000000..f27557a --- /dev/null +++ b/cios/dip_orig/Makefile @@ -0,0 +1,54 @@ +# devkitARM path +DEVKITARM ?= /opt/devkitARM + +# Prefix +PREFIX = $(DEVKITARM)/bin/arm-eabi- + +# Executables +CC = $(PREFIX)gcc +LD = $(PREFIX)gcc +STRIP = ../stripios/stripios + +# Flags +ARCH = -mcpu=arm926ej-s -mthumb -mthumb-interwork -mbig-endian +CFLAGS = $(ARCH) -I. -fomit-frame-pointer -Os -Wall +LDFLAGS = $(ARCH) -nostartfiles -Wl,-T,link.ld,-Map,$(TARGET).map -Wl,--gc-sections -Wl,-static + +# Libraries +LIBS = + +# Target +TARGET = dip-plugin + +# Objects +OBJS = dip.o \ + dip_calls.o \ + dma.o \ + file.o \ + ipc.o \ + main.o \ + patches.o \ + plugin.o \ + start.o \ + swi_mload.o \ + syscalls.o \ + tools.o \ + wbfs.o + + +$(TARGET).elf: $(OBJS) + @echo -e " LD\t$@" + @$(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.orig + @$(STRIP) $@.orig $@ + +%.o: %.s + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o: %.c + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -c -o $@ $< + +clean: + @echo -e "Cleaning..." + @rm -f $(OBJS) $(TARGET).elf $(TARGET).elf.orig $(TARGET).map diff --git a/cios/dip_orig/dip.c b/cios/dip_orig/dip.c new file mode 100644 index 0000000..00ce1c0 --- /dev/null +++ b/cios/dip_orig/dip.c @@ -0,0 +1,188 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include + +#include "dip.h" +#include "dip_calls.h" +#include "dma.h" +#include "errno.h" +#include "ioctl.h" +#include "syscalls.h" + +/* Constants */ +#define MAX_READ_RETRIES 16 + + +s32 __DI_ReadA8(void *outbuf, u32 len, u32 offset) +{ + u32 dic[8]; + + /* Clear buffer */ + memset(dic, 0, sizeof(dic)); + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_READ_A8 << 24; + dic[1] = len; + dic[2] = offset; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + /* Call command */ + return DI_HandleCmd(dic, outbuf, len); +} + +s32 __DI_ReadD0(void *outbuf, u32 len, u32 lba) +{ + u32 cnt; + s32 ret = DIP_EIO; + + /* Do DVD read */ + for (cnt = 0; ret && (cnt < MAX_READ_RETRIES); cnt++) { + u32 dic[8]; + + /* Clear buffer */ + memset(dic, 0, sizeof(dic)); + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_READ_D0 << 24; + dic[1] = 0; + dic[2] = 0; + dic[3] = len >> 11; + dic[4] = lba; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + /* Call command */ + ret = DI_HandleCmd(dic, outbuf, len); + } + + return ret; +} + +s32 __DI_ReadFromSector(void *outbuf, u32 len, u32 pos, u32 lba) +{ + u8 *buf = NULL; + s32 ret; + + /* Check length */ + if ((len + pos) > SECTOR_SIZE) + return IPC_EINVAL; + + /* Allocate memory */ + buf = DI_Alloc(SECTOR_SIZE, 32); + if (!buf) + return IPC_ENOMEM; + + /* Read sector */ + ret = __DI_ReadD0(buf, SECTOR_SIZE, lba); + + /* Extract data */ + if (!ret) + memcpy(outbuf, buf + pos, len); + + /* Free memory */ + DI_Free(buf); + + return ret; +} + + +u32 DI_CustomCmd(void *inbuf, void *outbuf) +{ + u32 *diRegs = (u32 *)0x0D006000; + + /* Set registers */ + memcpy(diRegs, inbuf, 32); + + /* Wait */ + while (diRegs[7] & 1); + + /* Copy registers */ + if (outbuf) + memcpy(outbuf, diRegs, 32); + + return diRegs[8]; +} + +s32 DI_StopMotor(void) +{ + u32 dic[8]; + + /* Prepare input buffer */ + dic[0] = IOCTL_DI_STOP_MOTOR << 24; + dic[1] = 0; + dic[2] = 0; + + /* Call command */ + return DI_HandleCmd(dic, NULL, 0); +} + +s32 DI_ReadDvd(u8 *outbuf, u32 len, u32 offset) +{ + u32 cnt, lba, size; + s32 ret = 0; + + /* Initial LBA */ + lba = offset >> 9; + + /* Do reads */ + for (cnt = 0; cnt < len; cnt += size) { + u32 dmasize, offlba, pos; + + /* Get offset/size */ + size = len - cnt; + offlba = lba << 9; + + /* Position in sector */ + pos = (offset > offlba) ? (offset - offlba) << 2 : 0; + + /* Check DMA range */ + dmasize = DMA_CheckRange(outbuf + cnt, size, SECTOR_SIZE); + + /* Use proper read method */ + if (!dmasize || pos) { + /* Check block size limit */ + if ((size + pos) > SECTOR_SIZE) + size = SECTOR_SIZE - pos; + + /* Read sector */ + ret = __DI_ReadFromSector(outbuf + cnt, size, pos, lba); + } else { + /* Set block size */ + size = (dmasize > MAX_SECTOR_SIZE) ? MAX_SECTOR_SIZE : dmasize; + + /* Read data */ + ret = __DI_ReadD0(outbuf + cnt, size, lba); + } + + /* Next LBA */ + lba += (size + pos) >> 11; + } + + return ret; +} + +s32 DI_ReadWod(void *outbuf, u32 len, u32 offset) +{ + /* Read data */ + return __DI_ReadA8(outbuf, len, offset); +} diff --git a/cios/dip_orig/dip.h b/cios/dip_orig/dip.h new file mode 100644 index 0000000..50b3a06 --- /dev/null +++ b/cios/dip_orig/dip.h @@ -0,0 +1,40 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DIP_H_ +#define _DIP_H_ + +#include "types.h" + +/* Disc lengths */ +#define DVD5_LENGTH 0x46090000 +#define DVD9_LENGTH 0x7ED38000 + +/* Disc sector */ +#define SECTOR_SIZE 0x800 +#define MAX_SECTOR_SIZE 0x7F8000 + + +/* Prototypes */ +u32 DI_CustomCmd(void *inbuf, void *outbuf); +s32 DI_StopMotor(void); +s32 DI_ReadDvd(u8 *outbuf, u32 len, u32 offset); +s32 DI_ReadWod(void *outbuf, u32 len, u32 offset); + +#endif diff --git a/cios/dip_orig/dip_calls.h b/cios/dip_orig/dip_calls.h new file mode 100644 index 0000000..d07c769 --- /dev/null +++ b/cios/dip_orig/dip_calls.h @@ -0,0 +1,35 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DIP_CALLS_H_ +#define _DIP_CALLS_H_ + +#include "types.h" + +/* Prototypes */ +s32 DI_ReadHash(void); +void *DI_Alloc(u32 size, u32 align); +void DI_Free(void *ptr); +void DI_Printf(const char *fmt, ...); + +/* DIP handlers */ +s32 DI_HandleIoctl(void *buffer, u32 fd); +s32 DI_HandleCmd(void *inbuf, const void *outbuf, u32 size); + +#endif diff --git a/cios/dip_orig/dip_calls.s b/cios/dip_orig/dip_calls.s new file mode 100644 index 0000000..a7e266d --- /dev/null +++ b/cios/dip_orig/dip_calls.s @@ -0,0 +1,104 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + + .align 4 + +/* + * DIP functions + */ + .code 32 + .global DI_ReadHash +DI_ReadHash: + stmfd sp!, {r7, lr} + ldr r7, =addr_readHash + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Alloc +DI_Alloc: + stmfd sp!, {r7, lr} + ldr r7, =addr_alloc + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Free +DI_Free: + stmfd sp!, {r7, lr} + ldr r7, =addr_free + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 + .global DI_Printf +DI_Printf: + stmfd sp!, {r7, lr} + ldr r7, =addr_printf + ldr r7, [r7] + bl _call_di + ldmfd sp!, {r7, lr} + bx lr + + .code 32 +_call_di: + bx r7 + + +/* + * DIP handlers + */ + .code 16 + .thumb_func + + .global DI_HandleIoctl +DI_HandleIoctl: + push {r4-r7, lr} + mov r7, r10 + mov r6, r8 + push {r6, r7} + ldr r5, [r0] + mov r10, r1 + + ldr r3, =addr_handleIoctl + ldr r3, [r3] + bx r3 + + .code 16 + .thumb_func + + .global DI_HandleCmd +DI_HandleCmd: + push {r4-r7, lr} + mov r7, r11 + mov r6, r10 + mov r5, r9 + mov r4, r8 + push {r4-r7} + + ldr r3, =addr_handleCmd + ldr r3, [r3] + bx r3 diff --git a/cios/dip_orig/dma.c b/cios/dip_orig/dma.c new file mode 100644 index 0000000..f2aa751 --- /dev/null +++ b/cios/dip_orig/dma.c @@ -0,0 +1,55 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "types.h" + +/* DMA constants */ +#define DMA1_START_ADDRESS 0x00000000 +#define DMA1_END_ADDRESS 0x01800000 +#define DMA2_START_ADDRESS 0x10000000 +#define DMA2_END_ADDRESS 0x13618000 + + +s32 DMA_CheckRange(void *outbuf, u32 size, u32 alignment) +{ + u32 mem; + s32 ret = 0; + + /* Output buffer address */ + mem = (u32)outbuf; + + /* Check for memory alignment */ + if (!(mem & 31)) { + u32 dmalen = 0; + + /* DMA1 range check */ + if ((mem >= DMA1_START_ADDRESS) && (mem < DMA1_END_ADDRESS)) + dmalen = (DMA1_END_ADDRESS - mem); + + /* DMA2 range check */ + if ((mem >= DMA2_START_ADDRESS) && (mem < DMA2_END_ADDRESS)) + dmalen = (DMA2_END_ADDRESS - mem); + + if (dmalen >= alignment) + ret = (dmalen < size) ? dmalen : size; + ret -= (ret & (alignment - 1)); + } + + return ret; +} diff --git a/cios/dip_orig/dma.h b/cios/dip_orig/dma.h new file mode 100644 index 0000000..ae8b2b2 --- /dev/null +++ b/cios/dip_orig/dma.h @@ -0,0 +1,28 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _DMA_H_ +#define _DMA_H_ + +#include "types.h" + +/* Prototypes */ +s32 DMA_CheckRange(void *outbuf, u32 size, u32 alignment); + +#endif diff --git a/cios/dip_orig/errno.h b/cios/dip_orig/errno.h new file mode 100644 index 0000000..3b8b943 --- /dev/null +++ b/cios/dip_orig/errno.h @@ -0,0 +1,30 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _ERRNO_H_ +#define _ERRNO_H_ + +/* Return codes */ +#define DIP_EIO 0xA000 + +/* Error codes */ +#define ERROR_BLOCK_RANGE 0x52100 +#define ERROR_WRONG_DISC 0x53100 + +#endif diff --git a/cios/dip_orig/file.c b/cios/dip_orig/file.c new file mode 100644 index 0000000..3bb83f1 --- /dev/null +++ b/cios/dip_orig/file.c @@ -0,0 +1,61 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "syscalls.h" + +/* Variables */ +static s32 fd = -1; + + +s32 File_Open(const char *path) +{ + /* Open file */ + fd = os_open(path, 0); + if (fd < 0) + return fd; + + return 0; +} + +void File_Close(void) +{ + /* Close file */ + if (fd >= 0) + os_close(fd); + + /* Reset descriptor */ + fd = -1; +} + +s32 File_Read(void *buffer, u32 len, u32 offset) +{ + s32 ret; + + /* Seek file */ + ret = os_seek(fd, offset << 2, 0); + if (ret < 0) + return ret; + + /* Read file */ + ret = os_read(fd, buffer, len); + if (ret < 0) + return ret; + + return 0; +} diff --git a/cios/dip_orig/file.h b/cios/dip_orig/file.h new file mode 100644 index 0000000..e9c9a38 --- /dev/null +++ b/cios/dip_orig/file.h @@ -0,0 +1,30 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _FILE_H_ +#define _FILE_H_ + +#include "types.h" + +/* Prototypes */ +s32 File_Open (const char *path); +void File_Close(void); +s32 File_Read (void *buffer, u32 len, u32 offset); + +#endif diff --git a/cios/dip_orig/ioctl.h b/cios/dip_orig/ioctl.h new file mode 100644 index 0000000..553f08a --- /dev/null +++ b/cios/dip_orig/ioctl.h @@ -0,0 +1,58 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _IOCTL_H_ +#define _IOCTL_H_ + +/* IOCTL commands */ +#define IOCTL_DI_LOW_READ 0x71 +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_WAITCVRCLOSE 0x79 +#define IOCTL_DI_COVER_REG 0x7A +#define IOCTL_DI_COVER_CLEAR 0x86 +#define IOCTL_DI_COVER_STATUS 0x88 +#define IOCTL_DI_COVER_SET 0x89 +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_STATUS_REG 0x95 +#define IOCTL_DI_REPORT_KEY 0xA4 +#define IOCTL_DI_READ_A8 0xA8 +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_READ_D0 0xD0 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_READBCA 0xDA +#define IOCTL_DI_REQCOVER 0xDB +#define IOCTL_DI_REQERROR 0xE0 +#define IOCTL_DI_STOP_MOTOR 0xE3 +#define IOCTL_DI_AUDIO_CONFIG 0xE4 + +/* Custom commands */ +#define IOCTL_DI_OFFSET_SET 0xF0 +#define IOCTL_DI_OFFSET_GET 0xF1 +#define IOCTL_DI_CRYPT_SET 0xF2 +#define IOCTL_DI_CRYPT_GET 0xF3 +#define IOCTL_DI_WBFS_SET 0xF4 +#define IOCTL_DI_WBFS_GET 0xF5 +#define IOCTL_DI_RESET_DISABLE 0xF6 +#define IOCTL_DI_FILE_SET 0xF7 +#define IOCTL_DI_FILE_GET 0xF8 +#define IOCTL_DI_CUSTOMCMD 0xFF + +#endif + diff --git a/cios/dip_orig/ipc.c b/cios/dip_orig/ipc.c new file mode 100644 index 0000000..f9c361f --- /dev/null +++ b/cios/dip_orig/ipc.c @@ -0,0 +1,30 @@ +#include "ipc.h" +#include "syscalls.h" +#include "types.h" + + +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = 0; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Invalidate cache */ + os_sync_before_read(buffer, len); + } +} + +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = inlen; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Flush cache */ + os_sync_after_write(buffer, len); + } +} diff --git a/cios/dip_orig/ipc.h b/cios/dip_orig/ipc.h new file mode 100644 index 0000000..c6b39d2 --- /dev/null +++ b/cios/dip_orig/ipc.h @@ -0,0 +1,44 @@ +#ifndef _IPC_H_ +#define _IPC_H_ + +#include "types.h" + +/* IPC error codes */ +#define IPC_ENOENT -6 +#define IPC_ENOMEM -22 +#define IPC_EINVAL -101 +#define IPC_EACCESS -102 +#define IPC_EEXIST -105 +#define IPC_NOENT -106 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTLV vector */ +typedef struct iovec { + void *data; + u32 len; +} ioctlv; + +/* IOCTL structure */ +typedef struct { + u32 command; + + u32 *inbuf; + u32 inlen; + u32 *iobuf; + u32 iolen; +} ioctl; + + +/* Prototypes */ +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen); +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen); + +#endif diff --git a/cios/dip_orig/link.ld b/cios/dip_orig/link.ld new file mode 100644 index 0000000..0e04351 --- /dev/null +++ b/cios/dip_orig/link.ld @@ -0,0 +1,60 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +/* Sections area */ +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x13800000, LENGTH = 0x1000 + ram(rw) : ORIGIN = 0x13801000, LENGTH = 0x3000 +} + +__exe_start_phys__ = 0x13800000; +__ram_start_phys__ = 0x13801000; + + +SECTIONS { + .ios_info_table : { + KEEP (*(.ios_info_table)) + } > table + + .init : AT (__exe_start_phys__) { + *(.init) + . = ALIGN(4); + } > exe + + .text : { + *(.text*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } > exe + + .data : AT (__ram_start_phys__) { + *(.data*) + *(.data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(4); + } > ram + + .rodata : { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .bss : { + *(.dynsbss) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(COMMON) + KEEP(*(.ios_bss)) + . = ALIGN(4); + } > ram +} diff --git a/cios/dip_orig/main.c b/cios/dip_orig/main.c new file mode 100644 index 0000000..9da1157 --- /dev/null +++ b/cios/dip_orig/main.c @@ -0,0 +1,71 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "main.h" +#include "patches.h" +#include "swi_mload.h" +#include "syscalls.h" +#include "tools.h" +#include "types.h" +#include "wbfs.h" + +/* IOS information */ +iosInfo ios = { 0 }; + + +s32 __DI_System(u32 arg1, u32 arg2) +{ + u32 perms; + + /* Invalidate cache */ + ICInvalidate(); + + /* Apply permissions */ + perms = Perms_Read(); + Perms_Write(0xFFFFFFFF); + + /* Patch modules */ + Patch_DipModule(ios.dipVersion); + + /* Restore permissions */ + Perms_Write(perms); + + return 0; +} + +s32 __DI_Initialize(void) +{ + /* Get IOS info */ + Swi_GetIosInfo(&ios); + + /* Prepare system */ + Swi_CallFunc((void *)__DI_System, NULL, NULL); + + return 0; +} + + +int main(void) +{ + /* Print info */ + write("$IOSVersion: DIPP: " __DATE__ " " __TIME__ " 64M$\n"); + + /* Initialize plugin */ + return __DI_Initialize(); +} diff --git a/cios/dip_orig/main.h b/cios/dip_orig/main.h new file mode 100644 index 0000000..2a7407f --- /dev/null +++ b/cios/dip_orig/main.h @@ -0,0 +1,40 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include "types.h" + +/* IOS info structure */ +typedef struct { + /* Syscall base */ + u32 syscall; + + /* Module versions */ + u32 dipVersion; + u32 esVersion; + u32 ffsVersion; + u32 iopVersion; +} iosInfo; + +/* Externs */ +extern iosInfo ios; + +#endif diff --git a/cios/dip_orig/patches.c b/cios/dip_orig/patches.c new file mode 100644 index 0000000..4f9de31 --- /dev/null +++ b/cios/dip_orig/patches.c @@ -0,0 +1,115 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "plugin.h" +#include "tools.h" + +/* Macros */ +#define Write8(addr, val) \ + *(u8 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u8)); + +#define Write16(addr, val) \ + *(u16 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u16)); + +#define Write32(addr, val) \ + *(u32 *)addr = val; \ + DCFlushRange((void *)addr, sizeof(u32)); + + +/* Addresses */ +u32 addr_handleIoctl = 0; +u32 addr_handleCmd = 0; +u32 addr_alloc = 0; +u32 addr_free = 0; +u32 addr_readHash = 0; +u32 addr_printf = 0; +u8 *dip_readctrl = 0; + + +void Patch_DipModule(u32 version) +{ + switch (version) { + /** 07/11/08 14:34:26 **/ + case 0x48776F72: + /* Patch IOCTL handler */ + Write32(0x20200400, 0x4B004718); + Write32(0x20200404, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200EF8, 0x4B004718); + Write32(0x20200EFC, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202A70 + 1; + addr_handleIoctl = 0x2020040C + 1; + addr_handleCmd = 0x20200F04 + 1; + addr_alloc = 0x2020096C + 1; + addr_free = 0x2020093C + 1; + addr_printf = 0x2020387C + 1; + dip_readctrl = (u8 *)0x2022DD60; + + break; + + /** 07/24/08 20:08:44 **/ + case 0x4888E14C: + /* Patch IOCTL handler */ + Write32(0x202003B8, 0x4B004718); + Write32(0x202003BC, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200D2C, 0x4B004718); + Write32(0x20200D30, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202874 + 1; + addr_handleIoctl = 0x202003C4 + 1; + addr_handleCmd = 0x20200D38 + 1; + addr_alloc = 0x202008C4 + 1; + addr_free = 0x20200898 + 1; + addr_printf = 0x2020365C + 1; + dip_readctrl = (u8 *)0x2022CDAC; + + break; + + /** 11/24/08 15:39:09 **/ + /** 06/03/09 07:49:09 **/ + case 0x492ACA9D: + case 0x4A262AF5: + /* Patch IOCTL handler */ + Write32(0x20200400, 0x4B004718); + Write32(0x20200404, (u32)DI_EmulateIoctl); + + /* Patch command handler */ + Write32(0x20200EF8, 0x4B004718); + Write32(0x20200EFC, (u32)DI_EmulateCmd); + + /* Set addresses */ + addr_readHash = 0x20202944 + 1; + addr_handleIoctl = 0x2020040C + 1; + addr_handleCmd = 0x20200F04 + 1; + addr_alloc = 0x2020096C + 1; + addr_free = 0x2020093C + 1; + addr_printf = 0x20203750 + 1; + dip_readctrl = (u8 *)0x2022CD60; + + break; + } +} diff --git a/cios/dip_orig/patches.h b/cios/dip_orig/patches.h new file mode 100644 index 0000000..6a45867 --- /dev/null +++ b/cios/dip_orig/patches.h @@ -0,0 +1,29 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _PATCHES_H_ +#define _PATCHES_H_ + +#include "types.h" + +/* Prototypes */ +void Patch_DipModule(u32 version); + +#endif + diff --git a/cios/dip_orig/plugin.c b/cios/dip_orig/plugin.c new file mode 100644 index 0000000..eca1246 --- /dev/null +++ b/cios/dip_orig/plugin.c @@ -0,0 +1,566 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "dip.h" +#include "dip_calls.h" +#include "errno.h" +#include "file.h" +#include "ioctl.h" +#include "plugin.h" +#include "syscalls.h" +#include "wbfs.h" + +/* Global config */ +struct dipConfig config = { 0 }; + + +s32 __DI_CheckOffset(u32 offset) +{ + u32 offmax; + + /* Check disc type */ + switch (config.type) { + /* Single layer */ + case DISC_DVD5: + offmax = DVD5_LENGTH; + break; + + /* Dual layer */ + case DISC_DVD9: + offmax = DVD9_LENGTH; + break; + + default: + return 0; + } + + /* Check offset */ + if (offset >= offmax) { + /* Set error */ + config.error = ERROR_BLOCK_RANGE; + + /* I/O error */ + return DIP_EIO; + } + + return 0; +} + +s32 __DI_ReadUnencrypted(void *outbuf, u32 len, u32 offset) +{ + s32 ret; + + /* Check offset */ + ret = __DI_CheckOffset(offset); + if (ret) + return ret; + + /* Update offset */ + offset += (config.offset[0] + config.offset[1]); + + /* File read */ + if (DI_ChkMode(MODE_FILE)) + return File_Read(outbuf, len, offset); + + /* WBFS read */ + if (DI_ChkMode(MODE_WBFS)) + return WBFS_Read(outbuf, len, offset); + + /* DVD read */ + if (DI_ChkMode(MODE_DVDROM)) + return DI_ReadDvd(outbuf, len, offset); + + /* WOD read */ + return DI_ReadWod(outbuf, len, offset); +} + +s32 __DI_ReadDiscId(u32 *outbuf, u32 len) +{ + s32 ret; + + /* Read ID (first sector) */ + ret = __DI_ReadUnencrypted(outbuf, len, 0); + if (ret < 0) + return ret; + + /* Check WOD magic word */ + if (outbuf[6] == WOD_MAGIC) { + extern u8 *dip_readctrl; + + /* Set read control */ + dip_readctrl[0] = 1; + + /* Read hash */ + if (!dip_readctrl[1]) + ret = DI_ReadHash(); + } + + return ret; +} + +void __DI_CheckDisc(void) +{ + void *buffer; + s32 ret; + + /* Allocate buffer */ + buffer = DI_Alloc(SECTOR_SIZE, 32); + if (!buffer) + return; + + /* Read second layer */ + ret = __DI_ReadUnencrypted(buffer, SECTOR_SIZE, 0x50000000); + + /* Set disc type */ + config.type = (!ret) ? DISC_DVD9 : DISC_DVD5; + + /* Free buffer */ + DI_Free(buffer); +} + +void __DI_ResetConfig(void) +{ + /* Reset modes */ + DI_DelMode(MODE_CRYPT); + DI_DelMode(MODE_DVDROM); + + /* Reset offsets */ + config.offset[0] = 0; + config.offset[1] = 0; + + /* Reset variables */ + config.type = 0; + config.error = 0; + config.cover = 0; + config.noreset = 0; +} + + +s32 DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 size) +{ + u32 cmd = (inbuf[0] >> 24); + + s32 res; + s32 ret = 0; + + /* Reset error */ + if (cmd != IOCTL_DI_REQERROR) + config.error = 0; + + switch(cmd) { + /** Reset drive **/ + case IOCTL_DI_RESET: { + /* Check reset flag */ + if (!config.noreset) { + /* Reset DIP config */ + __DI_ResetConfig(); + + /* Non-DVD mode */ + if (DI_ChkMode(MODE_FILE | MODE_WBFS)) { + /* Stop motor */ + DI_StopMotor(); + + /* Set cover register */ + BIT_SET(config.cover, 4); + } else { + /* Reset drive */ + ret = DI_HandleCmd(inbuf, outbuf, size); + } + } + + break; + } + + /** Read disc ID **/ + case IOCTL_DI_READID: { + u32 offset = (config.offset[0] | config.offset[1]); + + /* Read disc ID */ + if (!DI_ChkMode(MODE_FILE | MODE_WBFS)) { + /* Call command */ + ret = DI_HandleCmd(inbuf, outbuf, size); + + /* Set DVD mode */ + if (ret) + DI_SetMode(MODE_DVDROM); + } + + /* Manual read */ + if (DI_ChkMode(MODE_DVDROM | MODE_FILE | MODE_WBFS) || offset) + ret = __DI_ReadDiscId(outbuf, size); + + /* Check disc type */ + if (!ret) + __DI_CheckDisc(); + + break; + } + + /** Encrypted disc read **/ + case IOCTL_DI_LOW_READ: { + /* Crypted read */ + if (DI_ChkMode(MODE_CRYPT)) { + u32 len = inbuf[1]; + u32 offset = inbuf[2]; + + /* Do unencrypted read */ + ret = __DI_ReadUnencrypted(outbuf, len, offset); + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Unencrypted disc read **/ + case IOCTL_DI_UNENCREAD: + case IOCTL_DI_READ_A8: + case IOCTL_DI_READ_D0: { + u32 len = inbuf[1]; + u32 offset = inbuf[2]; + + /* Change values unit */ + if (cmd == IOCTL_DI_READ_D0) { + len <<= 11; + offset <<= 9; + } + + /* Unencrypted read */ + ret = __DI_ReadUnencrypted(outbuf, len, offset); + + break; + } + + /** Disc BCA read **/ + case IOCTL_DI_READBCA: { + /* Read disc BCA */ + ret = __DI_ReadUnencrypted(outbuf, size, 0x40); + + break; + } + + /** Set drive offset **/ + case IOCTL_DI_OFFSET: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_FILE | MODE_WBFS); + + /* Set disc offset */ + if (res) { + /* Calculate offset */ + u32 offset = (inbuf[1] << 30) | inbuf[2]; + + /* Set drive offset */ + config.offset[1] = (offset & -0x8000); + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Seek disc **/ + case IOCTL_DI_SEEK: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_FILE | MODE_WBFS); + + /* Seek disc */ + if (!res) + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Audio config **/ + case IOCTL_DI_AUDIO_CONFIG: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_FILE | MODE_WBFS); + + /* Set audio config */ + if (!res) + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Report DVD key **/ + case IOCTL_DI_REPORT_KEY: { + /* Check modes */ + res = DI_ChkMode(MODE_DVDROM | MODE_FILE | MODE_WBFS); + + /* Report DVD key */ + if (res) { + /* Wrong disc */ + config.error = ERROR_WRONG_DISC; + + /* I/O error */ + ret = DIP_EIO; + } else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Request cover status **/ + case IOCTL_DI_REQCOVER: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Request cover status */ + if (res) + *outbuf = 0; + else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Request error code **/ + case IOCTL_DI_REQERROR: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Request error code */ + if (res || config.error) + *outbuf = config.error; + else + ret = DI_HandleCmd(inbuf, outbuf, size); + + break; + } + + /** Set offset base **/ + case IOCTL_DI_OFFSET_SET: { + u32 offset = inbuf[1]; + + /* Set base offset */ + config.offset[0] = offset; + + break; + } + + /** Get offset base **/ + case IOCTL_DI_OFFSET_GET: { + /* Return offset base */ + *outbuf = config.offset[0]; + + break; + } + + /** Set crypt mode **/ + case IOCTL_DI_CRYPT_SET: { + u32 mode = inbuf[1]; + + /* Enable crypt mode */ + if (mode) + DI_SetMode(MODE_CRYPT); + else + DI_DelMode(MODE_CRYPT); + + break; + } + + /** Get crypt mode **/ + case IOCTL_DI_CRYPT_GET: { + /* Check crypt bit */ + *outbuf = DI_ChkMode(MODE_CRYPT); + + break; + } + + /** Set WBFS mode **/ + case IOCTL_DI_WBFS_SET: { + u32 device = inbuf[1]; + + /* Close WBFS */ + WBFS_Close(); + + /* Disable mode */ + DI_DelMode(MODE_WBFS); + + /* Check device */ + if (device) { + u8 *discid = (u8 *)&inbuf[2]; + + /* Open device */ + ret = WBFS_Open(device-1, discid); + + /* Enable mode */ + if (!ret) + DI_SetMode(MODE_WBFS); + } + + break; + } + + /** Get WBFS mode **/ + case IOCTL_DI_WBFS_GET: { + /* Check WBFS bit */ + *outbuf = DI_ChkMode(MODE_WBFS); + + break; + } + + /** Set file mode **/ + case IOCTL_DI_FILE_SET: { + char *filename = (char *)inbuf[1]; + + /* Close file */ + File_Close(); + + /* Disable mode */ + DI_DelMode(MODE_FILE); + + /* Check flag */ + if (filename) { + /* Convert address */ + filename = VirtToPhys(filename); + + /* Open file */ + ret = File_Open(filename); + + /* Enable mode */ + if (!ret) + DI_SetMode(MODE_FILE); + } + + break; + } + + /** Get file mode **/ + case IOCTL_DI_FILE_GET: { + /* Check file bit */ + *outbuf = DI_ChkMode(MODE_FILE); + + break; + } + + /** Disable reset **/ + case IOCTL_DI_RESET_DISABLE: { + u32 value = inbuf[1]; + + /* Disable reset */ + config.noreset = value; + + break; + } + + /** Send custom DVD command **/ + case IOCTL_DI_CUSTOMCMD: { + void *buffer = (void *)inbuf[1]; + + /* Convert address to physical */ + buffer = VirtToPhys(buffer); + + /* Send custom DI command */ + ret = DI_CustomCmd(buffer, outbuf); + + break; + } + + default: + /* Call command */ + ret = DI_HandleCmd(inbuf, outbuf, size); + } + + return ret; +} + +s32 DI_EmulateIoctl(ioctl *buffer, s32 fd) +{ + u32 *outbuf = buffer->iobuf; + u32 cmd = buffer->command; + + s32 res; + s32 ret = 1; + + /* Parse command */ + switch (cmd) { + /** Wait for cover close **/ + case IOCTL_DI_WAITCVRCLOSE: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Wait for cover close */ + if (!res) + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get cover register **/ + case IOCTL_DI_COVER_REG: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Get cover register */ + if (res) + *outbuf = config.cover; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Clear cover interrupt **/ + case IOCTL_DI_COVER_CLEAR: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Clear cover interrupt */ + if (res) + BIT_DEL(config.cover, 4); + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get cover status **/ + case IOCTL_DI_COVER_STATUS: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Get cover status */ + if (res) + *outbuf = 0x02; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + /** Get status register **/ + case IOCTL_DI_STATUS_REG: { + /* Check modes */ + res = DI_ChkMode(MODE_FILE | MODE_WBFS); + + /* Get status register */ + if (res) + *outbuf = 0x0A; + else + ret = DI_HandleIoctl(buffer, fd); + + break; + } + + default: + /* Call IOCTL */ + ret = DI_HandleIoctl(buffer, fd); + } + + return ret; +} diff --git a/cios/dip_orig/plugin.h b/cios/dip_orig/plugin.h new file mode 100644 index 0000000..d26e035 --- /dev/null +++ b/cios/dip_orig/plugin.h @@ -0,0 +1,75 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +#include "ipc.h" +#include "tools.h" +#include "types.h" + +/* WOD magic word */ +#define WOD_MAGIC 0x5D1C9EA3 + +/* Mode codes */ +#define MODE_DVDROM 0x01 +#define MODE_CRYPT 0x02 +#define MODE_WBFS 0x04 +#define MODE_FILE 0x08 + +/* Macros */ +#define DI_SetMode(bit) BIT_SET(config.mode, (bit)) +#define DI_DelMode(bit) BIT_DEL(config.mode, (bit)) +#define DI_ChkMode(bit) BIT_CHK(config.mode, (bit)) + +/* Disc types */ +enum { + DISC_UNKNOWN = 0, + DISC_DVD5, + DISC_DVD9, +}; + + +/* Config structure */ +struct dipConfig { + /* Modes */ + u32 mode; + + /* Type */ + u32 type; + + /* Offsets */ + u32 offset[2]; + + /* Last error */ + u32 error; + + /* Misc variables */ + u32 cover; + u32 noreset; +}; + +/* Prototypes */ +s32 DI_EmulateIoctl(ioctl *buffer, s32 fd); +s32 DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 size); + +/* Extern */ +extern struct dipConfig config; + +#endif diff --git a/cios/dip_orig/start.s b/cios/dip_orig/start.s new file mode 100644 index 0000000..f7a7c10 --- /dev/null +++ b/cios/dip_orig/start.s @@ -0,0 +1,71 @@ +/* + Custom IOS module for Wii. + Copyright (C) 2008 neimod. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .arm + + .EQU ios_thread_arg, 4 + .EQU ios_thread_priority, 0x48 + .EQU ios_thread_stacksize, 0x1000 + + + .global _start +_start: + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bx r3 + + + +/* + * IOS bss + */ + .section ".ios_bss", "a", %nobits + + .space ios_thread_stacksize + .global ios_thread_stack /* stack decrements from high address.. */ +ios_thread_stack: + + +/* + * IOS info table + */ + .section ".ios_info_table", "ax", %progbits + + .global ios_info_table +ios_info_table: + .long 0x0 + .long 0x28 @ numentries * 0x28 + .long 0x6 + + .long 0xB + .long ios_thread_arg @ passed to thread entry func, maybe module id + + .long 0x9 + .long _start + + .long 0x7D + .long ios_thread_priority + + .long 0x7E + .long ios_thread_stacksize + + .long 0x7F + .long ios_thread_stack diff --git a/cios/dip_orig/swi_mload.c b/cios/dip_orig/swi_mload.c new file mode 100644 index 0000000..78b3b66 --- /dev/null +++ b/cios/dip_orig/swi_mload.c @@ -0,0 +1,58 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include "tools.h" +#include "types.h" + + +void Swi_Memcpy(void *dst, void *src, s32 len) +{ + /* Wrong length */ + if (len <= 0) + return; + + /* Call function */ + Swi_MLoad(2, (u32)dst, (u32)src, (u32)len); +} + +void Swi_uMemcpy(void *dst, void *src, s32 len) +{ + /* Wrong length */ + if (len <= 0) + return; + + /* Call function */ + Swi_MLoad(9, (u32)dst, (u32)src, (u32)len); +} + +s32 Swi_CallFunc(s32 (*func)(void *in, void *out), void *in, void *out) +{ + /* Call function */ + return Swi_MLoad(16, (u32)func, (u32)in, (u32)out); +} + +u32 Swi_GetSyscallBase(void) +{ + return Swi_MLoad(17, 0, 0, 0); +} + +u32 Swi_GetIosInfo(void *buffer) +{ + return Swi_MLoad(18, (u32)buffer, 0, 0); +} diff --git a/cios/dip_orig/swi_mload.h b/cios/dip_orig/swi_mload.h new file mode 100644 index 0000000..07f7c6a --- /dev/null +++ b/cios/dip_orig/swi_mload.h @@ -0,0 +1,32 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _SWI_MLOAD_H_ +#define _SWI_MLOAD_H_ + +#include "types.h" + +/* Prototypes */ +void Swi_Memcpy(void *dst, void *src, s32 len); +void Swi_uMemcpy(void *dst, void *src, s32 len); +s32 Swi_CallFunc(s32 (*func)(void *in, void *out), void *in, void *out); +u32 Swi_GetSyscallBase(void); +u32 Swi_GetIosInfo(void *buffer); + +#endif diff --git a/cios/dip_orig/syscalls.h b/cios/dip_orig/syscalls.h new file mode 100644 index 0000000..e9c4585 --- /dev/null +++ b/cios/dip_orig/syscalls.h @@ -0,0 +1,69 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "ipc.h" +#include "types.h" + +/* IOS syscalls */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +s32 os_open(const char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, ioctlv *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* ARM syscalls */ +void write(const char *str); + +#endif diff --git a/cios/dip_orig/syscalls.s b/cios/dip_orig/syscalls.s new file mode 100644 index 0000000..6a29633 --- /dev/null +++ b/cios/dip_orig/syscalls.s @@ -0,0 +1,281 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + +.macro syscall vec_sys + .long 0xE6000010 + (\vec_sys<<5) + bx lr +.endm + + +/* + * IOS syscalls + */ + .code 32 + .global os_thread_create +os_thread_create: + syscall 0x0 + + .code 32 + .global os_thread_joint +os_thread_joint: + syscall 0x1 + + .code 32 + .global os_thread_cancel +os_thread_cancel: + syscall 0x2 + + .code 32 + .global os_get_thread_id +os_get_thread_id: + syscall 0x3 + + .code 32 + .global os_get_parent_thread_id +os_get_parent_thread_id: + syscall 0x4 + + .code 32 + .global os_thread_continue +os_thread_continue: + syscall 0x5 + + .code 32 + .global os_thread_stop +os_thread_stop: + syscall 0x6 + + .code 32 + .global os_thread_yield +os_thread_yiel: + syscall 0x7 + + .code 32 + .global os_thread_get_priority +os_thread_get_priority : + syscall 0x8 + + .code 32 + .global os_thread_set_priority +os_thread_set_priority: + syscall 0x9 + + .code 32 + .global os_message_queue_create +os_message_queue_create: + syscall 0xa + + .code 32 + .global os_message_queue_destroy +os_message_queue_destroy: + syscall 0xb + + .code 32 + .global os_message_queue_send +os_message_queue_send: + syscall 0xc + + .code 32 + .global os_message_queue_send_now +os_message_queue_send_now: + syscall 0xd + + .code 32 + .global os_message_queue_receive +os_message_queue_receive: + syscall 0xe + + .code 32 + .global os_register_event_handler +os_register_event_handler: + syscall 0xf + + .code 32 + .global os_unregister_event_handler +os_unregister_event_handler: + syscall 0x10 + + .code 32 + .global os_create_timer +os_create_timer: + syscall 0x11 + + .code 32 + .global os_restart_timer +os_restart_timer: + syscall 0x12 + + .code 32 + .global os_stop_timer +os_stop_timer: + syscall 0x13 + + .code 32 + .global os_destroy_timer +os_destroy_timer: + syscall 0x14 + + .code 32 + .global os_timer_now +os_timer_now: + syscall 0x15 + + .code 32 + .global os_heap_create +os_heap_create: + syscall 0x16 + + .code 32 + .global os_heap_destroy +os_heap_destroy: + syscall 0x17 + + .code 32 + .global os_heap_alloc +os_heap_alloc: + syscall 0x18 + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_device_register +os_device_register: + syscall 0x1b + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + .code 32 + .global os_ioctl +os_ioctl: + syscall 0x21 + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global os_open_async +os_open_async: + syscall 0x23 + + .code 32 + .global os_close_async +os_close_async: + syscall 0x24 + + .code 32 + .global os_read_async +os_read_async: + syscall 0x25 + + .code 32 + .global os_write_async +os_write_async: + syscall 0x26 + + .code 32 + .global os_seek_async +os_seek_async: + syscall 0x27 + + .code 32 + .global os_ioctl_async +os_ioctl_async: + syscall 0x28 + + .code 32 + .global os_ioctlv_async +os_ioctlv_async: + syscall 0x29 + + .code 32 + .global os_message_queue_ack +os_message_queue_ack: + syscall 0x2a + + .code 32 + .global os_software_IRQ +os_software_IRQ: + syscall 0x34 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3f + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_virt_to_phys +os_virt_to_phys: + syscall 0x4f + + .code 32 + .global os_syscall_50 +os_syscall_50: + syscall 0x50 + + +/* + * ARM syscalls + */ + .code 32 + .global write +write: + mov r2, lr + adds r1, r0, #0 + movs r0, #4 + svc 0xab + bx r2 diff --git a/cios/dip_orig/tools.h b/cios/dip_orig/tools.h new file mode 100644 index 0000000..eba35c4 --- /dev/null +++ b/cios/dip_orig/tools.h @@ -0,0 +1,51 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _TOOLS_H_ +#define _TOOLS_H_ + +#include "types.h" + +/* Macros */ +#define BIT_SET(x, y) (x |= y) +#define BIT_DEL(x, y) (x &= ~y) +#define BIT_CHK(x, y) (x & y) + + +/* Direct syscalls */ +void DCInvalidateRange(void* ptr, int size); +void DCFlushRange(void* ptr, int size); +void ICInvalidate(void); + +/* MLoad syscalls */ +s32 Swi_MLoad(u32 arg0, u32 arg1, u32 arg2, u32 arg3); + +/* ARM permissions */ +u32 Perms_Read(void); +void Perms_Write(u32 flags); + +/* MEM2 routines */ +void MEM2_Prot(u32 flag); + +/* Tools */ +void *VirtToPhys(void *address); +void *PhysToVirt(void *address); + +#endif + diff --git a/cios/dip_orig/tools.s b/cios/dip_orig/tools.s new file mode 100644 index 0000000..7f06838 --- /dev/null +++ b/cios/dip_orig/tools.s @@ -0,0 +1,85 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + + .text + + .align 4 + .code 32 + +/* Direct syscalls */ + .global DCInvalidateRange +DCInvalidateRange: + mcr p15, 0, r0, c7, c6, 1 + add r0, #0x20 + subs r1, #1 + bne DCInvalidateRange + bx lr + + .global DCFlushRange +DCFlushRange: + mcr p15, 0, r0, c7, c10, 1 + add r0, #0x20 + subs r1, #1 + bne DCFlushRange + bx lr + + .global ICInvalidate +ICInvalidate: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + + +/* MLoad syscalls */ + .global Swi_MLoad +Swi_MLoad: + svc 0xcc + bx lr + + +/* ARM permissions */ + .global Perms_Read +Perms_Read: + mrc p15, 0, r0, c3, c0 + bx lr + + .global Perms_Write +Perms_Write: + mcr p15, 0, r0, c3, c0 + bx lr + + +/* MEM2 routines */ + .global MEM2_Prot +MEM2_Prot: + ldr r1, =0xD8B420A + strh r0, [r1] + bx lr + + +/* Tools */ + .global VirtToPhys +VirtToPhys: + and r0, #0x7fffffff + bx lr + + .global PhysToVirt +PhysToVirt: + orr r0, #0x80000000 + bx lr diff --git a/cios/dip_orig/types.h b/cios/dip_orig/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/dip_orig/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/dip_orig/wbfs.c b/cios/dip_orig/wbfs.c new file mode 100644 index 0000000..803aaa2 --- /dev/null +++ b/cios/dip_orig/wbfs.c @@ -0,0 +1,97 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#include + +#include "dip_calls.h" +#include "syscalls.h" + +/* Commands */ +#define IOCTL_WBFS_BASE (('W'<<24) | ('F'<<16) | ('S'<<8)) +#define IOCTL_WBFS_OPEN (IOCTL_WBFS_BASE + 0x1) +#define IOCTL_WBFS_READ (IOCTL_WBFS_BASE + 0x2) + +/* Variables */ +static char *devFs[] = { "/dev/usb2", "/dev/sdio/sdhc" }; +static s32 devFd = -1; + + +/* I/O buffer */ +static ioctlv vector[4] ATTRIBUTE_ALIGN(32); +static u32 buffer[9] ATTRIBUTE_ALIGN(32); + + +s32 WBFS_Open(u32 device, u8 *discid) +{ + /* Open device */ + devFd = os_open(devFs[device], 1); + if (devFd < 0) + return devFd; + + /* Copy disc ID */ + memcpy(buffer, discid, 6); + + /* Setup vector */ + vector[0].data = buffer; + vector[0].len = 6; + + /* Open disc */ + return os_ioctlv(devFd, IOCTL_WBFS_OPEN, 1, 0, vector); +} + +void WBFS_Close(void) +{ + /* Close device */ + if (devFd >= 0) + os_close(devFd); + + /* Reset descriptor */ + devFd = -1; +} + +s32 WBFS_Read(void *outbuf, u32 len, u32 offset) +{ + s32 ret; + + /* Set buffers */ + buffer[0] = offset; + buffer[8] = len; + + /* Setup vector */ + vector[0].data = &buffer[0]; + vector[0].len = 4; + vector[1].data = &buffer[8]; + vector[1].len = 4; + vector[2].data = outbuf; + vector[2].len = len; + + /* Flush cache */ + os_sync_after_write(vector, sizeof(vector)); + os_sync_after_write(buffer, sizeof(buffer)); + + /* Read data */ + ret = os_ioctlv(devFd, IOCTL_WBFS_READ, 2, 1, vector); + if (ret) + return ret; + + /* Invalidate cache */ + os_sync_before_read(outbuf, len); + + return 0; +} diff --git a/cios/dip_orig/wbfs.h b/cios/dip_orig/wbfs.h new file mode 100644 index 0000000..67c376e --- /dev/null +++ b/cios/dip_orig/wbfs.h @@ -0,0 +1,28 @@ +/* + * DIP plugin for Custom IOS. + * + * Copyright (C) 2008-2010 Waninkoko, WiiGator. + * + * This program is free software: you can 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 . + */ + +#ifndef _WBFS_H_ +#define _WBFS_H_ + +/* Prototypes */ +s32 WBFS_Open (u32 device, u8 *disid); +void WBFS_Close(void); +s32 WBFS_Read (void *outbuf, u32 len, u32 offset); + +#endif diff --git a/cios/odip_frag/.gitignore b/cios/odip_frag/.gitignore new file mode 100644 index 0000000..9eca9b6 --- /dev/null +++ b/cios/odip_frag/.gitignore @@ -0,0 +1,2 @@ +build +bin diff --git a/cios/odip_frag/COPYING b/cios/odip_frag/COPYING new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/cios/odip_frag/COPYING @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/cios/odip_frag/LEEME.txt b/cios/odip_frag/LEEME.txt new file mode 100644 index 0000000..9ca2d7f --- /dev/null +++ b/cios/odip_frag/LEEME.txt @@ -0,0 +1,32 @@ +ODIP-Plugin +----------- + +Esta es un versión abierta del DIP-Plugin (intento de, por ahora) correspondiente al cIOS de Hermes, +la versión que lanzó los primeros días de Enero del 2010 (para ser usada con +el uLoader 3.6A). + + +Estado +------ + +No está funcionando correctamente. No es por ahora un producto para usuarios +finales sino para desarrolladores. + +Lamentablemente por ahora no tengo mucho tiempo para probarlo pero quería liberar +lo que tengo por ahora. + +Usandolo desde el uLoader, en modo DVD, en mi Wii con chip el juego se carga +correctamente. + +En modo USB, se detectan correctamente las particiones y cargue exitosamente 2 +juegos. No está funcionando la instalación de juegos. + +Tener en cuenta que esto es un producto de analizar el código binario y +generar código C, por lo tanto puede haber muchos errores, mi entendimiento +del HW de Wii y del funcionamiento de los módulo es limitado. + +Los nombres de las funciones pueden no ser correctos siempre, ya que son mi +interpretación de lo que el código hace. + + + Spaceman Spiff diff --git a/cios/odip_frag/MakeIt.bat b/cios/odip_frag/MakeIt.bat new file mode 100644 index 0000000..a6340c2 --- /dev/null +++ b/cios/odip_frag/MakeIt.bat @@ -0,0 +1,4 @@ + +make + +pause diff --git a/cios/odip_frag/Makefile b/cios/odip_frag/Makefile new file mode 100644 index 0000000..722625c --- /dev/null +++ b/cios/odip_frag/Makefile @@ -0,0 +1,149 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../libcios/include +#SCRIPTDIR := scripts.debug +SCRIPTDIR := scripts +BIN := bin + +#DEBUG := -DDEBUG + +#STRIPIOSPLUGIN := ../stripiosplugin/stripiosplugin.exe +STRIPIOSPLUGIN := ../stripios/stripios.exe + +LIBS := +LIBDIRS := + +CFLAGS+= $(DEBUG) + +#--------------------------------------------------------------------------------- +# the prefix on the compiler executables +#--------------------------------------------------------------------------------- +#$(DEVKITARM)/bin/ +#PREFIX := arm-eabi- +PREFIX := $(DEVKITARM)/bin/arm-eabi- +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +AR := $(PREFIX)ar +OBJCOPY := $(PREFIX)objcopy +LD := $(PREFIX)g++ +AS := $(PREFIX)g++ + +#--------------------------------------------------------------------------------- +# linker script +#--------------------------------------------------------------------------------- +LINKSCRIPT := $(ROOT)/$(SCRIPTDIR)/link.ld +SPECS := $(ROOT)/$(SCRIPTDIR)/nostart.specs + +ifeq ($(BUILDING),$(emptystring)) + +export ROOT := $(CURDIR) + + +all: + @[ -d $(BUILD) ] || mkdir -p $(BUILD) + @$(MAKE) -C $(BUILD) --no-print-directory -f $(CURDIR)/Makefile BUILDING=all +clean: + @echo clean ... + @rm -fr $(BUILD) $(BIN)/*.elf +else + +TARGET := $(notdir $(ROOT)) +STRIPIOSPLUGIN := $(ROOT)/$(STRIPIOSPLUGIN) +#---------------------------------------------------- +# MS Visual Studio Style Fix: +#---------------------------------------------------- +#STYLEFIX = 2>&1 | sed -e 's/\([a-zA-Z\.]\+\):\([0-9]\+\):\([0-9]\+:\)\?\(.\+\)/\1(\2):\4/' -e 's/undefined/error: undefined/' +STYLEFIX ?= + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +OUTPUT := $(ROOT)/$(BIN)/$(TARGET) +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(ROOT)/$(dir)/*.*))) + +OFILES := $(addsuffix _bin.o,$(BINFILES)) \ + $(CPPFILES:.cpp=_cpp.o) $(CFILES:.c=_c.o) \ + $(sFILES:.s=_s.o) $(SFILES:.S=_S.o) + +DEPENDS := $(OFILES:.o=.d) + +VPATH = $(foreach dir,$(SOURCES),$(ROOT)/$(dir)) + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +INCLUDE := $(foreach dir,$(INCLUDES),-I$(ROOT)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(ROOT)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ARCH = -mcpu=arm9tdmi -mtune=arm9tdmi -mthumb -mthumb-interwork -mbig-endian + +CFLAGS += -g $(ARCH) $(INCLUDE) -fno-strict-aliasing -Wall -Os -fomit-frame-pointer -ffast-math -fverbose-asm -Wpointer-arith -Winline -Wundef -g -ffunction-sections -fdata-sections -fno-exceptions +CFLAGS += -Wstrict-prototypes + + +AFLAGS = -g $(ARCH) -x assembler-with-cpp + +LDFLAGS = -g $(ARCH) -specs=$(SPECS) -T$(LINKSCRIPT) $(LIBPATHS) $(LIBS) -Wl,--gc-sections -Wl,-static -Wl,-Map,$(TARGET).map -nostartfiles + + +$(OUTPUT).bin: $(TARGET).elf + @echo Stripping plugin $(notdir $@) + $(OBJCOPY) -O binary $(TARGET).elf $(OUTPUT).bin +# arm-eabi-objcopy -O binary $(TARGET).elf $(OUTPUT).bin +# $(STRIPIOSPLUGIN) $< $@ + + +%.elf: $(OFILES) + @echo linking $(notdir $@) + @$(LD) -g -o $@ $(OFILES) $(LDFLAGS) $(STYLEFIX) + + +%_cpp.o : %.cpp + @echo $(notdir $<) + @$(CXX) -MMD -MF $*_cpp.d $(CFLAGS) -c $< -o$@ $(STYLEFIX) + +%_c.o : %.c + @echo $(notdir $<) + @$(CC) -MMD -MF $*_c.d $(CFLAGS) -c $< -o$@ $(STYLEFIX) + +%_s.o : %.s + @echo $(notdir $<) + @$(AS) -MMD -MF $*_s.d $(AFLAGS) -c $< -o$@ $(STYLEFIX) + +%_bin.o : %.bin + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +define bin2o + @echo -e "\t.section .rodata\n\t.align 4\n\t.global $(*)\n\t.global $(*)_end\n$(*):\n\t.incbin \"$(subst /,\\\\\\\\,$(shell echo $< | sed 's=/==;s=/=:/='))\"\n$(*)_end:\n" > $@.s + @$(CC) $(ASFLAGS) $(AFLAGS) -c $@.s -o $@ + @rm -rf $@.s +endef + +endif diff --git a/cios/odip_frag/bin/odip_frag.bin b/cios/odip_frag/bin/odip_frag.bin new file mode 100644 index 0000000000000000000000000000000000000000..f7955ef89f0243114ef27bfbba06eea652e0b603 GIT binary patch literal 9120 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 + +struct _ioctl{ + void *data; + u32 len; +}; + +struct _ipcreq +{ //ipc struct size: 32 + u32 cmd; //0 + s32 result; //4 + union { //8 + s32 fd; + u32 req_cmd; + }; + union { + struct { + char *filepath; + u32 mode; + } open; + struct { + void *data; + u32 len; + } read, write; + struct { + s32 where; + s32 whence; + } seek; + struct { + u32 ioctl; + void *buffer_in; + u32 len_in; + void *buffer_io; + u32 len_io; + } ioctl; + struct { + u32 ioctl; + u32 argcin; + u32 argcio; + struct _ioctl *argv; + } ioctlv; + u32 args[5]; + }; + + +} ATTRIBUTE_PACKED; + +#endif diff --git a/cios/odip_frag/include/module.h b/cios/odip_frag/include/module.h new file mode 100644 index 0000000..1ff6608 --- /dev/null +++ b/cios/odip_frag/include/module.h @@ -0,0 +1,84 @@ +#ifndef _MODULE_H_ +#define _MODULE_H_ + +/* Error codes */ +#define ERR_EINVAL -1 +#define ERR_ENOENT -6 +#define ERR_ENOMEM -22 +#define ERR_EIO 2 +#define ERR_EINCMD 128 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTL calls */ +#define IOCTL_DI_INQUIRY 0x12 +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_READ 0x71 +#define IOCTL_DI_WAITCOVERCLOSE 0x79 +#define IOCTL_DI_RESETNOTIFY 0x7E +#define IOCTL_DI_GETCOVER 0x88 +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_OPENPART 0x8B +#define IOCTL_DI_CLOSEPART 0x8C +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_LOWREAD 0xA8 +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_REPORTKEY 0xA4 +#define IOCTL_DI_READDVD 0xD0 +#define IOCTL_DI_STOPLASER 0xD2 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_DISC_BCA 0xDA +#define IOCTL_DI_REQERROR 0xE0 +#define IOCTL_DI_STOPMOTOR 0xE3 +#define IOCTL_DI_STREAMING 0xE4 + +#define IOCTL_DI_SETBASE 0xF0 +#define IOCTL_DI_GETBASE 0xF1 +#define IOCTL_DI_SETFORCEREAD 0xF2 +#define IOCTL_DI_GETFORCEREAD 0xF3 +#define IOCTL_DI_SETUSBMODE 0xF4 +#define IOCTL_DI_GETUSBMODE 0xF5 +#define IOCTL_DI_DISABLERESET 0xF6 +#define IOCTL_DI_CUSTOMCMD 0xFF + +#define IOCTL_DI_FRAG_SET 0xF9 +#define IOCTL_DI_MODE_GET 0xFA +#define IOCTL_DI_HELLO 0xFB + +#define IOCTL_DI_13 0x13 +#define IOCTL_DI_14 0x14 +#define IOCTL_DI_15 0x15 + +/* Constants */ +#define SECTOR_SIZE 0x800 + + +/* DIP struct */ +typedef struct { + /* DIP config */ + u32 low_read_from_device; + u32 dvdrom_mode; + u32 base; + u32 offset; + u32 has_id; + u32 partition; + u8 id[8]; + u32 currentError; // offset 0x20 + u8 disableReset; + u8 reading; + u8 frag_mode; +} __attribute__((packed)) dipstruct; + +extern dipstruct dip; + +/* Call original ioctl command */ +int handleDiCommand(u32 *inbuf, u32 *outbuf, u32 outbuf_size); + +#endif diff --git a/cios/odip_frag/include/syscalls.h b/cios/odip_frag/include/syscalls.h new file mode 100644 index 0000000..60de65b --- /dev/null +++ b/cios/odip_frag/include/syscalls.h @@ -0,0 +1,57 @@ +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "types.h" +#include "ipc.h" + +/* Prototypes */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +void os_puts(char *str); +s32 os_open(char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, struct _ioctl *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_set_uid(u32 pid, u32 uid); +void os_set_gid(u32 pid, u32 gid); +s32 os_ppc_boot(const char *filepath); +s32 os_ios_boot(const char *filepath, u32 flag, u32 version); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* Prototypes of functions exported from the hacked IOS */ +void dip_free(void *); +void dip_doReadHashEncryptedState(void); +u32 *dip_memcpy(u8 *, const u8 *, u32); +void *dip_alloc_aligned(u32, u32); +#endif diff --git a/cios/odip_frag/include/types.h b/cios/odip_frag/include/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/odip_frag/include/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/odip_frag/include/utils.h b/cios/odip_frag/include/utils.h new file mode 100644 index 0000000..0a6bcd3 --- /dev/null +++ b/cios/odip_frag/include/utils.h @@ -0,0 +1,24 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include "types.h" + +#define swab32(x) ((u32)( \ + (((u32)(x) & (u32)0x000000ffUL) << 24) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 8) | \ + (((u32)(x) & (u32)0x00ff0000UL) >> 8) | \ + (((u32)(x) & (u32)0xff000000UL) >> 24))) +#define swab16(x) ((u16)( \ + (((u16)(x) & (u16)0x00ffU) << 8) | \ + (((u16)(x) & (u16)0xff00U) >> 8))) + +# define ATTRIBUTE_PACKED __attribute__((packed)) +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) + + +void dip_memset(u8 *buf, u32 c, u32 size); + +void *VirtToPhys(void *address); +void *PhysToVirt(void *address); + +#endif diff --git a/cios/odip_frag/include/wbfs.h b/cios/odip_frag/include/wbfs.h new file mode 100644 index 0000000..ce782c5 --- /dev/null +++ b/cios/odip_frag/include/wbfs.h @@ -0,0 +1,14 @@ +#ifndef __WBFS_H__ +#define __WBFS_H__ + +#include "types.h" + +s32 usb_dvd_inserted(void); +s32 usb_read_device(u8 *outbuf, u32 size, u32 lba); +s32 usb_open_device(u32 device_nr, u8 *id, u32 partition); + +extern s32 usb_is_dvd; + +extern s32 usb_device_fd; + +#endif diff --git a/cios/odip_frag/scripts/link.ld b/cios/odip_frag/scripts/link.ld new file mode 100644 index 0000000..f0df044 --- /dev/null +++ b/cios/odip_frag/scripts/link.ld @@ -0,0 +1,104 @@ +OUTPUT_FORMAT("elf32-bigarm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + + +/* + Change exe start and ram start as needed for the custom IOS module. + Current settings are for the EHC module from IOS 31. actually this is free space in this IOS + */ + + +MEMORY { + exe(rwx) : ORIGIN = 0x1377C000, LENGTH = 0x2200 + ram(rw) : ORIGIN = 0x1377E200, LENGTH = 0x1E00 /* END 13780000 */ + /* end must not exceed mload ios_exe end */ +} + +__exe_start_virt__ = 0x1377C000; +__exe_start_phys__ = 0x1377C000; +__ram_start_virt__ = 0x1377E200; +__ram_start_phys__ = 0x1377E200; +__ios_info_table_start = 0x0; + +/* +orig: +MEMORY { + exe(rwx) : ORIGIN = 0x1377E000, LENGTH = 0x1600 + ram(rw) : ORIGIN = 0x1377F600, LENGTH = 0xA00 +} +__exe_start_virt__ = 0x1377E000; +__exe_start_phys__ = 0x1377E000; +__ram_start_virt__ = 0x1377F600; +__ram_start_phys__ = 0x1377F600; +__ios_info_table_start = 0x0; +*/ + + +/* + +uloader 5.1E: + + ehcmodule: + exe(rwx) : ORIGIN = 0x13700000, LENGTH = 0x6000 + ram(rw) : ORIGIN = 0x13706000, LENGTH = 0x2A000 // END 0x1372E000 + + fatffs: + exe(rwx) : ORIGIN = 0x13730000, LENGTH = 0xa800 + ram(rwx) : ORIGIN = 0x1373a800, LENGTH = 0x40000 // END 1377A800 + + odip: + exe(rwx) : ORIGIN = 0x1377E000, LENGTH = 0x1600 + ram(rw) : ORIGIN = 0x1377F600, LENGTH = 0xA00 + + mload: + exe(rwx) : ORIGIN = 0x138c0000, LENGTH = 0x4000 + ram(rw) : ORIGIN = 0x138c8000, LENGTH = 0x8000 // END 138D0000 + ios_exe(rw) : ORIGIN = 0x13700000, LENGTH = 0x80000 // END 13780000 + +cfg 5x dip_frag: + exe(rwx) : ORIGIN = 0x13700000, LENGTH = 0x2000 + ram(rw) : ORIGIN = 0x13702000, LENGTH = 0x40000 + +*/ + + + +SECTIONS +{ + .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } .debug_info 0 : { *(.debug_info) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } .note.arm.ident 0 : { KEEP (*(.note.arm.ident)) } + .init __exe_start_virt__ : AT (__exe_start_phys__) { . = .; KEEP (*(.init)) } > exe + .text ALIGN (0x20) : { + *(.text*) + *(.gnu.warning) + *(.rodata*) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) } > exe + .data __ram_start_virt__ : AT (__ram_start_phys__) { KEEP( *(.ios_data) ) *(.data*) *(.data1) *(.gnu.linkonce.d.*) . = ALIGN (4); __CTOR_LIST__ = ABSOLUTE (.); KEEP (*(SORT (.ctors*))) __CTOR_END__ = ABSOLUTE (.); __DTOR_LIST__ = ABSOLUTE (.); KEEP (*(SORT (.dtors*))) __DTOR_END__ = ABSOLUTE (.); + *(.dynamic) + *(.sdata*) + *(.dynbss) + *(.gnu.linkonce.s.*) . = ALIGN (4); *(.2ram.*) } > ram + .fini : { . = .; *(.fini) } > ram + .rodata ALIGN (0x4) : { + . = .; + *(.gnu.linkonce.r.*) } > ram + .rodata1 ALIGN (0x4) : { . = .; *(.rodata1) } > ram + .fixup ALIGN (0x4) : { . = .; *(.fixup) } > ram + .gcc_except_table ALIGN (0x4) : { . = .; *(.gcc_except_table) } > ram + .got ALIGN (0x4) : { *(.got.plt) *(.got) } > ram + + _ini_bss = . ; + .bss ALIGN (0x20) : { *(.scommon) + *(.dynsbss) + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON) KEEP( *(.ios_bss) ) } > ram + _len_bss = . - _ini_bss ; + + . = ALIGN(4); +} diff --git a/cios/odip_frag/scripts/nostart.specs b/cios/odip_frag/scripts/nostart.specs new file mode 100644 index 0000000..9c27dca --- /dev/null +++ b/cios/odip_frag/scripts/nostart.specs @@ -0,0 +1,2 @@ +*startfile: +crti%O%s crtbegin%O%s diff --git a/cios/odip_frag/source/ES_ioctlv_low.s b/cios/odip_frag/source/ES_ioctlv_low.s new file mode 100644 index 0000000..0bcbeef --- /dev/null +++ b/cios/odip_frag/source/ES_ioctlv_low.s @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + + .align 2 + .code 16 + .global in_ES_ioctlv + .thumb_func +in_ES_ioctlv: + + push {r2-r6} + push {lr} + bl ES_ioctlv+1 + pop {r1} + pop {r2-r6} + bx r1 + + .global out_ES_ioctlv + .thumb_func +out_ES_ioctlv: + push {r4-r6,lr} + sub sp, sp, #0x20 + ldr r5, [r0,#8] + add r1, r0, #0 + ldr r3, = 0x201000D5 + bx r3 diff --git a/cios/odip_frag/source/crt0.s b/cios/odip_frag/source/crt0.s new file mode 100644 index 0000000..6c6e342 --- /dev/null +++ b/cios/odip_frag/source/crt0.s @@ -0,0 +1,107 @@ +/* + Custom IOS module for Wii. + Copyright (C) 2008 neimod. + Copyright (C) 2010 Spaceman Spiff. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .global _start + + .align 4 + .code 32 + + +/******************************************************************************* + * + * crt0.s - IOS module startup code + * + ******************************************************************************* + * + * + * v1.0 - 26 July 2008 - initial release by neimod + * v1.1 - 5 September 2008 - prepared for public release + * + */ + +.EQU dip_plugin_id, 0x12340001 + +.EQU ios36_dvd_read_controlling_data, 0x2022DDAC +.EQU ios36_handle_di_cmd_reentry, 0x20201010 +.EQU ios36_shared_alloc_aligned, 0x20200b9c +.EQU ios36_shared_free, 0x20200b70 +.EQU ios36_memcpy, 0x20205dc0 +.EQU ios36_fatal_di_error, 0x20200048 +.EQU ios36_doReadHashEncryptedState, 0x20202b4c +.EQU ios36_printf, 0x20203934 + +.EQU ios38_dvd_read_controlling_data, 0x2022cdac +.EQU ios38_handle_di_cmd_reentry, 0x20200d38 +.EQU ios38_shared_alloc_aligned, 0x202008c4 +.EQU ios38_shared_free, 0x20200898 +.EQU ios38_memcpy, 0x20205b80 +.EQU ios38_fatal_di_error, 0x20200048 +.EQU ios38_doReadHashEncryptedState, 0x20202874 +.EQU ios38_printf, 0x2020365c + + .global addr_dvd_read_controlling_data + .global addr_di_cmd_reentry + .global addr_ios_shared_alloc_aligned + .global addr_ios_shared_free + .global addr_ios_memcpy + .global addr_ios_fatal_di_error + .global addr_ios_doReadHashEncryptedState + .global addr_ios_printf + +/* Begin DIP-Plugin compatible Table */ + .long DI_EmulateCmd + .long dip_plugin_id +addr_dvd_read_controlling_data: + .long ios36_dvd_read_controlling_data +addr_di_cmd_reentry: + .long ios36_handle_di_cmd_reentry + 1 +addr_ios_shared_alloc_aligned: + .long ios36_shared_alloc_aligned + 1 +addr_ios_shared_free: + .long ios36_shared_free + 1 +addr_ios_memcpy: + .long ios36_memcpy + 1 +addr_ios_fatal_di_error: + .long ios36_fatal_di_error + 1 +addr_ios_doReadHashEncryptedState: + .long ios36_doReadHashEncryptedState + 1 +addr_ios_printf: + .long ios36_printf + 1 + + .long 0 @ reserved @ + .long 0 @ reserved @ + .long 0 @ reserved @ + .long 0 @ reserved @ + .long filename_data + .long bca_bytes + .long in_ES_ioctlv + + .global mem_bss +mem_bss: + .long _ini_bss + + .global mem_bss_len +mem_bss_len: + .long _len_bss + + .align 4 +_start: + .end diff --git a/cios/odip_frag/source/debug.c b/cios/odip_frag/source/debug.c new file mode 100644 index 0000000..1a10e41 --- /dev/null +++ b/cios/odip_frag/source/debug.c @@ -0,0 +1,180 @@ +#include + +#include // for the s_printf function + +#include + +#include "usb.h" + +#define os_puts usb_puts + +#ifdef DEBUG + +static char mem_cad[32]; + +void int_char(int num) +{ +int sign=num<0; +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<10;n++) + { + m=num % 10;num/=10;if(m<0) m=-m; + mem_cad[25-n]=48+m; + } + + mem_cad[26]=0; + + n=0;m=16; + if(sign) {mem_cad[n]='-';n++;} + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void uint_char(unsigned int num) +{ +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<10;n++) + { + m=num % 10;num/=10; + mem_cad[25-n]=48+m; + } + + mem_cad[26]=0; + + n=0;m=16; + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void hex_char(u32 num) +{ +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<8;n++) + { + m=num & 15;num>>=4; + if(m>=10) m+=7; + mem_cad[23-n]=48+m; + } + + mem_cad[24]=0; + + n=0;m=16; + + mem_cad[n]='0';n++; + mem_cad[n]='x';n++; + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void s_printf(char *format,...) +{ + va_list opt; + + char out[2]=" "; + + int val; + + char *s; + + va_start(opt, format); + + while(format[0]) + { + if(format[0]!='%') {out[0]=*format++;os_puts(out);} + else + { + format++; + switch(format[0]) + { + case 'd': + case 'i': + val=va_arg(opt,int); + int_char(val); + + os_puts(mem_cad); + + break; + + case 'u': + val=va_arg(opt, unsigned); + uint_char(val); + + os_puts(mem_cad); + + break; + + case 'x': + val=va_arg(opt,int); + hex_char((u32) val); + os_puts(mem_cad); + + break; + + case 's': + s=va_arg(opt,char *); + os_puts(s); + break; + + } + format++; + } + + } + + va_end(opt); + + +} + +#endif diff --git a/cios/odip_frag/source/di.c b/cios/odip_frag/source/di.c new file mode 100644 index 0000000..4837db2 --- /dev/null +++ b/cios/odip_frag/source/di.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * + * This program is free software; you can 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 + */ + +#include + +#include +#include + +#define DVDIO_SIZE 32 + +#define DVDIO_ADDR 0xD006000 +#define SECTORSIZE 2048 +#define MAX_SIZE_DI (0xFF << 15) + +static u32 DIP_adjust_read_size(u8 *buf, u32 size, u32 align); + +int DIP_StopMotor(void) +{ + u32 inbuf[8]; + inbuf[0] = IOCTL_DI_STOPMOTOR << 24; + inbuf[1] = 0; + inbuf[2] = 0; + return handleDiCommand(inbuf,NULL,0); +} + + +int DIP_CustomCommand(u8 *cmd, u8 *ans) +{ + volatile unsigned long *dvdio = (volatile unsigned long *) DVDIO_ADDR; + + dip_memcpy((u8 *) dvdio, cmd, DVDIO_SIZE); + os_sync_after_write((u8 *) dvdio, DVDIO_SIZE); + + // DI Control Register, wait until ready + while (dvdio[7] & 1) // 0xD00601C + ; + + dip_memcpy(ans, (u8 *) dvdio, DVDIO_SIZE); + os_sync_after_write(ans, DVDIO_SIZE); + + return dvdio[8]; // 0xD006020 +} + +int DIP_ReadDVDVideo(void *dst, u32 len, u32 lba) +{ + int res; + int tries = 0; + u32 inbuf[8]; + do { + inbuf[0] = IOCTL_DI_READDVD << 24; + inbuf[1] = 0; + inbuf[2] = 0; + inbuf[3] = len >> 11; // Convert to 2048 blocks (dvd sector) + inbuf[4] = lba; + os_sync_before_read(dst, len); + res = handleDiCommand(inbuf, dst, len); + tries++; + } while (res && tries < 32); + return res; +} + + +int DIP_ReadDVD(void *dst, u32 len, u32 sector) +{ + u32 inbuf[8]; + int res; + int tries = 0; + do { + inbuf[0] = IOCTL_DI_LOWREAD << 24; // IOCTL_DI_LOWREAD A8 + inbuf[1] = len; + inbuf[2] = sector; + + os_sync_before_read(dst, len); + res =handleDiCommand(inbuf, dst, len); + tries++; + } while (res && tries < 8); + return res; +} + + +int DIP_ReadDVDRom(u8 *outbuf, u32 len, u32 offset) +{ + u32 cnt = 0; + int res; + + if (len == 0) + return 0; + + u32 lba = offset >> 9; // offset / 512 + u32 size; + + while (cnt < len) { + u32 skip; + size = len - cnt; + + skip = (offset > (lba << 9)) ? (offset - (lba << 9)) << 2 : 0; + + res = DIP_adjust_read_size(outbuf+ cnt, size, SECTORSIZE); + + if (skip || !res) { + if ((skip + size) > SECTORSIZE) + size = SECTORSIZE - skip; + u8 *mem = dip_alloc_aligned(SECTORSIZE, 0x20); + if (!mem) + return -1; + + res = DIP_ReadDVDVideo(mem, SECTORSIZE, lba); + if (res == 0) { + dip_memcpy(outbuf+cnt, mem + skip, size); + os_sync_after_write(outbuf+cnt, size); + } + dip_free(mem); + } else { + size = res; + if (size >= MAX_SIZE_DI) + size = MAX_SIZE_DI; + res = DIP_ReadDVDVideo(outbuf+cnt, size, lba); // inlined function + /* + u32 tries = 0; + do { + u32 inbuf[8]; + + inbuf[0] = IOCTL_DI_READDVD << 24; + inbuf[1] = 0; + inbuf[2] = 0; + inbuf[3] = size >> 11; + inbuf[4] = lba; + os_sync_before_read(outbuf, size); + res = handleDiCommand(inbuf, (u32 *) outbuf, size); + tries = tries + 1; + } while (res != 0 && tries < 15); + */ + } + + if (res) + return res; + + cnt += size; + lba += (size + skip) >> 11; + } + return res; +} + +/* si retorna cero, el buffer, no esta alineado. */ + +// desde dip_plugin +#define DMA1_START_ADDRESS 0x00000000 +#define DMA1_END_ADDRESS 0x01800000 +#define DMA2_START_ADDRESS 0x10000000 +#define DMA2_END_ADDRESS 0x13618000 + + +u32 DIP_adjust_read_size(u8 *outbuf, u32 size, u32 alignment) +{ + u32 mem; + int ret = 0; + + /* Output buffer address */ + mem = (u32)outbuf; + + /* Check for memory alignment */ + if (!(mem & (0x1f))) { + u32 dmalen = 0; + + /* DMA1 range check */ + if ((mem >= DMA1_START_ADDRESS) && (mem < DMA1_END_ADDRESS)) + dmalen = (DMA1_END_ADDRESS - mem); + + /* DMA2 range check */ + if ((mem >= DMA2_START_ADDRESS) && (mem < DMA2_END_ADDRESS)) + dmalen = (DMA2_END_ADDRESS - mem); + + if (dmalen >= alignment) + ret = (dmalen < size) ? dmalen : size; + ret -= (ret & (alignment-1)); + } + + return ret; +} \ No newline at end of file diff --git a/cios/odip_frag/source/es_ioctlv.c b/cios/odip_frag/source/es_ioctlv.c new file mode 100644 index 0000000..8ee2508 --- /dev/null +++ b/cios/odip_frag/source/es_ioctlv.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + +#include + +#include + +#include + +extern int in_ES_ioctlv(void *); +extern int out_ES_ioctlv(void *); + +//void ios_sync_before_read(void* ptr, int size); +//void ios_sync_after_write(void* ptr, int size); + +static u32 inside_ES_ioctl; + +int ES_ioctlv(struct _ipcreq *dat ) +{ + int r; + u32 ios,version; + + inside_ES_ioctl = 1; + + os_sync_before_read(dat, sizeof(struct _ipcreq)); + + if(dat->ioctlv.ioctl==8) { // reboot + os_sync_before_read( (void *) dat->ioctlv.argv[0].data, dat->ioctlv.argv[0].len); + ios=*(((volatile u32 *)dat->ioctlv.argv[0].data)+1) ; + version=1; + os_sync_before_read((void *) 0x3140,8); + *((volatile u32 *) 0x3140)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision + *((volatile u32 *) 0x3188)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision + os_sync_after_write((void *) 0x3140,4); + os_sync_after_write((void *) 0x3188,4); + inside_ES_ioctl = 0; + return 0; + } + r=out_ES_ioctlv(dat); + inside_ES_ioctl = 0; + return r; +} + diff --git a/cios/odip_frag/source/frag.c b/cios/odip_frag/source/frag.c new file mode 100644 index 0000000..c3969c9 --- /dev/null +++ b/cios/odip_frag/source/frag.c @@ -0,0 +1,267 @@ +/* + * DIP plugin for Custom IOS (FRAG mode) + * + * Copyright (C) 2010 Waninkoko, WiiGator, oggzee. + * + * This program is free software: you can 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 . + */ + +// frag list by oggzee + +#include + +//#include "dip_calls.h" +#include "syscalls.h" +#include "usbstorage.h" +#include "sdhc.h" +#include "frag.h" + +#define DEV_NONE 0 +#define DEV_USB 1 +#define DEV_SDHC 2 + +#define MAX_IDX 640 // 640*16MB = 10GB +#define IDX_CHUNK 32768 // 16MB in sectors units: 16*1024*1024/512 +#define IDX_SHIFT 15 // 1<<15 = 32768 + +static u16 frag_idx[MAX_IDX] = { 0 }; +static u32 frag_init = 0; +static u32 frag_dev = 0; +//static FragList fraglist_data = { 0 }; +static FragList *frag_list = 0; +static u8 sector_buf[512] __attribute__ ((aligned (32))); + +void optimize_frag_list(void); + +// NOTE: all local variables are declared static +// because stack size is rather limited + +// NOTE2: hmm, memcpy and char array writing seems +// to only work if length is word aligned... cache issue? + +s32 Frag_Init(u32 device, void *fraglist, int size) +{ + static int ret; + if (frag_init) Frag_Close(); + if (!device || device > 2) return -1; + if (!size) return -1; + if (size > sizeof(FragList)) return -2; + + if (device == DEV_USB) { + ret = usbstorage_Init(); + } else { + ret = sdhc_Init(); + } + if (ret) return ret; + frag_dev = device; + //frag_list = &fraglist_data; + // frag list is incompatible with fatffs + // because both need a large-ish chunk of mem + // so we will use the same adress as fatffs + frag_list = (void*)0x13730000; + os_sync_before_read(fraglist, size); + memset(frag_list, 0, sizeof(FragList)); + memcpy(frag_list, fraglist, size); + os_sync_after_write(fraglist, size); + optimize_frag_list(); + frag_init = 1; + return size; +} + +void Frag_Close(void) +{ + frag_init = 0; + frag_list = 0; + frag_dev = 0; +} + + +void optimize_frag_list(void) +{ + int i; + u32 off; + u16 idx = 0; + for (i=0; i= frag_list->frag[idx].offset + frag_list->frag[idx].count) + && (idx + 1 < frag_list->num)) + { + idx++; + } + frag_idx[i] = idx; + } +} + +// in case a sparse block is requested, +// the returned poffset might not be equal to requested offset +// the difference should be filled with 0 +int frag_get(FragList *ff, u32 offset, u32 count, + u32 *poffset, u32 *psector, u32 *pcount) +{ + static int i; + static u32 delta; + static u32 idx_off; + static u32 start_idx; + + // optimize seek inside frag list + // jump to a precalculated index + idx_off = offset >> IDX_SHIFT; + if (idx_off > MAX_IDX) idx_off = MAX_IDX - 1; + start_idx = frag_idx[idx_off]; + + //printf("frag_get(%u %u)\n", offset, count); + for (i=start_idx; inum; i++) { + if (ff->frag[i].offset <= offset + && ff->frag[i].offset + ff->frag[i].count > offset) + { + delta = offset - ff->frag[i].offset; + *poffset = offset; + *psector = ff->frag[i].sector + delta; + *pcount = ff->frag[i].count - delta; + if (*pcount > count) *pcount = count; + goto out; + } + if (ff->frag[i].offset > offset + && ff->frag[i].offset < offset + count) + { + delta = ff->frag[i].offset - offset; + *poffset = ff->frag[i].offset; + *psector = ff->frag[i].sector; + *pcount = ff->frag[i].count; + count -= delta; + if (*pcount > count) *pcount = count; + goto out; + } + } + // not found + if (offset + count > ff->size) { + // error: out of range! + return -2; + } + // if inside range, then it must be just sparse, zero filled + // return empty block at the end of requested + *poffset = offset + count; + *psector = 0; + *pcount = 0; + out: + //printf("=>(%u %u %u)\n", *poffset, *psector, *pcount); + return 0; +} + + +// offset and len in sectors +int frag_read_sect(u32 offset, u8 *data, u32 len) +{ + static u32 off_ret; + static u32 sector; + static u32 count; + static u32 delta; + static int ret; + + while (len) { + //int frag_get(FragList *ff, u32 offset, u32 count, + // u32 *poffset, u32 *psector, u32 *pcount) + ret = frag_get(frag_list, offset, len, + &off_ret, §or, &count); + if (ret) return ret; // err + delta = off_ret - offset; + if (delta) { + // sparse block, fill with 0 + memset(data, 0, delta << 9); + offset += delta; + data += delta << 9; + len -= delta; + } + if (count) { + //int read_sector(void *ign, u32 lba, u32 count, void*buf) + //ret = read_sector(0, sector, count, data); + //if (ret) return ret; + //bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) + //ret = usbstorage_ReadSectors(sector, count, data); + if (frag_dev == DEV_USB) { + ret = __usbstorage_Read(sector, count, data); + } else { + ret = sdhc_Read(sector, count, data); + } + if (!ret) return -3; + offset += count; + data += count << 9; + len -= count; + } + if (delta + count == 0) { + // (should never happen) + return -4; + } + } + + return 0; +} + +// offset is pointing 32bit words to address the whole dvd, len is in bytes +int frag_read_partial(u32 offset, u8 *data, u32 len, u32 *read_len) +{ + static int ret; + static u32 off_sec; + static u32 mod; + static u32 rlen; + + off_sec = offset >> 7; // word to sect + mod = (offset & ((512-1) >> 2)) << 2; // offset from start of sector in bytes + rlen = 512 - mod; // remaining len from mod to end of sector + if (rlen > len) rlen = len; + if (rlen == 512) rlen = 0; // don't read whole sectors + if (rlen) { + ret = frag_read_sect(off_sec, sector_buf, 1); + if (ret) return ret; + memcpy(data, sector_buf + mod, rlen); + } + *read_len = rlen; + return 0; +} + +// woffset is pointing 32bit words to address the whole dvd, len is in bytes +s32 Frag_Read(void *data, u32 len, u32 woffset) +{ + static int ret; + static u32 rlen; + static u32 off_sec; + static u32 len_sec; + + // read leading partial non sector aligned data + ret = frag_read_partial(woffset, data, len, &rlen); + if (ret) return ret; + woffset += rlen >> 2; + data = (u8*)data + rlen; + len -= rlen; + if (len >= 512) { + // read sector aligned data + off_sec = woffset >> 7; // word to sect + len_sec = len >> 9; // byte to sect + ret = frag_read_sect(off_sec, data, len_sec); + if (ret) return ret; + woffset += len_sec << 7; + data = (u8*)data + (len_sec << 9); + len -= len_sec << 9; + } + if (len) { + // read trailing partial non sector aligned data + ret = frag_read_partial(woffset, data, len, &rlen); + if (ret) return ret; + len -= rlen; + } + if (len) return -5; // should never happen + // success + return 0; +} + diff --git a/cios/odip_frag/source/frag.h b/cios/odip_frag/source/frag.h new file mode 100644 index 0000000..9355b4a --- /dev/null +++ b/cios/odip_frag/source/frag.h @@ -0,0 +1,33 @@ + +#define FRAG_MAX 20000 + +typedef struct +{ + u32 offset; // file offset, in sectors unit + u32 sector; + u32 count; +} Fragment; + +typedef struct +{ + u32 size; // num sectors + u32 num; // num fragments + u32 maxnum; + Fragment frag[FRAG_MAX]; +} FragList; + +int set_frag_list(FragList *p, int size); + +// in case a sparse block is requested, +// the returned poffset might not be equal to requested offset +// the difference should be filled with 0 +int frag_get(FragList *ff, u32 offset, u32 count, + u32 *poffset, u32 *psector, u32 *pcount); + +// woffset is pointing 32bit words to address the whole dvd, len is in bytes +int frag_read(u32 woffset, u8 *data, u32 len); + +s32 Frag_Init(u32 device, void *fraglist, int size); +void Frag_Close(void); +s32 Frag_Read(void *data, u32 len, u32 woffset); + diff --git a/cios/odip_frag/source/module.c b/cios/odip_frag/source/module.c new file mode 100644 index 0000000..0dfbe32 --- /dev/null +++ b/cios/odip_frag/source/module.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * Copyright (C) 2010 Waninkoko + * + * This program is free software; you can 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 + */ + +#include +#include +#include +#include + +#include +#include +#include "frag.h" + +#include + +dipstruct dip= {0}; + +#define WII_DVD_SIGNATURE 0x5D1C9EA3 + +#define READINFO_SIZE_DATA 32 + +#define BCADATA_SIZE 64 +const u8 bca_bytes[BCADATA_SIZE] ATTRIBUTE_ALIGN(32) = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +void dummy_function(u32 *outbuf, u32 outbuf_size) +{ +} + + +// From Waninkoko dip_plugin + +/* Return codes */ +#define DIP_EIO 0xA000 + +/* Disc lengths */ +#define DVD5_LENGTH 0x46090000 +#define DVD9_LENGTH 0x7ED38000 + +/* Error codes */ +#define ERROR_BLOCK_RANGE 0x52100 +#define ERROR_WRONG_DISC 0x53100 + +/* Disc types */ +enum { +DISC_UNKNOWN = 0, +DISC_DVD5, +DISC_DVD9, +}; + +int dvd_type=0; + +s32 __DI_CheckOffset(u32 offset) +{ +u32 offmax; + +/* Check disc type */ + switch (dvd_type) { + /* Single layer */ + case DISC_DVD5: + offmax = DVD5_LENGTH; + break; + + /* Dual layer */ + case DISC_DVD9: + offmax = DVD9_LENGTH; + break; + + default: + return 0; + } + + /* Check offset */ + if (offset >= offmax) { + /* Set error */ + dip.currentError = ERROR_BLOCK_RANGE; + + /* I/O error */ + return DIP_EIO; + } + +return 0; +} + + + +// CISO mem area +int ciso_lba=-1; +int ciso_size=0; +u32 *table_lba=NULL; + +extern s32 fat_mode; + +u8 mem_index[2048] __attribute__ ((aligned (32))); + +int read_from_device_out(void *outbuf, u32 size, u32 lba) +{ + + if (dip.frag_mode) + return Frag_Read((u8 *)outbuf, size, lba); + + if (dip.has_id) + return usb_read_device((u8 *)outbuf, size, lba); + + if (dip.dvdrom_mode) + return DIP_ReadDVDRom((u8 *) outbuf, size, lba); + + return DIP_ReadDVD((u8 *) outbuf, size, lba); +} + +int read_from_device(void *outbuf, u32 size, u32 lba) +{ +int r=-1; +int l; + + r = __DI_CheckOffset(lba); + if (r) return r; + + r=-1; + + lba += dip.base + dip.offset; + + if (dip.has_id) + { + /* Free memory */ + + if(fat_mode<=0) + { + if (table_lba) + os_heap_free(0, table_lba); + table_lba=NULL; + + ciso_lba=-1; + + } + + return read_from_device_out(outbuf, size, lba); + } + + if(ciso_lba>=0 && ciso_lba!=0x7fffffff) + { + u32 lba_glob=0; + + + if(table_lba==NULL) table_lba= (void *) os_heap_alloc_aligned(0, 2048*4, 32); // from global heap + //ciso_lba=265; + /* Allocate memory */ + u8 * buff = dip_alloc_aligned(0x800, 32); + if (!buff) + ciso_lba=-1; + + if(ciso_lba>=0) + { + + while(1) + { + lba_glob=(ciso_lba+16)<<9; + + + r=read_from_device_out(buff, 2048, ciso_lba<<9); // read 16 cached sectors + if(r<0) {ciso_lba=-1;break;} + + if((buff[0]=='C' && buff[1]=='I' && buff[2]=='S' && buff[3]=='O')) break; + else + { + if(ciso_lba!=0) {ciso_lba=0;continue;} + ciso_lba=-1; + } + + break; + } + } + // if(ciso_lba>=0 && table_lba && buff) ciso_lba=0x7fffffff; + + if(ciso_lba>=0 && table_lba) + { + + ciso_size=(((u32)buff[4])+(((u32)buff[5])<<8)+(((u32)buff[6])<<16)+(((u32)buff[7])<<24))/4; + + dip_memset(mem_index,0,2048); + + + for(l=0;l<16384;l++) + { + if(((l+8) & 2047)==0 && (l+8)>=2048) + { + r=read_from_device_out(buff, 2048, (ciso_lba+((l+8)>>11))<<9); // read 16 cached sectors + if(r<0) {ciso_lba=-1;break;} + } + + if((l & 7)==0) table_lba[l>>3]=lba_glob; + + if(buff[(8+l) & 2047]) + { + mem_index[l>>3]|=1<<(l & 7); + lba_glob+=ciso_size; + } + } + + if(ciso_lba>=0) ciso_lba=0x7fffffff; + } + + /* Free memory */ + if (buff) + dip_free(buff); + } + + + if(ciso_lba==0x7fffffff) + { + u32 temp=(lba)/ciso_size; + + + u32 read_lba=table_lba[temp>>3]; + + for(l=0;l<(temp & 7);l++) if((mem_index[temp>>3]>>l) & 1) read_lba+=ciso_size; + + read_lba+=(lba) & ((ciso_size)-1); + + + r=read_from_device_out(outbuf, size, read_lba); + if(r<0) return r; + + + } + else + { + /* Free memory */ + if (table_lba) + os_heap_free(0, table_lba); + + table_lba=NULL; + + return read_from_device_out(outbuf, size, lba); + + } + + +return r; + + +} + +// From Waninkoko dip_plugin + +void __DI_CheckDisc(void) +{ +void *buffer; +s32 ret; + + /* Allocate buffer */ + buffer = (void *) os_heap_alloc_aligned(0, SECTOR_SIZE, 32); + if (!buffer) + return; + + /* Read second layer */ + ret = read_from_device(buffer, SECTOR_SIZE, 0x47000000); + + /* Set disc type */ + dvd_type = (!ret) ? DISC_DVD9 : DISC_DVD5; + + /* Free buffer */ + os_heap_free(0, buffer); + +} +int read_id_from_image(u32 *outbuf, u32 outbuf_size) +{ + u32 res; + + res= read_from_device(outbuf, READINFO_SIZE_DATA, 0); + if(res<0) return res; + + if (outbuf[6] == WII_DVD_SIGNATURE) { + + extern u8 * addr_dvd_read_controlling_data; + + addr_dvd_read_controlling_data[0] = 1; + + if (!addr_dvd_read_controlling_data[1]) + dip_doReadHashEncryptedState(); + } + return res; +} + + +/* +Ioctl 0x13 -> usada por la función Disc_USB_DVD_Wait(), checkea si hay disco montado desde la unidad DVD. Solo se usa en uLoader + +Ioctl 0x14 -> equivale a Ioctl 0x88, pero exceptuando el uso de DVD, devuelve un estado fake para WBFS y DVD USB + +Ioctl 0x15 -> equivale a Ioctl 0x7a y devuelve el registro tal cual, exceptuando el bit 0 (para indicar la presencia del disco siempre) + */ + +static int _noinit=1; + +extern void * mem_bss; +extern int mem_bss_len; + +int DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 outbuf_size) +{ + int res = 0; + + u8 cmd = ((u8 *) inbuf)[0]; + +#ifdef DEBUG + s_printf("DIP::DI_EmulateCmd(%x, %x, %x)\n", cmd, outbuf, outbuf_size); +#endif + + if(_noinit) + { + + dip_memset(mem_bss, 0, mem_bss_len); + + os_sync_after_write(mem_bss, mem_bss_len); + + _noinit=0; + } + + + if (cmd != IOCTL_DI_REQERROR) + dip.currentError = 0; + + switch (cmd) { + case IOCTL_DI_REQERROR: + { + + if (dip.currentError || dip.has_id) + { + dip_memset((u8 *) outbuf, 0, outbuf_size); + outbuf[0] = dip.currentError; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + } + else goto call_original_di; + } + + break; + + case IOCTL_DI_SEEK: + res = 0; + + if (!dip.dvdrom_mode && !dip.has_id) { + res = handleDiCommand(inbuf, outbuf, outbuf_size); + + } + break; + case IOCTL_DI_WAITCOVERCLOSE: + if (!dip.has_id) + goto call_original_di; + res = 0; + break; + + case IOCTL_DI_STREAMING: + if (!dip.dvdrom_mode && + !dip.has_id) + goto call_original_di; + dip_memset((u8*) outbuf, 0, outbuf_size); + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETBASE: + dip.base = inbuf[1]; + res = 0; + break; + + case IOCTL_DI_GETBASE: + outbuf[0] = dip.base; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETFORCEREAD: + dip.low_read_from_device = inbuf[1]; + res = 0; + break; + + case IOCTL_DI_GETFORCEREAD: + outbuf[0] = dip.low_read_from_device; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETUSBMODE: { + dip.has_id = inbuf[1]; + dip.frag_mode = 0; + // Copy id + if (dip.has_id) { + dip_memcpy(dip.id, (u8 *)&(inbuf[2]), 6); + } + dip.partition = inbuf[5]; + res = 0; + break; + } + + case IOCTL_DI_GETUSBMODE: + outbuf[0] = dip.has_id; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + // Set FRAG mode + case IOCTL_DI_FRAG_SET: + { + u32 device = inbuf[1]; + void *fraglist = (void*)inbuf[2]; + int size = inbuf[3]; + + // Close frag + Frag_Close(); + + // Disable mode + dip.frag_mode = 0; + dip.has_id = 0; + *outbuf = 0; + + // Check device + if (device && fraglist && size) { + // Convert address + fraglist = VirtToPhys(fraglist); + + // Open device + res = Frag_Init(device, fraglist, size); + *outbuf = res; + + // Enable mode + if (res > 0) { + dip.frag_mode = 1; + dip.has_id = device; + } + } + + res = 0; + break; + } + + // Get IO mode + case IOCTL_DI_MODE_GET: + { + /* return all mode bits */ + *outbuf = 0; + if (dip.frag_mode) *outbuf = 0x10; + else if (fat_mode) *outbuf = 0x08; + else if (dip.has_id) *outbuf = 0x04; + else if (dip.dvdrom_mode) *outbuf = 0x01; + break; + } + + // debug stuff + case IOCTL_DI_HELLO: + { + dip_memcpy((u8*)outbuf, (u8*)"HELO", 4); + outbuf[1] = dip.has_id; + outbuf[2] = dip.frag_mode; + break; + } + + + case IOCTL_DI_DISABLERESET: + dip.disableReset = *((u8 *) (&inbuf[1])); + res = 0; + break; + + case IOCTL_DI_13: + case IOCTL_DI_14: { + volatile unsigned long *dvdio = (volatile unsigned long *) 0xD006000; + if (outbuf == NULL) { + res = 0; + break; + } + if (cmd == 0x13) { + + if (dip.has_id && usb_is_dvd){ + outbuf[0] = (usb_dvd_inserted() == 0)?0:2; + } else { + if (!dip.has_id || usb_device_fd<0) outbuf[0] = (dvdio[1] & 1)?0:2; + else outbuf[0] = 2; + } + + + } else { + + // ioctl 0x14 + if (!dip.has_id || usb_device_fd<0) outbuf[0] = (dvdio[1] & 1)?0:2; + else {outbuf[0] = 0x2;} + + } + + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + } + case IOCTL_DI_15: { + volatile unsigned long *dvdio = (volatile unsigned long *) 0xD006000; + outbuf[0] = dvdio[1] & (~0x00000001); + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + } + + case IOCTL_DI_CUSTOMCMD: + res = DIP_CustomCommand((u8 *)((u32) inbuf[1] & 0x7FFFFFFF), (u8 *) outbuf); + break; + + case IOCTL_DI_OFFSET: { + if (dip.dvdrom_mode || dip.has_id) { + dip.offset = ((inbuf[1] << 30 ) | inbuf[2]) & 0xFFFF8000; // ~(SECTOR_SIZE >> 2) + res = 0; + } else + goto call_original_di; + break; + } + case IOCTL_DI_REPORTKEY: + if (!dip.dvdrom_mode && !dip.has_id) + goto call_original_di; + res = 0xA000; + dip.currentError = 0x53100; + break; + + case IOCTL_DI_DISC_BCA: { + u32 cont = 0; + os_sync_before_read((u8 *) bca_bytes, BCADATA_SIZE); + while (bca_bytes[cont] == 0) { + cont++; + if (cont == 64) + goto call_original_di; + } + dip_memcpy((u8 *) outbuf, bca_bytes, BCADATA_SIZE); + os_sync_after_write(outbuf,BCADATA_SIZE); + res = 0; + break; + } + + case IOCTL_DI_READ: { + dip.reading = 1; + if (!dip.low_read_from_device) + res = handleDiCommand(inbuf, outbuf, outbuf_size); + else + res = read_from_device(outbuf, inbuf[1], inbuf[2]); + dip.reading = 0; + + break; + } + case IOCTL_DI_READID: { + u32 cmdRes; + + cmdRes = 0; + + res=0; + + if (!dip.has_id) cmdRes = handleDiCommand(inbuf, outbuf, outbuf_size); + + + if (cmdRes || (dip.base | dip.offset) || dip.has_id || ((u32 *) outbuf)[6] != WII_DVD_SIGNATURE) + { + dip.dvdrom_mode = (cmdRes==0)?0:1; + res = read_id_from_image(outbuf, outbuf_size); + } + else + dip.dvdrom_mode = 0; + if (!res) + __DI_CheckDisc(); + + break; + } + case IOCTL_DI_RESET: { + if (dip.disableReset) { + res = 0; + break; + } + dip.reading = 0; + dip.low_read_from_device = 0; + dip.dvdrom_mode = 0; + dip.base = 0; + dip.offset = 0; + dip.currentError = 0; + + if (!dip.has_id) + { + ciso_lba=dip.partition-1; + goto call_original_di; + } + + DIP_StopMotor(); + if (!dip.frag_mode) { + res = usb_open_device(dip.has_id - 1, &(dip.id[0]), dip.partition); + } + break; + } + case IOCTL_DI_UNENCREAD: + case IOCTL_DI_LOWREAD: + case IOCTL_DI_READDVD: { + u32 offset = inbuf[1]; + u32 len = inbuf[2]; + if (cmd == IOCTL_DI_READDVD) { + offset = offset << 11; + len = len << 9; + } + res = read_from_device(outbuf, offset, len); + /* if (res == 0 && dip.reading == 0) + dummy_function(outbuf, outbuf_size);*/ + break; + } + + default: + call_original_di: + res = handleDiCommand(inbuf, outbuf, outbuf_size); + break; + } + dbg_printf("DIP::res=%d\n", res); + return res; +} diff --git a/cios/odip_frag/source/sdhc.c b/cios/odip_frag/source/sdhc.c new file mode 100644 index 0000000..fc3c236 --- /dev/null +++ b/cios/odip_frag/source/sdhc.c @@ -0,0 +1,101 @@ + +#include "syscalls.h" +#include "types.h" +#include "sdhc.h" + +#include +#include + +/* IOCTL commands */ +#define IOCTL_SDHC_INIT 0x01 +#define IOCTL_SDHC_READ 0x02 +#define IOCTL_SDHC_WRITE 0x03 +#define IOCTL_SDHC_ISINSERTED 0x04 + +/* Constants */ +#define SDHC_SECTOR_SIZE 0x200 + +typedef struct _ioctl ioctlv; + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/sdhc"; +static ioctlv io_vector[3] ATTRIBUTE_ALIGN(32); +static u32 io_buffer[3] ATTRIBUTE_ALIGN(32); +static s32 fd = -1; +static u32 sectorSz = SDHC_SECTOR_SIZE; + + +int sdhc_Init(void) +{ + //s32 ret; + + /* Already open */ + if (fd >= 0) + return 0; + + /* Open USB device */ + fd = os_open(fs, 1); + if (fd < 0) + return -11; + + /* Initialize USB storage */ + os_ioctlv(fd, IOCTL_SDHC_INIT, 0, 0, NULL); + + sectorSz = SDHC_SECTOR_SIZE; + + return 0; +} + + +bool sdhc_Read(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + io_buffer[0] = sector; + io_buffer[1] = numSectors; + + /* Setup io_vector */ + io_vector[0].data = &io_buffer[0]; + io_vector[0].len = sizeof(u32); + io_vector[1].data = &io_buffer[1]; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = (sectorSz * numSectors); + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Read data */ + ret = os_ioctlv(fd, IOCTL_SDHC_READ, 2, 1, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} + + +bool sdhc_Shutdown(void) +{ + if (fd >= 0) { + /* Close USB device */ + os_close(fd); + /* Remove descriptor */ + fd = -1; + } + return true; +} + + diff --git a/cios/odip_frag/source/sdhc.h b/cios/odip_frag/source/sdhc.h new file mode 100644 index 0000000..0f2abf0 --- /dev/null +++ b/cios/odip_frag/source/sdhc.h @@ -0,0 +1,11 @@ +#ifndef _SDHC_H_ +#define _SDHC_H_ + +#include "types.h" + +/* Prototypes */ +int sdhc_Init(void); +bool sdhc_Read(u32 sector, u32 numSectors, void *buffer); + +#endif + diff --git a/cios/odip_frag/source/syscalls.s b/cios/odip_frag/source/syscalls.s new file mode 100644 index 0000000..fb5d97f --- /dev/null +++ b/cios/odip_frag/source/syscalls.s @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * syscalls.s (c) 2009, Hermes + * info from http://wiibrew.org/wiki/IOS/Syscalls + * + * This program is free software; you can 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 + */ + +.macro syscall vec_sys + .long 0xE6000010 +(\vec_sys<<5) + bx lr +.endm + + .align 4 + .arm + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3F + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global swi_mload_func +swi_mload_func: + svc 0xCC + bx lr + + + .code 16 + .global os_puts +os_puts: + mov r2, lr + add r1, r0, #0 + mov r0, #4 + svc 0xab + bx r2 + +/* + + // De rodries: http://www.elotrolado.net/hilo_utilidad-uloader-v2-1c-ocarina-y-forzado-de-video-idioma_1217626_s1050 + .code 32 + .global os_puts +os_puts: + mov R2,lr + adds r1,r0,#0 + movs R0,#4 + svc 0xAB + bx r2 +*/ diff --git a/cios/odip_frag/source/usb.c b/cios/odip_frag/source/usb.c new file mode 100644 index 0000000..ff887cf --- /dev/null +++ b/cios/odip_frag/source/usb.c @@ -0,0 +1,234 @@ +/*--------------------------------------------------------------------------------------------- + * USB Gecko Development Kit - http://www.usbgecko.com + * -------------------------------------------------------------------------------------------- + * + * + * usb.c - V1.2 functions for the USB Gecko adapter (www.usbgecko.com). + * Now works for Wii Mode - use WIIMODE define in usb.h to set + * Copyright (c) 2008 - Nuke - + * + *---------------------------------------------------------------------------------------------*/ + +#include "types.h" +#include "usb.h" + + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbyte + Description: Send byte to Gamecube/Wii over EXI memory card port +*----------------------------------------------------------------------------------------------*/ + +static int __usb_sendbyte (char sendbyte) +{ + s32 i; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xB0000000 | (sendbyte<<20); + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebyte + Description: Receive byte from Gamecube/Wii over EXI memory card port +*----------------------------------------------------------------------------------------------*/ + +static int __usb_receivebyte (char *receivebyte) +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xA0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0; + if (i&0x08000000){ + *receivebyte=(i>>16)&0xff; + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checksendstatus + Description: Chesk the FIFO is ready to send +*----------------------------------------------------------------------------------------------*/ + +static int __usb_checksendstatus(void) +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xC0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checkreceivestatus + Description: Check the FIFO is ready to receive +*----------------------------------------------------------------------------------------------*/ + +static int __usb_checkreceivestatus(void) +{ + s32 i = 0; + exi_chan1sr = 0x000000D0; + exi_chan1data = 0xD0000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i&0x04000000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbuffer + Description: Simple buffer send routine +*----------------------------------------------------------------------------------------------*/ + +void usb_sendbuffer (const void *buffer, int size) +{ + char *sendbyte = (char*) buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + returnvalue = __usb_sendbyte(*sendbyte); + if(returnvalue) { + sendbyte++; + bytesleft--; + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebuffer + Description: Simple buffer receive routine +*----------------------------------------------------------------------------------------------*/ + +void usb_receivebuffer (void *buffer, int size) +{ + char *receivebyte = (char*)buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + returnvalue = __usb_receivebyte(receivebyte); + if(returnvalue) { + receivebyte++; + bytesleft--; + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_sendbuffersafe + Description: Simple buffer send routine with fifo check (use for large transfers) +*----------------------------------------------------------------------------------------------*/ + +void usb_sendbuffersafe (const void *buffer, int size) +{ + char *sendbyte = (char*) buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + if(__usb_checksendstatus()){ + returnvalue = __usb_sendbyte(*sendbyte); + if(returnvalue) { + sendbyte++; + bytesleft--; + } + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_receivebuffersafe + Description: Simple buffer receive routine with fifo check (use for large transfers) +*----------------------------------------------------------------------------------------------*/ + +void usb_receivebuffersafe (void *buffer, int size) +{ + char *receivebyte = (char*)buffer; + s32 bytesleft = size; + s32 returnvalue; + + while (bytesleft > 0) + { + if(__usb_checkreceivestatus()){ + returnvalue = __usb_receivebyte(receivebyte); + if(returnvalue) { + receivebyte++; + bytesleft--; + } + } + } +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_checkgecko + Description: Check the Gecko is connected +*----------------------------------------------------------------------------------------------*/ +int usb_checkgecko() +{ + s32 i = 0; + + exi_chan1sr = 0x000000D0; + exi_chan1data = 0x90000000; + exi_chan1cr = 0x19; + while((exi_chan1cr)&1); + i = exi_chan1data; + exi_chan1sr = 0x0; + if (i==0x04700000){ + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------------------------* + Name: usb_flush + Description: Flushes the FIFO, Use at the start of your program to avoid trash +*----------------------------------------------------------------------------------------------*/ + +void usb_flush() +{ + char tempbyte; + + while (__usb_receivebyte(&tempbyte)); +} + + +void usb_puts (const void *buffer) +{ + char *sendbyte = (char*) buffer; + s32 returnvalue; + + while (*sendbyte) + { + returnvalue = __usb_sendbyte(*sendbyte); + if(returnvalue) { + sendbyte++; + } + } +} + diff --git a/cios/odip_frag/source/usb.h b/cios/odip_frag/source/usb.h new file mode 100644 index 0000000..4644019 --- /dev/null +++ b/cios/odip_frag/source/usb.h @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * USB Gecko Development Kit - http://www.usbgecko.com + * -------------------------------------------------------------------------------------------- + * + * + * usb.h - functions for the USB Gecko adapter (www.usbgecko.com). + * + * Copyright (c) 2008 - Nuke - + * + *---------------------------------------------------------------------------------------------*/ + +#ifndef __USB_H__ +#define __USB_H__ + +#define exi_chan0sr *(volatile unsigned int*) 0x0D006800 // Channel 0 Status Register +#define exi_chan1sr *(volatile unsigned int*) 0x0D006814 // Channel 1 Status Register +#define exi_chan2sr *(volatile unsigned int*) 0x0D006828 // Channel 2 Status Register +#define exi_chan0cr *(volatile unsigned int*) 0x0D00680c // Channel 0 Control Register +#define exi_chan1cr *(volatile unsigned int*) 0x0D006820 // Channel 1 Control Register +#define exi_chan2cr *(volatile unsigned int*) 0x0D006834 // Channel 2 Control Register +#define exi_chan0data *(volatile unsigned int*) 0x0D006810 // Channel 0 Immediate Data +#define exi_chan1data *(volatile unsigned int*) 0x0D006824 // Channel 1 Immediate Data +#define exi_chan2data *(volatile unsigned int*) 0x0D006838 // Channel 2 Immediate Data +#define exi_chan0dmasta *(volatile unsigned int*) 0x0D006804 // Channel 0 DMA Start address +#define exi_chan1dmasta *(volatile unsigned int*) 0x0D006818 // Channel 1 DMA Start address +#define exi_chan2dmasta *(volatile unsigned int*) 0x0D00682c // Channel 2 DMA Start address +#define exi_chan0dmalen *(volatile unsigned int*) 0x0D006808 // Channel 0 DMA Length +#define exi_chan1dmalen *(volatile unsigned int*) 0x0D00681c // Channel 1 DMA Length +#define exi_chan2dmalen *(volatile unsigned int*) 0x0D006830 // Channel 2 DMA Length + +// Function prototypes +void usb_flush(void); +int usb_checkgecko(void); +void usb_sendbuffer (const void *buffer, int size); +void usb_receivebuffer (void *buffer, int size); +void usb_sendbuffersafe (const void *buffer, int size); +void usb_receivebuffersafe (void *buffer, int size); +void usb_puts (const void *buffer); + +#endif // __USB_H__ diff --git a/cios/odip_frag/source/usbstorage.c b/cios/odip_frag/source/usbstorage.c new file mode 100644 index 0000000..a167b61 --- /dev/null +++ b/cios/odip_frag/source/usbstorage.c @@ -0,0 +1,303 @@ +/*------------------------------------------------------------- + +usbstorage_starlet.c -- USB mass storage support, inside starlet +Copyright (C) 2009 Kwiirk + +If this driver is linked before libogc, this will replace the original +usbstorage driver by svpe from libogc +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. + +-------------------------------------------------------------*/ + +#include "ipc.h" +//#include "mem.h" +#include "syscalls.h" +//#include "timer.h" +#include "types.h" +#include "usbstorage.h" + +//#include +//#include + +#define DEVICE_TYPE_WII_USB (('W'<<24)|('U'<<16)|('S'<<8)|'B') + +/* IOCTL commands */ +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) + +/* Constants */ +#define USB_MAX_SECTORS 64 + +typedef struct _ioctl ioctlv; + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/usb123"; +static ioctlv io_vector[3] ATTRIBUTE_ALIGN(32); +static u32 io_buffer[3] ATTRIBUTE_ALIGN(32); +static s32 fd = -1; +static u32 sectorSz = 0; + + + +bool __usbstorage_Read(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + io_buffer[0] = sector; + io_buffer[1] = numSectors; + + /* Setup io_vector */ + io_vector[0].data = &io_buffer[0]; + io_vector[0].len = sizeof(u32); + io_vector[1].data = &io_buffer[1]; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = (sectorSz * numSectors); + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Read data */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_READ_SECTORS, 2, 1, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} + +#if 0 +bool __usbstorage_Write(u32 sector, u32 numSectors, void *buffer) +{ + STACK_ALIGN(u32, _sector, 1, 32); + STACK_ALIGN(u32, _numSectors, 1, 32); + + u32 cnt, len = (sectorSz * numSectors); + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Sector info */ + *_sector = sector; + *_numSectors = numSectors; + + /* Setup io_vector */ + io_vector[0].data = _sector; + io_vector[0].len = sizeof(u32); + io_vector[1].data = _numSectors; + io_vector[1].len = sizeof(u32); + io_vector[2].data = buffer; + io_vector[2].len = len; + + /* Flush cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_after_write(io_vector[cnt].data, io_vector[cnt].len); + + os_sync_after_write(io_vector, sizeof(ioctlv) * 3); + + /* Write data */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_WRITE_SECTORS, 3, 0, io_vector); + if (ret < 0) + return false; + + /* Invalidate cache */ + for (cnt = 0; cnt < 3; cnt++) + os_sync_before_read(io_vector[cnt].data, io_vector[cnt].len); + + return true; +} +#endif + +s32 __usbstorage_GetCapacity(u32 *_sectorSz) +{ + s32 ret; + if (fd >= 0) { + + /* Setup io_vector */ + io_vector[0].data = io_buffer; + io_vector[0].len = sizeof(u32); + + os_sync_after_write(io_vector, sizeof(ioctlv)); + + /* Get capacity */ + ret = os_ioctlv(fd, USB_IOCTL_UMS_GET_CAPACITY, 0, 1, io_vector); + + os_sync_after_write(io_buffer, sizeof(u32)); + + /* Set sector size */ + sectorSz = io_buffer[0]; + + if (ret && _sectorSz) + *_sectorSz = sectorSz; + + return ret; + } + + return 0; +} + +int usbstorage_Init(void) +{ + //s32 ret; + + /* Already open */ + if (fd >= 0) + return 0; + + /* Open USB device */ + fd = os_open(fs, 1); + if (fd < 0) + return -11; + + /* Initialize USB storage */ + os_ioctlv(fd, USB_IOCTL_UMS_INIT, 0, 0, NULL); + + sectorSz = 0x200; // 512 + + return 0; +#if 0 + /* Get device capacity */ + ret = __usbstorage_GetCapacity(NULL); + if (ret == 0) + goto err; + + return 0; + +err: + /* Close USB device */ + usbstorage_Shutdown(); + return -22; +#endif +} + +bool usbstorage_Shutdown(void) +{ + if (fd >= 0) { + /* Close USB device */ + os_close(fd); + + /* Remove descriptor */ + fd = -1; + } + + return true; +} + +bool usbstorage_IsInserted(void) +{ + s32 ret; + + /* Get device capacity */ + ret = __usbstorage_GetCapacity(NULL); + + return (ret > 0); +} + +#if 0 + +bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt = 0; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + while (cnt < numSectors) { + void *ptr = (char *)buffer + (sectorSz * cnt); + + u32 _sector = sector + cnt; + u32 _numSectors = numSectors - cnt; + + /* Limit sector count */ + if (_numSectors > USB_MAX_SECTORS) + _numSectors = USB_MAX_SECTORS; + + /* Read sectors */ + ret = __usbstorage_Read(_sector, _numSectors, ptr); + if (!ret) + return false; + + /* Increase counter */ + cnt += _numSectors; + } + + return true; +} + +bool usbstorage_WriteSectors(u32 sector, u32 numSectors, void *buffer) +{ + u32 cnt = 0; + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + while (cnt < numSectors) { + void *ptr = (char *)buffer + (sectorSz * cnt); + + u32 _sector = sector + cnt; + u32 _numSectors = numSectors - cnt; + + /* Limit sector count */ + if (_numSectors > USB_MAX_SECTORS) + _numSectors = USB_MAX_SECTORS; + + /* Write sectors */ + ret = __usbstorage_Write(_sector, _numSectors, ptr); + if (!ret) + return false; + + /* Increase counter */ + cnt += _numSectors; + } + + return true; +} + +bool usbstorage_ClearStatus(void) +{ + return true; +} + +#endif + + diff --git a/cios/odip_frag/source/usbstorage.h b/cios/odip_frag/source/usbstorage.h new file mode 100644 index 0000000..9415503 --- /dev/null +++ b/cios/odip_frag/source/usbstorage.h @@ -0,0 +1,16 @@ +#ifndef _USBSTORAGE_H_ +#define _USBSTORAGE_H_ + +#include "types.h" + +/* Prototypes */ +int usbstorage_Init(void); +bool usbstorage_Shutdown(void); +bool usbstorage_IsInserted(void); +bool usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer); +//bool usbstorage_WriteSectors(u32 sector, u32 numSectors, void *buffer); +bool usbstorage_ClearStatus(void); +bool __usbstorage_Read(u32 sector, u32 numSectors, void *buffer); + +#endif + diff --git a/cios/odip_frag/source/utils.c b/cios/odip_frag/source/utils.c new file mode 100644 index 0000000..534a6e8 --- /dev/null +++ b/cios/odip_frag/source/utils.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + +#include + +void dip_memset(u8 *buf, u32 ch, u32 size) +{ + u32 unaligned; + u8 c = (u8) ch; + + int cnt = 0; + + if (size == 0) + return; + + unaligned = (4 - ((u32) buf & 0x3)) & 0x3; + + if (unaligned > size) + unaligned = size; + + if (unaligned) + while (unaligned--) { + buf[cnt] = c; + cnt++; + }; + + // Process aligned bytes + u32 c32 = (ch << 24) | (ch << 16) | (ch << 8) | ch; + + u32 *buf32 = (u32 *) (buf + cnt); + while (cnt + 4 <= size) { + *(buf32++) = c32; + cnt += 4; + } + + // Process unaligned tail bytes + while (cnt++ < size) + buf[cnt] = c; +} diff --git a/cios/odip_frag/source/wbfs.c b/cios/odip_frag/source/wbfs.c new file mode 100644 index 0000000..858d102 --- /dev/null +++ b/cios/odip_frag/source/wbfs.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * + * This program is free software; you can 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 + */ + +#include + +#include +#include +#include + +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) +#define USB_IOCTL_WBFS_OPEN_DISC (WBFS_BASE+0x1) +#define USB_IOCTL_WBFS_READ_DISC (WBFS_BASE+0x2) +#define USB_IOCTL_WBFS_READ_DIRECT_DISC (WBFS_BASE+0x3) +#define USB_IOCTL_WBFS_STS_DISC (WBFS_BASE+0x4) + +s32 usb_is_dvd=0; + +s32 usb_device_fd=-1; + +s32 fat_mode=0; + +s32 null_mode=0; + +#define USB_DATA_SIZE 64 +// Ver atributo packed +static struct _usb_data{ + struct _ioctl ioctlv[4]; + u32 values[8]; +} usb_data ATTRIBUTE_ALIGN(32); + +static struct _usb_data* usb_data_ptr; + +char filename_data[256] ATTRIBUTE_ALIGN(32) ="filename_data"; + + +#define MAX_DEVICES 2 +static char *usb_device[] = {"/dev/usb123", + "/dev/sdio/sdhc", + "/dev/usb123"}; + +s32 usb_dvd_inserted(void) +{ + if (!usb_is_dvd) + return 1; + + if (usb_device_fd < 0) + return 1; + + return os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_STS_DISC, 0,0, usb_data_ptr->ioctlv); +} + +extern int ciso_size; +extern u32 *table_lba; + +extern u8 mem_index[2048]; + +s32 usb_read_device(u8 *outbuf, u32 size, u32 lba) +{ + s32 res; + int l; + + if(null_mode) + { + dip_memset(outbuf,0xff, size); + return 0; + } + + if (usb_device_fd < 0) + return -1; + +// FAT Mode from Hermes + if(fat_mode) + { + if(fat_mode==1 || fat_mode==2) + { + u32 lba_glob=0; + + if(table_lba==NULL) table_lba= (void *) os_heap_alloc_aligned(0, 2048*4, 32); // from global heap + + /* Allocate memory */ + u8 * buff = dip_alloc_aligned(0x800, 32); + if (!buff) + fat_mode=-1; + + if(fat_mode>=0) + { + + lba_glob=(16)<<9; + + res=os_seek(usb_device_fd, 0, 0); + if(res>=0) res=os_read(usb_device_fd, buff, 2048); + if(res<0) {fat_mode=-1;} + else + if(!(buff[0]=='C' && buff[1]=='I' && buff[2]=='S' && buff[3]=='O')) fat_mode=-1; + + + + } + + if(fat_mode>=0 && table_lba) + { + + ciso_size=(((u32)buff[4])+(((u32)buff[5])<<8)+(((u32)buff[6])<<16)+(((u32)buff[7])<<24))/4; + + dip_memset(mem_index,0,2048); + + + for(l=0;l<16384;l++) + { + if(((l+8) & 2047)==0 && (l+8)>=2048) + { + if(fat_mode==2) res=os_seek(usb_device_fd, (((l+8)>>11))<<9, 0); + else + res=os_seek(usb_device_fd, (((l+8)>>11))<<11, 0); // read 16 cached sectors + + if(res>=0) res=os_read(usb_device_fd, buff, 2048); + if(res<0) {fat_mode=-1;break;} + } + + if((l & 7)==0) table_lba[l>>3]=lba_glob; + + if(buff[(8+l) & 2047]) + { + mem_index[l>>3]|=1<<(l & 7); + lba_glob+=ciso_size; + } + } + + if(fat_mode>=0) + { + fat_mode|=4; + + } + + + } + + /* Free memory */ + if (buff) + dip_free(buff); + } + + if(fat_mode>=4) + { + u32 temp=(lba)/ciso_size; + + + u32 read_lba=table_lba[temp>>3]; + + for(l=0;l<(temp & 7);l++) if((mem_index[temp>>3]>>l) & 1) read_lba+=ciso_size; + + read_lba+=(lba) & ((ciso_size)-1); + + if(fat_mode & 2) os_seek(usb_device_fd, read_lba, 0); + else + os_seek(usb_device_fd, read_lba<<2, 0); // read sectors + + res=os_read(usb_device_fd, outbuf, size); + //if(r<0) return r; + return 0; + + + } + else + { + + /* Free memory */ + if (table_lba) + os_heap_free(0, table_lba); + + table_lba=NULL; + + return -1; + + } + + } + + usb_data_ptr->values[0] = lba; + usb_data_ptr->values[7] = size; + + usb_data_ptr->ioctlv[2].data = outbuf; + usb_data_ptr->ioctlv[3].data = (u32 *) size; // No es necesario, pero estaba en el codigo original + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + os_sync_after_write(outbuf, size); + + if (usb_is_dvd) + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_READ_DIRECT_DISC, 2, 1, usb_data_ptr->ioctlv); + else + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_READ_DISC, 2, 1, usb_data_ptr->ioctlv); + + os_sync_before_read(outbuf, size); + + return res; +} + + +s32 usb_open_device(u32 device_nr, u8 *id, u32 partition) +{ + u32 res; + u32 part = partition; + + if (device_nr >= MAX_DEVICES) + return -1; + + if(usb_device_fd==0x666999) usb_device_fd=-1; + + if (usb_data_ptr == 0) { + usb_data_ptr = &usb_data; + usb_device_fd = -1; + } else if (usb_device_fd>=0) + os_close(usb_device_fd); + usb_device_fd=-1; + + usb_is_dvd = 0; fat_mode=0; + + + if((id[0]=='_' && id[1]=='N' && id[2]=='U' && id[3]=='L')) + { + fat_mode=1;null_mode=1;usb_device_fd=0x666999; + + return 0; + + } + + if((id[0]=='_' && id[1]=='D' && id[2]=='E' && id[3]=='V')) + { + fat_mode=1; + if(id[4]=='W') fat_mode=2; // change the seek operations of the device from bytes to words + + os_sync_before_read((void *) filename_data, 256); + + usb_device_fd = (int) os_open((void *) filename_data, 0); + if(usb_device_fd<0) {fat_mode=0;return usb_device_fd;} + + return 0; + + } + + if (id[0] == '_' && id[1] == 'D' && id[2] == 'V' && id[3] == 'D') + usb_is_dvd = 1; + + usb_device_fd = os_open(usb_device[device_nr] , 1); + if (usb_device_fd < 0) { + if (device_nr == 0) + usb_device_fd = os_open(usb_device[0], 1); + } + + if (usb_device_fd < 0) + return usb_device_fd; + + dip_memcpy((void *)(usb_data_ptr->values), id, 6); + dip_memcpy((void *) &(usb_data_ptr->values[7]), (u8 *) &part, sizeof(part)); + + usb_data_ptr->ioctlv[0].data = (void *) (usb_data_ptr->values); + usb_data_ptr->ioctlv[0].len = 6; + usb_data_ptr->ioctlv[1].data = (void *) &(usb_data_ptr->values[7]); + usb_data_ptr->ioctlv[1].len = sizeof(u32); + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_OPEN_DISC, 2, 0, usb_data_ptr->ioctlv); + + usb_data_ptr->ioctlv[0].data = (void *) (usb_data_ptr->values); + usb_data_ptr->ioctlv[0].len = sizeof(u32); + usb_data_ptr->ioctlv[1].data = (void *) &(usb_data_ptr->values[7]); + usb_data_ptr->ioctlv[1].len = sizeof(u32); + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + + return res; +} + diff --git a/cios/odip_frag/source/wrappers.s b/cios/odip_frag/source/wrappers.s new file mode 100644 index 0000000..fdbbc62 --- /dev/null +++ b/cios/odip_frag/source/wrappers.s @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + + .align 4 + + .global dip_alloc_aligned + .code 32 +dip_alloc_aligned: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_shared_alloc_aligned + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_free + .code 32 +dip_free: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_shared_free + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_memcpy + .code 32 +dip_memcpy: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_memcpy + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_fatal_di_error + .code 32 +dip_fatal_di_error: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_fatal_di_error + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_doReadHashEncryptedState + .code 32 +dip_doReadHashEncryptedState: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_doReadHashEncryptedState + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_printf + .code 32 +dip_printf: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_printf + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global _callOriginal + .code 32 +_callOriginal: + bx r7 + + .global handleDiCommand + .code 16 + .thumb_func +handleDiCommand: + push {r4-r7,lr} + mov r7, r11 + mov r6, r10 + mov r5, r9 + mov r4, r8 + push {r4-r7} + ldr r3, =addr_di_cmd_reentry + ldr r3, [r3] + bx r3 + +/* Address conversion */ + .code 32 + .global VirtToPhys +VirtToPhys: + and r0, #0x7fffffff + bx lr + + .global PhysToVirt +PhysToVirt: + orr r0, #0x80000000 + bx lr + diff --git a/cios/odip_plugin/COPYING b/cios/odip_plugin/COPYING new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/cios/odip_plugin/COPYING @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/cios/odip_plugin/LEEME.txt b/cios/odip_plugin/LEEME.txt new file mode 100644 index 0000000..9ca2d7f --- /dev/null +++ b/cios/odip_plugin/LEEME.txt @@ -0,0 +1,32 @@ +ODIP-Plugin +----------- + +Esta es un versión abierta del DIP-Plugin (intento de, por ahora) correspondiente al cIOS de Hermes, +la versión que lanzó los primeros días de Enero del 2010 (para ser usada con +el uLoader 3.6A). + + +Estado +------ + +No está funcionando correctamente. No es por ahora un producto para usuarios +finales sino para desarrolladores. + +Lamentablemente por ahora no tengo mucho tiempo para probarlo pero quería liberar +lo que tengo por ahora. + +Usandolo desde el uLoader, en modo DVD, en mi Wii con chip el juego se carga +correctamente. + +En modo USB, se detectan correctamente las particiones y cargue exitosamente 2 +juegos. No está funcionando la instalación de juegos. + +Tener en cuenta que esto es un producto de analizar el código binario y +generar código C, por lo tanto puede haber muchos errores, mi entendimiento +del HW de Wii y del funcionamiento de los módulo es limitado. + +Los nombres de las funciones pueden no ser correctos siempre, ya que son mi +interpretación de lo que el código hace. + + + Spaceman Spiff diff --git a/cios/odip_plugin/MakeIt.bat b/cios/odip_plugin/MakeIt.bat new file mode 100644 index 0000000..a6340c2 --- /dev/null +++ b/cios/odip_plugin/MakeIt.bat @@ -0,0 +1,4 @@ + +make + +pause diff --git a/cios/odip_plugin/Makefile b/cios/odip_plugin/Makefile new file mode 100644 index 0000000..d169960 --- /dev/null +++ b/cios/odip_plugin/Makefile @@ -0,0 +1,148 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../libcios/include +#SCRIPTDIR := scripts.debug +SCRIPTDIR := scripts +BIN := bin + +#DEBUG := -DDEBUG + +STRIPIOSPLUGIN := ../stripiosplugin/stripiosplugin.exe + +LIBS := +LIBDIRS := + +CFLAGS+= $(DEBUG) + +#--------------------------------------------------------------------------------- +# the prefix on the compiler executables +#--------------------------------------------------------------------------------- +#$(DEVKITARM)/bin/ +#PREFIX := arm-eabi- +PREFIX := $(DEVKITARM)/bin/arm-eabi- +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +AR := $(PREFIX)ar +OBJCOPY := $(PREFIX)objcopy +LD := $(PREFIX)g++ +AS := $(PREFIX)g++ + +#--------------------------------------------------------------------------------- +# linker script +#--------------------------------------------------------------------------------- +LINKSCRIPT := $(ROOT)/$(SCRIPTDIR)/link.ld +SPECS := $(ROOT)/$(SCRIPTDIR)/nostart.specs + +ifeq ($(BUILDING),$(emptystring)) + +export ROOT := $(CURDIR) + + +all: + @[ -d $(BUILD) ] || mkdir -p $(BUILD) + @$(MAKE) -C $(BUILD) --no-print-directory -f $(CURDIR)/Makefile BUILDING=all +clean: + @echo clean ... + @rm -fr $(BUILD) $(BIN)/*.elf +else + +TARGET := $(notdir $(ROOT)) +STRIPIOSPLUGIN := $(ROOT)/$(STRIPIOSPLUGIN) +#---------------------------------------------------- +# MS Visual Studio Style Fix: +#---------------------------------------------------- +#STYLEFIX = 2>&1 | sed -e 's/\([a-zA-Z\.]\+\):\([0-9]\+\):\([0-9]\+:\)\?\(.\+\)/\1(\2):\4/' -e 's/undefined/error: undefined/' +STYLEFIX ?= + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +OUTPUT := $(ROOT)/$(BIN)/$(TARGET) +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(ROOT)/$(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(ROOT)/$(dir)/*.*))) + +OFILES := $(addsuffix _bin.o,$(BINFILES)) \ + $(CPPFILES:.cpp=_cpp.o) $(CFILES:.c=_c.o) \ + $(sFILES:.s=_s.o) $(SFILES:.S=_S.o) + +DEPENDS := $(OFILES:.o=.d) + +VPATH = $(foreach dir,$(SOURCES),$(ROOT)/$(dir)) + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +INCLUDE := $(foreach dir,$(INCLUDES),-I$(ROOT)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(ROOT)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ARCH = -mcpu=arm9tdmi -mtune=arm9tdmi -mthumb -mthumb-interwork -mbig-endian + +CFLAGS += -g $(ARCH) $(INCLUDE) -fno-strict-aliasing -Wall -Os -fomit-frame-pointer -ffast-math -fverbose-asm -Wpointer-arith -Winline -Wundef -g -ffunction-sections -fdata-sections -fno-exceptions +CFLAGS += -Wstrict-prototypes + + +AFLAGS = -g $(ARCH) -x assembler-with-cpp + +LDFLAGS = -g $(ARCH) -specs=$(SPECS) -T$(LINKSCRIPT) $(LIBPATHS) $(LIBS) -Wl,--gc-sections -Wl,-static -Wl,-Map,$(TARGET).map -nostartfiles + + +$(OUTPUT).bin: $(TARGET).elf + @echo Stripping plugin $(notdir $@) + $(OBJCOPY) -O binary $(TARGET).elf $(OUTPUT).bin +# arm-eabi-objcopy -O binary $(TARGET).elf $(OUTPUT).bin +# $(STRIPIOSPLUGIN) $< $@ + + +%.elf: $(OFILES) + @echo linking $(notdir $@) + @$(LD) -g -o $@ $(OFILES) $(LDFLAGS) $(STYLEFIX) + + +%_cpp.o : %.cpp + @echo $(notdir $<) + @$(CXX) -MMD -MF $*_cpp.d $(CFLAGS) -c $< -o$@ $(STYLEFIX) + +%_c.o : %.c + @echo $(notdir $<) + @$(CC) -MMD -MF $*_c.d $(CFLAGS) -c $< -o$@ $(STYLEFIX) + +%_s.o : %.s + @echo $(notdir $<) + @$(AS) -MMD -MF $*_s.d $(AFLAGS) -c $< -o$@ $(STYLEFIX) + +%_bin.o : %.bin + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +define bin2o + @echo -e "\t.section .rodata\n\t.align 4\n\t.global $(*)\n\t.global $(*)_end\n$(*):\n\t.incbin \"$(subst /,\\\\\\\\,$(shell echo $< | sed 's=/==;s=/=:/='))\"\n$(*)_end:\n" > $@.s + @$(CC) $(ASFLAGS) $(AFLAGS) -c $@.s -o $@ + @rm -rf $@.s +endef + +endif diff --git a/cios/odip_plugin/bin/odip_plugin.bin b/cios/odip_plugin/bin/odip_plugin.bin new file mode 100644 index 0000000000000000000000000000000000000000..760ac33baa21fb22d23832d31fa013076bdd1014 GIT binary patch literal 5920 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 literal 0 HcmV?d00001 diff --git a/cios/odip_plugin/include/debug.h b/cios/odip_plugin/include/debug.h new file mode 100644 index 0000000..e380db6 --- /dev/null +++ b/cios/odip_plugin/include/debug.h @@ -0,0 +1,6 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +void s_printf(char *format,...); + +#endif diff --git a/cios/odip_plugin/include/di.h b/cios/odip_plugin/include/di.h new file mode 100644 index 0000000..f092275 --- /dev/null +++ b/cios/odip_plugin/include/di.h @@ -0,0 +1,27 @@ +#ifndef _DI_H_ +#define _DI_H_ + +/* Prototypes */ +int DIP_StopMotor(void); +int DIP_CustomCommand(u8 *cmd, u8 *ans); +int DIP_ReadDVDVideo(void *dst, u32 len, u32 lba); +int DIP_ReadDVD(void *dst, u32 len, u32 sector); +int DIP_ReadDVDRom(u8 *outbuf, u32 len, u32 offset); + +extern u8 * addr_dvd_read_controlling_data; +/* +int DI_Inquiry(void *outbuf); +int DI_Reset(void); +int DI_ResetNotify(void); +int DI_ReadDiskId(void *outbuf, unsigned long len); +int DI_Read(void *outbuf, unsigned long len, unsigned long offset); +int DI_ReadDvd(void *outbuf, unsigned long len, unsigned long lba); +int DI_StopLaser(void); +int DI_Seek(unsigned long offset); +int DI_Offset(unsigned long offset); +int DI_AudioStreaming(unsigned long len); +int DI_GetStatus(unsigned long *outbuf); +*/ + +#endif + diff --git a/cios/odip_plugin/include/es.h b/cios/odip_plugin/include/es.h new file mode 100644 index 0000000..2395f44 --- /dev/null +++ b/cios/odip_plugin/include/es.h @@ -0,0 +1,7 @@ +#ifndef __ES_H__ +#define __ES_H__ + +extern int in_ES_ioctlv(void *); +extern int out_ES_ioctlv(void *); + +#endif \ No newline at end of file diff --git a/cios/odip_plugin/include/ipc.h b/cios/odip_plugin/include/ipc.h new file mode 100644 index 0000000..71fbb13 --- /dev/null +++ b/cios/odip_plugin/include/ipc.h @@ -0,0 +1,51 @@ +#ifndef __IPC_H__ +#define __IPC_H__ + +#include + +struct _ioctl{ + void *data; + u32 len; +}; + +struct _ipcreq +{ //ipc struct size: 32 + u32 cmd; //0 + s32 result; //4 + union { //8 + s32 fd; + u32 req_cmd; + }; + union { + struct { + char *filepath; + u32 mode; + } open; + struct { + void *data; + u32 len; + } read, write; + struct { + s32 where; + s32 whence; + } seek; + struct { + u32 ioctl; + void *buffer_in; + u32 len_in; + void *buffer_io; + u32 len_io; + } ioctl; + struct { + u32 ioctl; + u32 argcin; + u32 argcio; + struct _ioctl *argv; + } ioctlv; + u32 args[5]; + }; + + +} ATTRIBUTE_PACKED; + +#endif diff --git a/cios/odip_plugin/include/module.h b/cios/odip_plugin/include/module.h new file mode 100644 index 0000000..17b599b --- /dev/null +++ b/cios/odip_plugin/include/module.h @@ -0,0 +1,79 @@ +#ifndef _MODULE_H_ +#define _MODULE_H_ + +/* Error codes */ +#define ERR_EINVAL -1 +#define ERR_ENOENT -6 +#define ERR_ENOMEM -22 +#define ERR_EIO 2 +#define ERR_EINCMD 128 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTL calls */ +#define IOCTL_DI_INQUIRY 0x12 +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_READ 0x71 +#define IOCTL_DI_WAITCOVERCLOSE 0x79 +#define IOCTL_DI_RESETNOTIFY 0x7E +#define IOCTL_DI_GETCOVER 0x88 +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_OPENPART 0x8B +#define IOCTL_DI_CLOSEPART 0x8C +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_LOWREAD 0xA8 +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_REPORTKEY 0xA4 +#define IOCTL_DI_READDVD 0xD0 +#define IOCTL_DI_STOPLASER 0xD2 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_DISC_BCA 0xDA +#define IOCTL_DI_REQERROR 0xE0 +#define IOCTL_DI_STOPMOTOR 0xE3 +#define IOCTL_DI_STREAMING 0xE4 + +#define IOCTL_DI_SETBASE 0xF0 +#define IOCTL_DI_GETBASE 0xF1 +#define IOCTL_DI_SETFORCEREAD 0xF2 +#define IOCTL_DI_GETFORCEREAD 0xF3 +#define IOCTL_DI_SETUSBMODE 0xF4 +#define IOCTL_DI_GETUSBMODE 0xF5 +#define IOCTL_DI_DISABLERESET 0xF6 +#define IOCTL_DI_CUSTOMCMD 0xFF + +#define IOCTL_DI_13 0x13 +#define IOCTL_DI_14 0x14 +#define IOCTL_DI_15 0x15 + +/* Constants */ +#define SECTOR_SIZE 0x800 + + +/* DIP struct */ +typedef struct { + /* DIP config */ + u32 low_read_from_device; + u32 dvdrom_mode; + u32 base; + u32 offset; + u32 has_id; + u32 partition; + u8 id[8]; + u32 currentError; // offset 0x20 + u8 disableReset; + u8 reading; +} __attribute__((packed)) dipstruct; + +extern dipstruct dip; + +/* Call original ioctl command */ +int handleDiCommand(u32 *inbuf, u32 *outbuf, u32 outbuf_size); + +#endif diff --git a/cios/odip_plugin/include/syscalls.h b/cios/odip_plugin/include/syscalls.h new file mode 100644 index 0000000..60de65b --- /dev/null +++ b/cios/odip_plugin/include/syscalls.h @@ -0,0 +1,57 @@ +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "types.h" +#include "ipc.h" + +/* Prototypes */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +void os_puts(char *str); +s32 os_open(char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, struct _ioctl *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_set_uid(u32 pid, u32 uid); +void os_set_gid(u32 pid, u32 gid); +s32 os_ppc_boot(const char *filepath); +s32 os_ios_boot(const char *filepath, u32 flag, u32 version); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* Prototypes of functions exported from the hacked IOS */ +void dip_free(void *); +void dip_doReadHashEncryptedState(void); +u32 *dip_memcpy(u8 *, const u8 *, u32); +void *dip_alloc_aligned(u32, u32); +#endif diff --git a/cios/odip_plugin/include/types.h b/cios/odip_plugin/include/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/odip_plugin/include/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/odip_plugin/include/utils.h b/cios/odip_plugin/include/utils.h new file mode 100644 index 0000000..68991db --- /dev/null +++ b/cios/odip_plugin/include/utils.h @@ -0,0 +1,20 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include "types.h" + +#define swab32(x) ((u32)( \ + (((u32)(x) & (u32)0x000000ffUL) << 24) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 8) | \ + (((u32)(x) & (u32)0x00ff0000UL) >> 8) | \ + (((u32)(x) & (u32)0xff000000UL) >> 24))) +#define swab16(x) ((u16)( \ + (((u16)(x) & (u16)0x00ffU) << 8) | \ + (((u16)(x) & (u16)0xff00U) >> 8))) + +# define ATTRIBUTE_PACKED __attribute__((packed)) +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) + + +void dip_memset(u8 *buf, u32 c, u32 size); +#endif diff --git a/cios/odip_plugin/include/wbfs.h b/cios/odip_plugin/include/wbfs.h new file mode 100644 index 0000000..ce782c5 --- /dev/null +++ b/cios/odip_plugin/include/wbfs.h @@ -0,0 +1,14 @@ +#ifndef __WBFS_H__ +#define __WBFS_H__ + +#include "types.h" + +s32 usb_dvd_inserted(void); +s32 usb_read_device(u8 *outbuf, u32 size, u32 lba); +s32 usb_open_device(u32 device_nr, u8 *id, u32 partition); + +extern s32 usb_is_dvd; + +extern s32 usb_device_fd; + +#endif diff --git a/cios/odip_plugin/scripts/link.ld b/cios/odip_plugin/scripts/link.ld new file mode 100644 index 0000000..2ddcc22 --- /dev/null +++ b/cios/odip_plugin/scripts/link.ld @@ -0,0 +1,62 @@ +OUTPUT_FORMAT("elf32-bigarm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + + +/* + Change exe start and ram start as needed for the custom IOS module. + Current settings are for the EHC module from IOS 31. actually this is free space in this IOS + */ + +MEMORY { + exe(rwx) : ORIGIN = 0x1377E000, LENGTH = 0x1600 + ram(rw) : ORIGIN = 0x1377F600, LENGTH = 0xA00 +} + + + +__exe_start_virt__ = 0x1377E000; +__exe_start_phys__ = 0x1377E000; +__ram_start_virt__ = 0x1377F600; +__ram_start_phys__ = 0x1377F600; +__ios_info_table_start = 0x0; + + +SECTIONS +{ + .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } .debug_info 0 : { *(.debug_info) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } .note.arm.ident 0 : { KEEP (*(.note.arm.ident)) } + .init __exe_start_virt__ : AT (__exe_start_phys__) { . = .; KEEP (*(.init)) } > exe + .text ALIGN (0x20) : { + *(.text*) + *(.gnu.warning) + *(.rodata*) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) } > exe + .data __ram_start_virt__ : AT (__ram_start_phys__) { KEEP( *(.ios_data) ) *(.data*) *(.data1) *(.gnu.linkonce.d.*) . = ALIGN (4); __CTOR_LIST__ = ABSOLUTE (.); KEEP (*(SORT (.ctors*))) __CTOR_END__ = ABSOLUTE (.); __DTOR_LIST__ = ABSOLUTE (.); KEEP (*(SORT (.dtors*))) __DTOR_END__ = ABSOLUTE (.); + *(.dynamic) + *(.sdata*) + *(.dynbss) + *(.gnu.linkonce.s.*) . = ALIGN (4); *(.2ram.*) } > ram + .fini : { . = .; *(.fini) } > ram + .rodata ALIGN (0x4) : { + . = .; + *(.gnu.linkonce.r.*) } > ram + .rodata1 ALIGN (0x4) : { . = .; *(.rodata1) } > ram + .fixup ALIGN (0x4) : { . = .; *(.fixup) } > ram + .gcc_except_table ALIGN (0x4) : { . = .; *(.gcc_except_table) } > ram + .got ALIGN (0x4) : { *(.got.plt) *(.got) } > ram + + _ini_bss = . ; + .bss ALIGN (0x20) : { *(.scommon) + *(.dynsbss) + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON) KEEP( *(.ios_bss) ) } > ram + _len_bss = . - _ini_bss ; + + . = ALIGN(4); +} diff --git a/cios/odip_plugin/scripts/nostart.specs b/cios/odip_plugin/scripts/nostart.specs new file mode 100644 index 0000000..9c27dca --- /dev/null +++ b/cios/odip_plugin/scripts/nostart.specs @@ -0,0 +1,2 @@ +*startfile: +crti%O%s crtbegin%O%s diff --git a/cios/odip_plugin/source/ES_ioctlv_low.s b/cios/odip_plugin/source/ES_ioctlv_low.s new file mode 100644 index 0000000..0bcbeef --- /dev/null +++ b/cios/odip_plugin/source/ES_ioctlv_low.s @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + + .align 2 + .code 16 + .global in_ES_ioctlv + .thumb_func +in_ES_ioctlv: + + push {r2-r6} + push {lr} + bl ES_ioctlv+1 + pop {r1} + pop {r2-r6} + bx r1 + + .global out_ES_ioctlv + .thumb_func +out_ES_ioctlv: + push {r4-r6,lr} + sub sp, sp, #0x20 + ldr r5, [r0,#8] + add r1, r0, #0 + ldr r3, = 0x201000D5 + bx r3 diff --git a/cios/odip_plugin/source/crt0.s b/cios/odip_plugin/source/crt0.s new file mode 100644 index 0000000..6c6e342 --- /dev/null +++ b/cios/odip_plugin/source/crt0.s @@ -0,0 +1,107 @@ +/* + Custom IOS module for Wii. + Copyright (C) 2008 neimod. + Copyright (C) 2010 Spaceman Spiff. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .global _start + + .align 4 + .code 32 + + +/******************************************************************************* + * + * crt0.s - IOS module startup code + * + ******************************************************************************* + * + * + * v1.0 - 26 July 2008 - initial release by neimod + * v1.1 - 5 September 2008 - prepared for public release + * + */ + +.EQU dip_plugin_id, 0x12340001 + +.EQU ios36_dvd_read_controlling_data, 0x2022DDAC +.EQU ios36_handle_di_cmd_reentry, 0x20201010 +.EQU ios36_shared_alloc_aligned, 0x20200b9c +.EQU ios36_shared_free, 0x20200b70 +.EQU ios36_memcpy, 0x20205dc0 +.EQU ios36_fatal_di_error, 0x20200048 +.EQU ios36_doReadHashEncryptedState, 0x20202b4c +.EQU ios36_printf, 0x20203934 + +.EQU ios38_dvd_read_controlling_data, 0x2022cdac +.EQU ios38_handle_di_cmd_reentry, 0x20200d38 +.EQU ios38_shared_alloc_aligned, 0x202008c4 +.EQU ios38_shared_free, 0x20200898 +.EQU ios38_memcpy, 0x20205b80 +.EQU ios38_fatal_di_error, 0x20200048 +.EQU ios38_doReadHashEncryptedState, 0x20202874 +.EQU ios38_printf, 0x2020365c + + .global addr_dvd_read_controlling_data + .global addr_di_cmd_reentry + .global addr_ios_shared_alloc_aligned + .global addr_ios_shared_free + .global addr_ios_memcpy + .global addr_ios_fatal_di_error + .global addr_ios_doReadHashEncryptedState + .global addr_ios_printf + +/* Begin DIP-Plugin compatible Table */ + .long DI_EmulateCmd + .long dip_plugin_id +addr_dvd_read_controlling_data: + .long ios36_dvd_read_controlling_data +addr_di_cmd_reentry: + .long ios36_handle_di_cmd_reentry + 1 +addr_ios_shared_alloc_aligned: + .long ios36_shared_alloc_aligned + 1 +addr_ios_shared_free: + .long ios36_shared_free + 1 +addr_ios_memcpy: + .long ios36_memcpy + 1 +addr_ios_fatal_di_error: + .long ios36_fatal_di_error + 1 +addr_ios_doReadHashEncryptedState: + .long ios36_doReadHashEncryptedState + 1 +addr_ios_printf: + .long ios36_printf + 1 + + .long 0 @ reserved @ + .long 0 @ reserved @ + .long 0 @ reserved @ + .long 0 @ reserved @ + .long filename_data + .long bca_bytes + .long in_ES_ioctlv + + .global mem_bss +mem_bss: + .long _ini_bss + + .global mem_bss_len +mem_bss_len: + .long _len_bss + + .align 4 +_start: + .end diff --git a/cios/odip_plugin/source/debug.c b/cios/odip_plugin/source/debug.c new file mode 100644 index 0000000..932e01f --- /dev/null +++ b/cios/odip_plugin/source/debug.c @@ -0,0 +1,176 @@ +#include + +#include // for the s_printf function + +#include + +#ifdef DEBUG + +static char mem_cad[32]; + +void int_char(int num) +{ +int sign=num<0; +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<10;n++) + { + m=num % 10;num/=10;if(m<0) m=-m; + mem_cad[25-n]=48+m; + } + + mem_cad[26]=0; + + n=0;m=16; + if(sign) {mem_cad[n]='-';n++;} + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void uint_char(unsigned int num) +{ +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<10;n++) + { + m=num % 10;num/=10; + mem_cad[25-n]=48+m; + } + + mem_cad[26]=0; + + n=0;m=16; + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void hex_char(u32 num) +{ +int n,m; + + if(num==0) + { + mem_cad[0]='0';mem_cad[1]=0; + return; + } + + for(n=0;n<8;n++) + { + m=num & 15;num>>=4; + if(m>=10) m+=7; + mem_cad[23-n]=48+m; + } + + mem_cad[24]=0; + + n=0;m=16; + + mem_cad[n]='0';n++; + mem_cad[n]='x';n++; + + while(mem_cad[m]=='0') m++; + + if(mem_cad[m]==0) m--; + + while(mem_cad[m]) + { + mem_cad[n]=mem_cad[m]; + n++;m++; + } + mem_cad[n]=0; + +} + +void s_printf(char *format,...) +{ + va_list opt; + + char out[2]=" "; + + int val; + + char *s; + + va_start(opt, format); + + while(format[0]) + { + if(format[0]!='%') {out[0]=*format++;os_puts(out);} + else + { + format++; + switch(format[0]) + { + case 'd': + case 'i': + val=va_arg(opt,int); + int_char(val); + + os_puts(mem_cad); + + break; + + case 'u': + val=va_arg(opt, unsigned); + uint_char(val); + + os_puts(mem_cad); + + break; + + case 'x': + val=va_arg(opt,int); + hex_char((u32) val); + os_puts(mem_cad); + + break; + + case 's': + s=va_arg(opt,char *); + os_puts(s); + break; + + } + format++; + } + + } + + va_end(opt); + + +} + +#endif diff --git a/cios/odip_plugin/source/di.c b/cios/odip_plugin/source/di.c new file mode 100644 index 0000000..4837db2 --- /dev/null +++ b/cios/odip_plugin/source/di.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * + * This program is free software; you can 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 + */ + +#include + +#include +#include + +#define DVDIO_SIZE 32 + +#define DVDIO_ADDR 0xD006000 +#define SECTORSIZE 2048 +#define MAX_SIZE_DI (0xFF << 15) + +static u32 DIP_adjust_read_size(u8 *buf, u32 size, u32 align); + +int DIP_StopMotor(void) +{ + u32 inbuf[8]; + inbuf[0] = IOCTL_DI_STOPMOTOR << 24; + inbuf[1] = 0; + inbuf[2] = 0; + return handleDiCommand(inbuf,NULL,0); +} + + +int DIP_CustomCommand(u8 *cmd, u8 *ans) +{ + volatile unsigned long *dvdio = (volatile unsigned long *) DVDIO_ADDR; + + dip_memcpy((u8 *) dvdio, cmd, DVDIO_SIZE); + os_sync_after_write((u8 *) dvdio, DVDIO_SIZE); + + // DI Control Register, wait until ready + while (dvdio[7] & 1) // 0xD00601C + ; + + dip_memcpy(ans, (u8 *) dvdio, DVDIO_SIZE); + os_sync_after_write(ans, DVDIO_SIZE); + + return dvdio[8]; // 0xD006020 +} + +int DIP_ReadDVDVideo(void *dst, u32 len, u32 lba) +{ + int res; + int tries = 0; + u32 inbuf[8]; + do { + inbuf[0] = IOCTL_DI_READDVD << 24; + inbuf[1] = 0; + inbuf[2] = 0; + inbuf[3] = len >> 11; // Convert to 2048 blocks (dvd sector) + inbuf[4] = lba; + os_sync_before_read(dst, len); + res = handleDiCommand(inbuf, dst, len); + tries++; + } while (res && tries < 32); + return res; +} + + +int DIP_ReadDVD(void *dst, u32 len, u32 sector) +{ + u32 inbuf[8]; + int res; + int tries = 0; + do { + inbuf[0] = IOCTL_DI_LOWREAD << 24; // IOCTL_DI_LOWREAD A8 + inbuf[1] = len; + inbuf[2] = sector; + + os_sync_before_read(dst, len); + res =handleDiCommand(inbuf, dst, len); + tries++; + } while (res && tries < 8); + return res; +} + + +int DIP_ReadDVDRom(u8 *outbuf, u32 len, u32 offset) +{ + u32 cnt = 0; + int res; + + if (len == 0) + return 0; + + u32 lba = offset >> 9; // offset / 512 + u32 size; + + while (cnt < len) { + u32 skip; + size = len - cnt; + + skip = (offset > (lba << 9)) ? (offset - (lba << 9)) << 2 : 0; + + res = DIP_adjust_read_size(outbuf+ cnt, size, SECTORSIZE); + + if (skip || !res) { + if ((skip + size) > SECTORSIZE) + size = SECTORSIZE - skip; + u8 *mem = dip_alloc_aligned(SECTORSIZE, 0x20); + if (!mem) + return -1; + + res = DIP_ReadDVDVideo(mem, SECTORSIZE, lba); + if (res == 0) { + dip_memcpy(outbuf+cnt, mem + skip, size); + os_sync_after_write(outbuf+cnt, size); + } + dip_free(mem); + } else { + size = res; + if (size >= MAX_SIZE_DI) + size = MAX_SIZE_DI; + res = DIP_ReadDVDVideo(outbuf+cnt, size, lba); // inlined function + /* + u32 tries = 0; + do { + u32 inbuf[8]; + + inbuf[0] = IOCTL_DI_READDVD << 24; + inbuf[1] = 0; + inbuf[2] = 0; + inbuf[3] = size >> 11; + inbuf[4] = lba; + os_sync_before_read(outbuf, size); + res = handleDiCommand(inbuf, (u32 *) outbuf, size); + tries = tries + 1; + } while (res != 0 && tries < 15); + */ + } + + if (res) + return res; + + cnt += size; + lba += (size + skip) >> 11; + } + return res; +} + +/* si retorna cero, el buffer, no esta alineado. */ + +// desde dip_plugin +#define DMA1_START_ADDRESS 0x00000000 +#define DMA1_END_ADDRESS 0x01800000 +#define DMA2_START_ADDRESS 0x10000000 +#define DMA2_END_ADDRESS 0x13618000 + + +u32 DIP_adjust_read_size(u8 *outbuf, u32 size, u32 alignment) +{ + u32 mem; + int ret = 0; + + /* Output buffer address */ + mem = (u32)outbuf; + + /* Check for memory alignment */ + if (!(mem & (0x1f))) { + u32 dmalen = 0; + + /* DMA1 range check */ + if ((mem >= DMA1_START_ADDRESS) && (mem < DMA1_END_ADDRESS)) + dmalen = (DMA1_END_ADDRESS - mem); + + /* DMA2 range check */ + if ((mem >= DMA2_START_ADDRESS) && (mem < DMA2_END_ADDRESS)) + dmalen = (DMA2_END_ADDRESS - mem); + + if (dmalen >= alignment) + ret = (dmalen < size) ? dmalen : size; + ret -= (ret & (alignment-1)); + } + + return ret; +} \ No newline at end of file diff --git a/cios/odip_plugin/source/es_ioctlv.c b/cios/odip_plugin/source/es_ioctlv.c new file mode 100644 index 0000000..8ee2508 --- /dev/null +++ b/cios/odip_plugin/source/es_ioctlv.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + +#include + +#include + +#include + +extern int in_ES_ioctlv(void *); +extern int out_ES_ioctlv(void *); + +//void ios_sync_before_read(void* ptr, int size); +//void ios_sync_after_write(void* ptr, int size); + +static u32 inside_ES_ioctl; + +int ES_ioctlv(struct _ipcreq *dat ) +{ + int r; + u32 ios,version; + + inside_ES_ioctl = 1; + + os_sync_before_read(dat, sizeof(struct _ipcreq)); + + if(dat->ioctlv.ioctl==8) { // reboot + os_sync_before_read( (void *) dat->ioctlv.argv[0].data, dat->ioctlv.argv[0].len); + ios=*(((volatile u32 *)dat->ioctlv.argv[0].data)+1) ; + version=1; + os_sync_before_read((void *) 0x3140,8); + *((volatile u32 *) 0x3140)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision + *((volatile u32 *) 0x3188)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision + os_sync_after_write((void *) 0x3140,4); + os_sync_after_write((void *) 0x3188,4); + inside_ES_ioctl = 0; + return 0; + } + r=out_ES_ioctlv(dat); + inside_ES_ioctl = 0; + return r; +} + diff --git a/cios/odip_plugin/source/module.c b/cios/odip_plugin/source/module.c new file mode 100644 index 0000000..71191ea --- /dev/null +++ b/cios/odip_plugin/source/module.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * Copyright (C) 2010 Waninkoko + * + * This program is free software; you can 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 + */ + +#include +#include +#include +#include + +#include +#include + +#ifdef DEBUG +#include +#endif +dipstruct dip= {0}; + +#define WII_DVD_SIGNATURE 0x5D1C9EA3 + +#define READINFO_SIZE_DATA 32 + +#define BCADATA_SIZE 64 +const u8 bca_bytes[BCADATA_SIZE] ATTRIBUTE_ALIGN(32) = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +void dummy_function(u32 *outbuf, u32 outbuf_size) +{ +} + + +// From Waninkoko dip_plugin + +/* Return codes */ +#define DIP_EIO 0xA000 + +/* Disc lengths */ +#define DVD5_LENGTH 0x46090000 +#define DVD9_LENGTH 0x7ED38000 + +/* Error codes */ +#define ERROR_BLOCK_RANGE 0x52100 +#define ERROR_WRONG_DISC 0x53100 + +/* Disc types */ +enum { +DISC_UNKNOWN = 0, +DISC_DVD5, +DISC_DVD9, +}; + +int dvd_type=0; + +s32 __DI_CheckOffset(u32 offset) +{ +u32 offmax; + +/* Check disc type */ + switch (dvd_type) { + /* Single layer */ + case DISC_DVD5: + offmax = DVD5_LENGTH; + break; + + /* Dual layer */ + case DISC_DVD9: + offmax = DVD9_LENGTH; + break; + + default: + return 0; + } + + /* Check offset */ + if (offset >= offmax) { + /* Set error */ + dip.currentError = ERROR_BLOCK_RANGE; + + /* I/O error */ + return DIP_EIO; + } + +return 0; +} + + + +// CISO mem area +int ciso_lba=-1; +int ciso_size=0; +u32 *table_lba=NULL; + +extern s32 fat_mode; + +u8 mem_index[2048] __attribute__ ((aligned (32))); + +int read_from_device_out(void *outbuf, u32 size, u32 lba) +{ + + if (dip.has_id) + return usb_read_device((u8 *)outbuf, size, lba); + + if (dip.dvdrom_mode) + return DIP_ReadDVDRom((u8 *) outbuf, size, lba); + + return DIP_ReadDVD((u8 *) outbuf, size, lba); +} + +int read_from_device(void *outbuf, u32 size, u32 lba) +{ +int r=-1; +int l; + + r = __DI_CheckOffset(lba); + if (r) return r; + + r=-1; + + lba += dip.base + dip.offset; + + if (dip.has_id) + { + /* Free memory */ + + if(fat_mode<=0) + { + if (table_lba) + os_heap_free(0, table_lba); + table_lba=NULL; + + ciso_lba=-1; + + } + + return read_from_device_out(outbuf, size, lba); + } + + if(ciso_lba>=0 && ciso_lba!=0x7fffffff) + { + u32 lba_glob=0; + + + if(table_lba==NULL) table_lba= (void *) os_heap_alloc_aligned(0, 2048*4, 32); // from global heap + //ciso_lba=265; + /* Allocate memory */ + u8 * buff = dip_alloc_aligned(0x800, 32); + if (!buff) + ciso_lba=-1; + + if(ciso_lba>=0) + { + + while(1) + { + lba_glob=(ciso_lba+16)<<9; + + + r=read_from_device_out(buff, 2048, ciso_lba<<9); // read 16 cached sectors + if(r<0) {ciso_lba=-1;break;} + + if((buff[0]=='C' && buff[1]=='I' && buff[2]=='S' && buff[3]=='O')) break; + else + { + if(ciso_lba!=0) {ciso_lba=0;continue;} + ciso_lba=-1; + } + + break; + } + } + // if(ciso_lba>=0 && table_lba && buff) ciso_lba=0x7fffffff; + + if(ciso_lba>=0 && table_lba) + { + + ciso_size=(((u32)buff[4])+(((u32)buff[5])<<8)+(((u32)buff[6])<<16)+(((u32)buff[7])<<24))/4; + + dip_memset(mem_index,0,2048); + + + for(l=0;l<16384;l++) + { + if(((l+8) & 2047)==0 && (l+8)>=2048) + { + r=read_from_device_out(buff, 2048, (ciso_lba+((l+8)>>11))<<9); // read 16 cached sectors + if(r<0) {ciso_lba=-1;break;} + } + + if((l & 7)==0) table_lba[l>>3]=lba_glob; + + if(buff[(8+l) & 2047]) + { + mem_index[l>>3]|=1<<(l & 7); + lba_glob+=ciso_size; + } + } + + if(ciso_lba>=0) ciso_lba=0x7fffffff; + } + + /* Free memory */ + if (buff) + dip_free(buff); + } + + + if(ciso_lba==0x7fffffff) + { + u32 temp=(lba)/ciso_size; + + + u32 read_lba=table_lba[temp>>3]; + + for(l=0;l<(temp & 7);l++) if((mem_index[temp>>3]>>l) & 1) read_lba+=ciso_size; + + read_lba+=(lba) & ((ciso_size)-1); + + + r=read_from_device_out(outbuf, size, read_lba); + if(r<0) return r; + + + } + else + { + /* Free memory */ + if (table_lba) + os_heap_free(0, table_lba); + + table_lba=NULL; + + return read_from_device_out(outbuf, size, lba); + + } + + +return r; + + +} + +// From Waninkoko dip_plugin + +void __DI_CheckDisc(void) +{ +void *buffer; +s32 ret; + + /* Allocate buffer */ + buffer = (void *) os_heap_alloc_aligned(0, SECTOR_SIZE, 32); + if (!buffer) + return; + + /* Read second layer */ + ret = read_from_device(buffer, SECTOR_SIZE, 0x47000000); + + /* Set disc type */ + dvd_type = (!ret) ? DISC_DVD9 : DISC_DVD5; + + /* Free buffer */ + os_heap_free(0, buffer); + +} +int read_id_from_image(u32 *outbuf, u32 outbuf_size) +{ + u32 res; + + res= read_from_device(outbuf, READINFO_SIZE_DATA, 0); + if(res<0) return res; + + if (outbuf[6] == WII_DVD_SIGNATURE) { + + extern u8 * addr_dvd_read_controlling_data; + + addr_dvd_read_controlling_data[0] = 1; + + if (!addr_dvd_read_controlling_data[1]) + dip_doReadHashEncryptedState(); + } + return res; +} + + +/* +Ioctl 0x13 -> usada por la función Disc_USB_DVD_Wait(), checkea si hay disco montado desde la unidad DVD. Solo se usa en uLoader + +Ioctl 0x14 -> equivale a Ioctl 0x88, pero exceptuando el uso de DVD, devuelve un estado fake para WBFS y DVD USB + +Ioctl 0x15 -> equivale a Ioctl 0x7a y devuelve el registro tal cual, exceptuando el bit 0 (para indicar la presencia del disco siempre) + */ + +static int _noinit=1; + +extern void * mem_bss; +extern int mem_bss_len; + +int DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 outbuf_size) +{ + int res = 0; + + u8 cmd = ((u8 *) inbuf)[0]; + +#ifdef DEBUG + s_printf("DIP::DI_EmulateCmd(%x, %x, %x)", cmd, outbuf, outbuf_size); +#endif + + if(_noinit) + { + + dip_memset(mem_bss, 0, mem_bss_len); + + os_sync_after_write(mem_bss, mem_bss_len); + + _noinit=0; + } + + + if (cmd != IOCTL_DI_REQERROR) + dip.currentError = 0; + + switch (cmd) { + case IOCTL_DI_REQERROR: + { + + if (dip.currentError || dip.has_id) + { + dip_memset((u8 *) outbuf, 0, outbuf_size); + outbuf[0] = dip.currentError; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + } + else goto call_original_di; + } + + break; + + case IOCTL_DI_SEEK: + res = 0; + + if (!dip.dvdrom_mode && !dip.has_id) { + res = handleDiCommand(inbuf, outbuf, outbuf_size); + + } + break; + case IOCTL_DI_WAITCOVERCLOSE: + if (!dip.has_id) + goto call_original_di; + res = 0; + break; + + case IOCTL_DI_STREAMING: + if (!dip.dvdrom_mode && + !dip.has_id) + goto call_original_di; + dip_memset((u8*) outbuf, 0, outbuf_size); + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETBASE: + dip.base = inbuf[1]; + res = 0; + break; + + case IOCTL_DI_GETBASE: + outbuf[0] = dip.base; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETFORCEREAD: + dip.low_read_from_device = inbuf[1]; + res = 0; + break; + + case IOCTL_DI_GETFORCEREAD: + outbuf[0] = dip.low_read_from_device; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_SETUSBMODE: { + dip.has_id = inbuf[1]; + // Copy id + if (dip.has_id) { + dip_memcpy(dip.id, (u8 *)&(inbuf[2]), 6); + } + dip.partition = inbuf[5]; + res = 0; + break; + } + + case IOCTL_DI_GETUSBMODE: + outbuf[0] = dip.has_id; + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + + case IOCTL_DI_DISABLERESET: + dip.disableReset = *((u8 *) (&inbuf[1])); + res = 0; + break; + + case IOCTL_DI_13: + case IOCTL_DI_14: { + volatile unsigned long *dvdio = (volatile unsigned long *) 0xD006000; + if (outbuf == NULL) { + res = 0; + break; + } + if (cmd == 0x13) { + + if (dip.has_id && usb_is_dvd){ + outbuf[0] = (usb_dvd_inserted() == 0)?0:2; + } else { + if (!dip.has_id || usb_device_fd<0) outbuf[0] = (dvdio[1] & 1)?0:2; + else outbuf[0] = 2; + } + + + } else { + + // ioctl 0x14 + if (!dip.has_id || usb_device_fd<0) outbuf[0] = (dvdio[1] & 1)?0:2; + else {outbuf[0] = 0x2;} + + } + + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + } + case IOCTL_DI_15: { + volatile unsigned long *dvdio = (volatile unsigned long *) 0xD006000; + outbuf[0] = dvdio[1] & (~0x00000001); + os_sync_after_write(outbuf, outbuf_size); + res = 0; + break; + } + + case IOCTL_DI_CUSTOMCMD: + res = DIP_CustomCommand((u8 *)((u32) inbuf[1] & 0x7FFFFFFF), (u8 *) outbuf); + break; + + case IOCTL_DI_OFFSET: { + if (dip.dvdrom_mode || dip.has_id) { + dip.offset = ((inbuf[1] << 30 ) | inbuf[2]) & 0xFFFF8000; // ~(SECTOR_SIZE >> 2) + res = 0; + } else + goto call_original_di; + break; + } + case IOCTL_DI_REPORTKEY: + if (!dip.dvdrom_mode && !dip.has_id) + goto call_original_di; + res = 0xA000; + dip.currentError = 0x53100; + break; + + case IOCTL_DI_DISC_BCA: { + u32 cont = 0; + os_sync_before_read((u8 *) bca_bytes, BCADATA_SIZE); + while (bca_bytes[cont] == 0) { + cont++; + if (cont == 64) + goto call_original_di; + } + dip_memcpy((u8 *) outbuf, bca_bytes, BCADATA_SIZE); + os_sync_after_write(outbuf,BCADATA_SIZE); + res = 0; + break; + } + + case IOCTL_DI_READ: { + dip.reading = 1; + if (!dip.low_read_from_device) + res = handleDiCommand(inbuf, outbuf, outbuf_size); + else + res = read_from_device(outbuf, inbuf[1], inbuf[2]); + dip.reading = 0; + + break; + } + case IOCTL_DI_READID: { + u32 cmdRes; + + cmdRes = 0; + + res=0; + + if (!dip.has_id) cmdRes = handleDiCommand(inbuf, outbuf, outbuf_size); + + + if (cmdRes || (dip.base | dip.offset) || dip.has_id || ((u32 *) outbuf)[6] != WII_DVD_SIGNATURE) + { + dip.dvdrom_mode = (cmdRes==0)?0:1; + res = read_id_from_image(outbuf, outbuf_size); + } + else + dip.dvdrom_mode = 0; + if (!res) + __DI_CheckDisc(); + + break; + } + case IOCTL_DI_RESET: { + if (dip.disableReset) { + res = 0; + break; + } + dip.reading = 0; + dip.low_read_from_device = 0; + dip.dvdrom_mode = 0; + dip.base = 0; + dip.offset = 0; + dip.currentError = 0; + + if (!dip.has_id) + { + ciso_lba=dip.partition-1; + goto call_original_di; + } + + DIP_StopMotor(); + res = usb_open_device(dip.has_id - 1, &(dip.id[0]), dip.partition); + break; + } + case IOCTL_DI_UNENCREAD: + case IOCTL_DI_LOWREAD: + case IOCTL_DI_READDVD: { + u32 offset = inbuf[1]; + u32 len = inbuf[2]; + if (cmd == IOCTL_DI_READDVD) { + offset = offset << 11; + len = len << 9; + } + res = read_from_device(outbuf, offset, len); + /* if (res == 0 && dip.reading == 0) + dummy_function(outbuf, outbuf_size);*/ + break; + } + + default: + call_original_di: + res = handleDiCommand(inbuf, outbuf, outbuf_size); + break; + } + return res; +} diff --git a/cios/odip_plugin/source/syscalls.s b/cios/odip_plugin/source/syscalls.s new file mode 100644 index 0000000..fb5d97f --- /dev/null +++ b/cios/odip_plugin/source/syscalls.s @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * syscalls.s (c) 2009, Hermes + * info from http://wiibrew.org/wiki/IOS/Syscalls + * + * This program is free software; you can 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 + */ + +.macro syscall vec_sys + .long 0xE6000010 +(\vec_sys<<5) + bx lr +.endm + + .align 4 + .arm + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3F + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global swi_mload_func +swi_mload_func: + svc 0xCC + bx lr + + + .code 16 + .global os_puts +os_puts: + mov r2, lr + add r1, r0, #0 + mov r0, #4 + svc 0xab + bx r2 + +/* + + // De rodries: http://www.elotrolado.net/hilo_utilidad-uloader-v2-1c-ocarina-y-forzado-de-video-idioma_1217626_s1050 + .code 32 + .global os_puts +os_puts: + mov R2,lr + adds r1,r0,#0 + movs R0,#4 + svc 0xAB + bx r2 +*/ diff --git a/cios/odip_plugin/source/utils.c b/cios/odip_plugin/source/utils.c new file mode 100644 index 0000000..534a6e8 --- /dev/null +++ b/cios/odip_plugin/source/utils.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + +#include + +void dip_memset(u8 *buf, u32 ch, u32 size) +{ + u32 unaligned; + u8 c = (u8) ch; + + int cnt = 0; + + if (size == 0) + return; + + unaligned = (4 - ((u32) buf & 0x3)) & 0x3; + + if (unaligned > size) + unaligned = size; + + if (unaligned) + while (unaligned--) { + buf[cnt] = c; + cnt++; + }; + + // Process aligned bytes + u32 c32 = (ch << 24) | (ch << 16) | (ch << 8) | ch; + + u32 *buf32 = (u32 *) (buf + cnt); + while (cnt + 4 <= size) { + *(buf32++) = c32; + cnt += 4; + } + + // Process unaligned tail bytes + while (cnt++ < size) + buf[cnt] = c; +} diff --git a/cios/odip_plugin/source/wbfs.c b/cios/odip_plugin/source/wbfs.c new file mode 100644 index 0000000..858d102 --- /dev/null +++ b/cios/odip_plugin/source/wbfs.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * Copyright (C) 2010 Hermes + * + * This program is free software; you can 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 + */ + +#include + +#include +#include +#include + +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) +#define USB_IOCTL_WBFS_OPEN_DISC (WBFS_BASE+0x1) +#define USB_IOCTL_WBFS_READ_DISC (WBFS_BASE+0x2) +#define USB_IOCTL_WBFS_READ_DIRECT_DISC (WBFS_BASE+0x3) +#define USB_IOCTL_WBFS_STS_DISC (WBFS_BASE+0x4) + +s32 usb_is_dvd=0; + +s32 usb_device_fd=-1; + +s32 fat_mode=0; + +s32 null_mode=0; + +#define USB_DATA_SIZE 64 +// Ver atributo packed +static struct _usb_data{ + struct _ioctl ioctlv[4]; + u32 values[8]; +} usb_data ATTRIBUTE_ALIGN(32); + +static struct _usb_data* usb_data_ptr; + +char filename_data[256] ATTRIBUTE_ALIGN(32) ="filename_data"; + + +#define MAX_DEVICES 2 +static char *usb_device[] = {"/dev/usb123", + "/dev/sdio/sdhc", + "/dev/usb123"}; + +s32 usb_dvd_inserted(void) +{ + if (!usb_is_dvd) + return 1; + + if (usb_device_fd < 0) + return 1; + + return os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_STS_DISC, 0,0, usb_data_ptr->ioctlv); +} + +extern int ciso_size; +extern u32 *table_lba; + +extern u8 mem_index[2048]; + +s32 usb_read_device(u8 *outbuf, u32 size, u32 lba) +{ + s32 res; + int l; + + if(null_mode) + { + dip_memset(outbuf,0xff, size); + return 0; + } + + if (usb_device_fd < 0) + return -1; + +// FAT Mode from Hermes + if(fat_mode) + { + if(fat_mode==1 || fat_mode==2) + { + u32 lba_glob=0; + + if(table_lba==NULL) table_lba= (void *) os_heap_alloc_aligned(0, 2048*4, 32); // from global heap + + /* Allocate memory */ + u8 * buff = dip_alloc_aligned(0x800, 32); + if (!buff) + fat_mode=-1; + + if(fat_mode>=0) + { + + lba_glob=(16)<<9; + + res=os_seek(usb_device_fd, 0, 0); + if(res>=0) res=os_read(usb_device_fd, buff, 2048); + if(res<0) {fat_mode=-1;} + else + if(!(buff[0]=='C' && buff[1]=='I' && buff[2]=='S' && buff[3]=='O')) fat_mode=-1; + + + + } + + if(fat_mode>=0 && table_lba) + { + + ciso_size=(((u32)buff[4])+(((u32)buff[5])<<8)+(((u32)buff[6])<<16)+(((u32)buff[7])<<24))/4; + + dip_memset(mem_index,0,2048); + + + for(l=0;l<16384;l++) + { + if(((l+8) & 2047)==0 && (l+8)>=2048) + { + if(fat_mode==2) res=os_seek(usb_device_fd, (((l+8)>>11))<<9, 0); + else + res=os_seek(usb_device_fd, (((l+8)>>11))<<11, 0); // read 16 cached sectors + + if(res>=0) res=os_read(usb_device_fd, buff, 2048); + if(res<0) {fat_mode=-1;break;} + } + + if((l & 7)==0) table_lba[l>>3]=lba_glob; + + if(buff[(8+l) & 2047]) + { + mem_index[l>>3]|=1<<(l & 7); + lba_glob+=ciso_size; + } + } + + if(fat_mode>=0) + { + fat_mode|=4; + + } + + + } + + /* Free memory */ + if (buff) + dip_free(buff); + } + + if(fat_mode>=4) + { + u32 temp=(lba)/ciso_size; + + + u32 read_lba=table_lba[temp>>3]; + + for(l=0;l<(temp & 7);l++) if((mem_index[temp>>3]>>l) & 1) read_lba+=ciso_size; + + read_lba+=(lba) & ((ciso_size)-1); + + if(fat_mode & 2) os_seek(usb_device_fd, read_lba, 0); + else + os_seek(usb_device_fd, read_lba<<2, 0); // read sectors + + res=os_read(usb_device_fd, outbuf, size); + //if(r<0) return r; + return 0; + + + } + else + { + + /* Free memory */ + if (table_lba) + os_heap_free(0, table_lba); + + table_lba=NULL; + + return -1; + + } + + } + + usb_data_ptr->values[0] = lba; + usb_data_ptr->values[7] = size; + + usb_data_ptr->ioctlv[2].data = outbuf; + usb_data_ptr->ioctlv[3].data = (u32 *) size; // No es necesario, pero estaba en el codigo original + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + os_sync_after_write(outbuf, size); + + if (usb_is_dvd) + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_READ_DIRECT_DISC, 2, 1, usb_data_ptr->ioctlv); + else + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_READ_DISC, 2, 1, usb_data_ptr->ioctlv); + + os_sync_before_read(outbuf, size); + + return res; +} + + +s32 usb_open_device(u32 device_nr, u8 *id, u32 partition) +{ + u32 res; + u32 part = partition; + + if (device_nr >= MAX_DEVICES) + return -1; + + if(usb_device_fd==0x666999) usb_device_fd=-1; + + if (usb_data_ptr == 0) { + usb_data_ptr = &usb_data; + usb_device_fd = -1; + } else if (usb_device_fd>=0) + os_close(usb_device_fd); + usb_device_fd=-1; + + usb_is_dvd = 0; fat_mode=0; + + + if((id[0]=='_' && id[1]=='N' && id[2]=='U' && id[3]=='L')) + { + fat_mode=1;null_mode=1;usb_device_fd=0x666999; + + return 0; + + } + + if((id[0]=='_' && id[1]=='D' && id[2]=='E' && id[3]=='V')) + { + fat_mode=1; + if(id[4]=='W') fat_mode=2; // change the seek operations of the device from bytes to words + + os_sync_before_read((void *) filename_data, 256); + + usb_device_fd = (int) os_open((void *) filename_data, 0); + if(usb_device_fd<0) {fat_mode=0;return usb_device_fd;} + + return 0; + + } + + if (id[0] == '_' && id[1] == 'D' && id[2] == 'V' && id[3] == 'D') + usb_is_dvd = 1; + + usb_device_fd = os_open(usb_device[device_nr] , 1); + if (usb_device_fd < 0) { + if (device_nr == 0) + usb_device_fd = os_open(usb_device[0], 1); + } + + if (usb_device_fd < 0) + return usb_device_fd; + + dip_memcpy((void *)(usb_data_ptr->values), id, 6); + dip_memcpy((void *) &(usb_data_ptr->values[7]), (u8 *) &part, sizeof(part)); + + usb_data_ptr->ioctlv[0].data = (void *) (usb_data_ptr->values); + usb_data_ptr->ioctlv[0].len = 6; + usb_data_ptr->ioctlv[1].data = (void *) &(usb_data_ptr->values[7]); + usb_data_ptr->ioctlv[1].len = sizeof(u32); + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + res = os_ioctlv(usb_device_fd, USB_IOCTL_WBFS_OPEN_DISC, 2, 0, usb_data_ptr->ioctlv); + + usb_data_ptr->ioctlv[0].data = (void *) (usb_data_ptr->values); + usb_data_ptr->ioctlv[0].len = sizeof(u32); + usb_data_ptr->ioctlv[1].data = (void *) &(usb_data_ptr->values[7]); + usb_data_ptr->ioctlv[1].len = sizeof(u32); + + os_sync_after_write(usb_data_ptr, USB_DATA_SIZE); + + return res; +} + diff --git a/cios/odip_plugin/source/wrappers.s b/cios/odip_plugin/source/wrappers.s new file mode 100644 index 0000000..93bde7b --- /dev/null +++ b/cios/odip_plugin/source/wrappers.s @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Spaceman Spiff + * + * This program is free software; you can 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 + */ + + .align 4 + + .global dip_alloc_aligned + .code 32 +dip_alloc_aligned: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_shared_alloc_aligned + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_free + .code 32 +dip_free: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_shared_free + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_memcpy + .code 32 +dip_memcpy: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_memcpy + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_fatal_di_error + .code 32 +dip_fatal_di_error: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_fatal_di_error + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_doReadHashEncryptedState + .code 32 +dip_doReadHashEncryptedState: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_doReadHashEncryptedState + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global dip_printf + .code 32 +dip_printf: + stmfd sp!, {r7, lr} + ldr r7, =addr_ios_printf + ldr r7, [r7] + bl _callOriginal + ldmfd sp!, {r7, lr} + bx lr + + .global _callOriginal + .code 32 +_callOriginal: + bx r7 + + .global handleDiCommand + .code 16 + .thumb_func +handleDiCommand: + push {r4-r7,lr} + mov r7, r11 + mov r6, r10 + mov r5, r9 + mov r4, r8 + push {r4-r7} + ldr r3, =addr_di_cmd_reentry + ldr r3, [r3] + bx r3 diff --git a/cios/readme.txt b/cios/readme.txt new file mode 100644 index 0000000..87ff1f6 --- /dev/null +++ b/cios/readme.txt @@ -0,0 +1,67 @@ + +dip_orig : waninkoko rev19 +dip_frag : + frag (devkit arm 17) + +odip_plugin : spaceman spiff & hermes cios_mload_4.1 +odip_frag : + frag (devkit arm 15) + +sdhc_orig : waninkoko rev20 +sdhc : address change for hermes (devkit arm 15) + + +addr: + + +hermes cios 5.1 / uloader 5.1E: + + ehcmodule: + exe(rwx) : ORIGIN = 0x13700000, LENGTH = 0x6000 + ram(rw) : ORIGIN = 0x13706000, LENGTH = 0x2A000 // END 0x1372E000 + + fatffs: + exe(rwx) : ORIGIN = 0x13730000, LENGTH = 0xa800 + ram(rwx) : ORIGIN = 0x1373a800, LENGTH = 0x40000 // END 1377A800 + + odip: + exe(rwx) : ORIGIN = 0x1377E000, LENGTH = 0x1600 + ram(rw) : ORIGIN = 0x1377F600, LENGTH = 0xA00 + + mload: + exe(rwx) : ORIGIN = 0x138c0000, LENGTH = 0x4000 + ram(rw) : ORIGIN = 0x138c8000, LENGTH = 0x8000 // END 138D0000 + ios_exe(rw) : ORIGIN = 0x13700000, LENGTH = 0x80000 // END 13780000 + + +cfg 59+ + + frag list - overlaps fatffs: + 0x13730000 + 3A98C = 0x1376A98C + ( 20001*(3*4) = 240012 = 0x3A98C ) + + sdhc: (for hermes cios, overlaps fatffs) + exe(rwx) : ORIGIN = 0x1376B000, LENGTH = 0x2000 + ram(rwx) : ORIGIN = 0x1376D000, LENGTH = 0xB000 // END 13778000 + used: + Writing segment 0x1376B000 to 0x00000108 (5184 bytes) - 1440 + Writing segment 0x1376D000 to 0x00001548 (224 bytes) - a20c + + odip_frag: (for hermes cios) + exe(rwx) : ORIGIN = 0x1377C000, LENGTH = 0x2200 + ram(rw) : ORIGIN = 0x1377E200, LENGTH = 0x1E00 /* END 13780000 */ + /* end must not exceed mload ios_exe end */ + + +cfg 5x + + dip_frag: (for waninkoko cios) + exe(rwx) : ORIGIN = 0x13700000, LENGTH = 0x2000 + ram(rw) : ORIGIN = 0x13702000, LENGTH = 0x40000 + + +waninkoko rev19: + + sdhc: + exe(rwx) : ORIGIN = 0x137e0000, LENGTH = 0x8000 + ram(rw) : ORIGIN = 0x137e8000, LENGTH = 0x10000 // END 0x137f8000 + + diff --git a/cios/sdhc/LICENSE.txt b/cios/sdhc/LICENSE.txt new file mode 100644 index 0000000..7e14f38 --- /dev/null +++ b/cios/sdhc/LICENSE.txt @@ -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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin 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. + + , 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. \ No newline at end of file diff --git a/cios/sdhc/Makefile b/cios/sdhc/Makefile new file mode 100644 index 0000000..d3b497e --- /dev/null +++ b/cios/sdhc/Makefile @@ -0,0 +1,54 @@ +# devkitARM path +DEVKITARM ?= /opt/devkitARM + +# Prefix +PREFIX = $(DEVKITARM)/bin/arm-eabi- + +# Executables +CC = $(PREFIX)gcc +LD = $(PREFIX)gcc +#STRIP = ./stripios +STRIP = ../stripios/stripios + +# Flags +ARCH = -mcpu=arm926ej-s -mthumb -mthumb-interwork -mbig-endian +CFLAGS = $(ARCH) -I. -fomit-frame-pointer -Os -Wall -Wstrict-prototypes -ffunction-sections +LDFLAGS = $(ARCH) -nostartfiles -Wl,-T,link.ld,-Map,$(TARGET).map -Wl,--gc-sections -Wl,-static + +# Libraries +LIBS = + +# Target +TARGET = sdhc-module + +# Objects +OBJS = es.o \ + ipc.o \ + main.o \ + mem.o \ + sdio.o \ + start.o \ + syscalls.o \ + timer.o \ + wbfs.o \ + libwbfs/libwbfs.o \ + libwbfs/rijndael.o \ + libwbfs/wiidisc.o + + +$(TARGET).elf: $(OBJS) + @echo -e " LD\t$@" + @$(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.orig + @$(STRIP) $@.orig $@ + +%.o: %.s + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o: %.c + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -c -o $@ $< + +clean: + @echo -e "Cleaning..." + @rm -f $(OBJS) $(TARGET).elf $(TARGET).elf.orig $(TARGET).map diff --git a/cios/sdhc/es.c b/cios/sdhc/es.c new file mode 100644 index 0000000..ebce4a4 --- /dev/null +++ b/cios/sdhc/es.c @@ -0,0 +1,65 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "syscalls.h" +#include "types.h" + +/* IOCTL commands */ +#define IOCTL_ES_GETTITLEID 0x20 + +/* Variables */ +static ioctlv vector[8] ATTRIBUTE_ALIGN(32); + + +s32 __ES_Init(void) +{ + /* Open /dev/es */ + return os_open("/dev/es", 0); +} + +void __ES_Close(s32 fd) +{ + /* Close /dev/es */ + os_close(fd); +} + +s32 ES_GetTitleID(u64 *tid) +{ + s32 fd, ret; + + /* Open ES */ + fd = __ES_Init(); + if (fd < 0) + return fd; + + /* Setup vector */ + vector[0].data = tid; + vector[0].len = sizeof(u64); + + /* Get title ID */ + ret = os_ioctlv(fd, IOCTL_ES_GETTITLEID, 0, 1, vector); + + /* Close ES */ + __ES_Close(fd); + + return ret; +} diff --git a/cios/sdhc/es.h b/cios/sdhc/es.h new file mode 100644 index 0000000..d7b60b9 --- /dev/null +++ b/cios/sdhc/es.h @@ -0,0 +1,10 @@ +#ifndef _ES_H_ +#define _ES_H_ + +#include "types.h" + + +/* Prototypes */ +s32 ES_GetTitleID(u64 *tid); + +#endif diff --git a/cios/sdhc/ipc.c b/cios/sdhc/ipc.c new file mode 100644 index 0000000..0033265 --- /dev/null +++ b/cios/sdhc/ipc.c @@ -0,0 +1,52 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ipc.h" +#include "syscalls.h" +#include "types.h" + + +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = 0; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Invalidate cache */ + os_sync_before_read(buffer, len); + } +} + +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = inlen; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Flush cache */ + os_sync_after_write(buffer, len); + } +} diff --git a/cios/sdhc/ipc.h b/cios/sdhc/ipc.h new file mode 100644 index 0000000..ec5e1ab --- /dev/null +++ b/cios/sdhc/ipc.h @@ -0,0 +1,81 @@ +#ifndef _IPC_H_ +#define _IPC_H_ + +#include "types.h" + +/* IPC error codes */ +#define IPC_ENOENT -6 +#define IPC_ENOMEM -22 +#define IPC_EINVAL -101 +#define IPC_EACCESS -102 +#define IPC_EEXIST -105 +#define IPC_NOENT -106 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTL vector */ +typedef struct iovec { + void *data; + u32 len; +} ioctlv; + +/* IPC message */ +typedef struct ipcmessage { + u32 command; + u32 result; + u32 fd; + + union + { + struct + { + char *device; + u32 mode; + u32 resultfd; + } open; + + struct + { + void *data; + u32 length; + } read, write; + + struct + { + s32 offset; + s32 origin; + } seek; + + struct + { + u32 command; + + u32 *buffer_in; + u32 length_in; + u32 *buffer_io; + u32 length_io; + } ioctl; + struct + { + u32 command; + + u32 num_in; + u32 num_io; + ioctlv *vector; + } ioctlv; + }; +} ATTRIBUTE_PACKED ipcmessage; + + +/* Prototypes */ +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen); +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen); + +#endif diff --git a/cios/sdhc/libwbfs/libwbfs.c b/cios/sdhc/libwbfs/libwbfs.c new file mode 100644 index 0000000..2faf17e --- /dev/null +++ b/cios/sdhc/libwbfs/libwbfs.c @@ -0,0 +1,541 @@ +// Copyright 2009 Kwiirk +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + + +#include "libwbfs.h" + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define ERROR(x) do {wbfs_error(x);goto error;}while(0) +#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) +static int force_mode=0; +void wbfs_set_force_mode(int force) +{ + force_mode = force; +} +static u8 size_to_shift(u32 size) +{ + u8 ret = 0; + while(size) + { + ret++; + size>>=1; + } + return ret-1; +} +#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24)) + + +wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector __attribute((unused)), int reset) +{ + int i=num_hd_sector,ret; + u8 *ptr,*tmp_buffer = wbfs_ioalloc(hd_sector_size); + u8 part_table[16*4]; + ret = read_hdsector(callback_data,0,1,tmp_buffer); + if(ret) + return 0; + //find wbfs partition + wbfs_memcpy(part_table,tmp_buffer+0x1be,16*4); + ptr = part_table; + for(i=0;i<4;i++,ptr+=16) + { + u32 part_lba = read_le32_unaligned(ptr+0x8); + wbfs_head_t *head = (wbfs_head_t *)tmp_buffer; + ret = read_hdsector(callback_data,part_lba,1,tmp_buffer); + // verify there is the magic. + if (head->magic == wbfs_htonl(WBFS_MAGIC)) + { + wbfs_t*p = wbfs_open_partition(read_hdsector,write_hdsector, + callback_data,hd_sector_size,0,part_lba,reset); + wbfs_iofree(tmp_buffer); + return p; + } + } + if(reset)// XXX make a empty hd partition.. + { + } + return 0; +} +wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, u32 part_lba, int reset) +{ + wbfs_t *p = wbfs_malloc(sizeof(wbfs_t)); + + wbfs_head_t *head = wbfs_ioalloc(hd_sector_size?hd_sector_size:512); + + //constants, but put here for consistancy + p->wii_sec_sz = 0x8000; + p->wii_sec_sz_s = size_to_shift(0x8000); + p->n_wii_sec = (num_hd_sector/0x8000)*hd_sector_size; + p->n_wii_sec_per_disc = 143432*2;//support for double layers discs.. + p->head = head; + p->part_lba = part_lba; + // init the partition + if (reset) + { + u8 sz_s; + wbfs_memset(head,0,hd_sector_size); + head->magic = wbfs_htonl(WBFS_MAGIC); + head->hd_sec_sz_s = size_to_shift(hd_sector_size); + head->n_hd_sec = wbfs_htonl(num_hd_sector); + // choose minimum wblk_sz that fits this partition size + for(sz_s=6;sz_s<11;sz_s++) + { + // ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits + if(p->n_wii_sec <((1U<<16)*(1<wbfs_sec_sz_s = sz_s+p->wii_sec_sz_s; + }else + read_hdsector(callback_data,p->part_lba,1,head); + if (head->magic != wbfs_htonl(WBFS_MAGIC)) + ERROR("bad magic"); + if(!force_mode && hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) + ERROR("hd sector size doesn't match"); + if(!force_mode && num_hd_sector && head->n_hd_sec != wbfs_htonl(num_hd_sector)) + ERROR("hd num sector doesn't match"); + p->hd_sec_sz = 1<hd_sec_sz_s; + p->hd_sec_sz_s = head->hd_sec_sz_s; + p->n_hd_sec = wbfs_ntohl(head->n_hd_sec); + + p->n_wii_sec = (p->n_hd_sec/p->wii_sec_sz)*(p->hd_sec_sz); + + p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; + p->wbfs_sec_sz = 1<wbfs_sec_sz_s; + p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->disc_info_sz = ALIGN_LBA(sizeof(wbfs_disc_info_t) + p->n_wbfs_sec_per_disc*2); + + //printf("hd_sector_size %X wii_sector size %X wbfs sector_size %X\n",p->hd_sec_sz,p->wii_sec_sz,p->wbfs_sec_sz); + p->read_hdsector = read_hdsector; + p->write_hdsector = write_hdsector; + p->callback_data = callback_data; + + p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec/8)>>p->hd_sec_sz_s; + + if(!reset) + p->freeblks = 0; // will alloc and read only if needed + else + { + // init with all free blocks + p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8)); + wbfs_memset(p->freeblks,0xff,p->n_wbfs_sec/8); + } + p->max_disc = (p->freeblks_lba-1)/(p->disc_info_sz>>p->hd_sec_sz_s); + if(p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t)) + p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t); + + p->tmp_buffer = wbfs_ioalloc(p->hd_sec_sz); + p->n_disc_open = 0; + return p; +error: + wbfs_free(p); + wbfs_iofree(head); + return 0; + +} + +void wbfs_sync(wbfs_t*p) +{ + // copy back descriptors + if(p->write_hdsector){ + p->write_hdsector(p->callback_data,p->part_lba+0,1, p->head); + + if(p->freeblks) + p->write_hdsector(p->callback_data,p->part_lba+p->freeblks_lba,ALIGN_LBA(p->n_wbfs_sec/8)>>p->hd_sec_sz_s, p->freeblks); + } +} +void wbfs_close(wbfs_t*p) +{ + wbfs_sync(p); + + if(p->n_disc_open) + ERROR("trying to close wbfs while discs still open"); + + wbfs_iofree(p->head); + wbfs_iofree(p->tmp_buffer); + if(p->freeblks) + wbfs_iofree(p->freeblks); + + wbfs_free(p); + +error: + return; +} + +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *discid) +{ + u32 i; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + wbfs_disc_t *d = 0; + for(i=0;imax_disc;i++) + { + if (p->head->disc_table[i]){ + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer); + if(wbfs_memcmp(discid,p->tmp_buffer,6)==0){ + d = wbfs_malloc(sizeof(*d)); + if(!d) + ERROR("allocating memory"); + d->p = p; + d->i = i; + d->header = wbfs_ioalloc(p->disc_info_sz); + if(!d->header) + ERROR("allocating memory"); + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba, + disc_info_sz_lba,d->header); + p->n_disc_open ++; +// for(i=0;in_wbfs_sec_per_disc;i++) +// printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); + return d; + } + } + } + return 0; +error: + if(d) + wbfs_iofree(d); + return 0; + +} +void wbfs_close_disc(wbfs_disc_t*d) +{ + d->p->n_disc_open --; + wbfs_iofree(d->header); + wbfs_free(d); +} +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len) +{ + + wbfs_t *p = d->p; + u16 wlba = offset>>(p->wbfs_sec_sz_s-2); + u32 iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; + u32 lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s); + u32 lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask; + u32 off = offset&((p->hd_sec_sz>>2)-1); + u16 iwlba = wbfs_ntohs(d->header->wlba_table[wlba]); + u32 len_copied; + int err = 0; + u8 *ptr = data; + if(unlikely(iwlba==0)) + return 1; + if(unlikely(off)){ + off*=4; + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<tmp_buffer); + if(err) + return err; + len_copied = p->hd_sec_sz - off; + if(likely(len < len_copied)) + len_copied = len; + wbfs_memcpy(ptr, p->tmp_buffer + off, len_copied); + len -= len_copied; + ptr += len_copied; + lba++; + if(unlikely(lba>lba_mask && len)){ + lba=0; + iwlba = wbfs_ntohs(d->header->wlba_table[++wlba]); + if(unlikely(iwlba==0)) + return 1; + } + } + while(likely(len>=p->hd_sec_sz)) + { + u32 nlb = len>>(p->hd_sec_sz_s); + + if(unlikely(lba + nlb > p->wbfs_sec_sz)) // dont cross wbfs sectors.. + nlb = p->wbfs_sec_sz-lba; + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<hd_sec_sz_s; + ptr += nlb<hd_sec_sz_s; + lba += nlb; + if(unlikely(lba>lba_mask && len)){ + lba = 0; + iwlba =wbfs_ntohs(d->header->wlba_table[++wlba]); + if(unlikely(iwlba==0)) + return 1; + } + } + if(unlikely(len)){ + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<tmp_buffer); + if(err) + return err; + wbfs_memcpy(ptr, p->tmp_buffer, len); + } + return 0; +} + +// disc listing +u32 wbfs_count_discs(wbfs_t*p) +{ + u32 i,count=0; + for(i=0;imax_disc;i++) + if (p->head->disc_table[i]) + count++; + return count; + +} +static u32 wbfs_sector_used(wbfs_t *p,wbfs_disc_info_t *di) +{ + u32 tot_blk=0,j; + for(j=0;jn_wbfs_sec_per_disc;j++) + if(wbfs_ntohs(di->wlba_table[j])) + tot_blk++; + return tot_blk; + +} +u32 wbfs_get_disc_info(wbfs_t*p, u32 index,u8 *header,int header_size,u32 *size)//size in 32 bit +{ + u32 i,count=0; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + for(i=0;imax_disc;i++) + if (p->head->disc_table[i]){ + if(count++==index) + { + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer); + if(header_size > (int)p->hd_sec_sz) + header_size = p->hd_sec_sz; + u32 magic = wbfs_ntohl(*(u32*)(p->tmp_buffer+24)); + if(magic!=0x5D1C9EA3){ + p->head->disc_table[i]=0; + return 1; + } + memcpy(header,p->tmp_buffer,header_size); + if(size) + { + u8 *header = wbfs_ioalloc(p->disc_info_sz); + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,disc_info_sz_lba,header); + u32 sec_used = wbfs_sector_used(p,(wbfs_disc_info_t *)header); + wbfs_iofree(header); + *size = sec_used<<(p->wbfs_sec_sz_s-2); + } + return 0; + } + } + return 1; +} + +static void load_freeblocks(wbfs_t*p) +{ + if(p->freeblks) + return; + // XXX should handle malloc error.. + p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8)); + p->read_hdsector(p->callback_data,p->part_lba+p->freeblks_lba,ALIGN_LBA(p->n_wbfs_sec/8)>>p->hd_sec_sz_s, p->freeblks); + +} +u32 wbfs_count_usedblocks(wbfs_t*p) +{ + u32 i,j,count=0; + load_freeblocks(p); + for(i=0;in_wbfs_sec/(8*4);i++) + { + u32 v = wbfs_ntohl(p->freeblks[i]); + if(v == ~0U) + count+=32; + else if(v!=0) + for(j=0;j<32;j++) + if (v & (1<n_wbfs_sec/(8*4);i++) + { + u32 v = wbfs_ntohl(p->freeblks[i]); + if(v != 0) + { + for(j=0;j<32;j++) + if (v & (1<freeblks[i] = wbfs_htonl(v & ~(1<freeblks[i]); + p->freeblks[i] = wbfs_htonl(v | 1<wbfs_sec_sz_s-p->wii_sec_sz_s); + wiidisc_t *d = 0; + u8 *used = 0; + wbfs_disc_info_t *info = 0; + u8* copy_buffer = 0; + used = wbfs_malloc(p->n_wii_sec_per_disc); + if(!used) + ERROR("unable to alloc memory"); + if(!copy_1_1) + { + d = wd_open_disc(read_src_wii_disc,callback_data); + if(!d) + ERROR("unable to open wii disc"); + wd_build_disc_usage(d,sel,used); + wd_close_disc(d); + d = 0; + } + + + for(i=0;imax_disc;i++)// find a free slot. + if(p->head->disc_table[i]==0) + break; + if(i==p->max_disc) + ERROR("no space left on device (table full)"); + p->head->disc_table[i] = 1; + discn = i; + load_freeblocks(p); + + // build disc info + info = wbfs_ioalloc(p->disc_info_sz); + u8*b = (u8*)info; + read_src_wii_disc(callback_data,0,0x100,info->disc_header_copy); + fprintf(stderr, "adding %c%c%c%c%c%c %s...\n",b[0], b[1], b[2], b[3], b[4], b[5], b + 0x20); + + copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz); + if(!copy_buffer) + ERROR("alloc memory"); + tot=0; + cur=0; + if(spinner){ + // count total number to write for spinner + for(i=0; in_wbfs_sec_per_disc;i++) + if(copy_1_1 || block_used(used,i,wii_sec_per_wbfs_sect)) tot++; + spinner(0,tot); + } + for(i=0; in_wbfs_sec_per_disc;i++){ + u16 bl = 0; + if(copy_1_1 || block_used(used,i,wii_sec_per_wbfs_sect)) { + bl = alloc_block(p); + if (bl==0xffff) + ERROR("no space left on device (disc full)"); + read_src_wii_disc(callback_data,i*(p->wbfs_sec_sz>>2),p->wbfs_sec_sz,copy_buffer); + + //fix the partition table. + if(i==(0x40000>>p->wbfs_sec_sz_s)) + wd_fix_partition_table(d, sel, copy_buffer+(0x40000&(p->wbfs_sec_sz-1))); + + p->write_hdsector(p->callback_data,p->part_lba+bl*(p->wbfs_sec_sz/p->hd_sec_sz), + p->wbfs_sec_sz/p->hd_sec_sz,copy_buffer); + cur++; + if(spinner) + spinner(cur,tot); + } + info->wlba_table[i] = wbfs_htons(bl); + } + // write disc info + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + p->write_hdsector(p->callback_data,p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,info); + wbfs_sync(p); +error: + if(d) + wd_close_disc(d); + if(used) + wbfs_free(used); + if(info) + wbfs_iofree(info); + if(copy_buffer) + wbfs_iofree(copy_buffer); + // init with all free blocks + + return 0; +} +u32 wbfs_rm_disc(wbfs_t*p, u8* discid) +{ + wbfs_disc_t *d = wbfs_open_disc(p,discid); + int i; + int discn = 0; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + if(!d) + return 1; + load_freeblocks(p); + discn = d->i; + for( i=0; i< p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]); + if (iwlba) + free_block(p,iwlba); + } + memset(d->header,0,p->disc_info_sz); + p->write_hdsector(p->callback_data,p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,d->header); + p->head->disc_table[discn] = 0; + wbfs_close_disc(d); + wbfs_sync(p); + return 0; +} + +// trim the file-system to its minimum size +u32 wbfs_trim(wbfs_t*p); + +// data extraction +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner) +{ + wbfs_t *p = d->p; + u8* copy_buffer = 0; + int i; + int src_wbs_nlb=p->wbfs_sec_sz/p->hd_sec_sz; + int dst_wbs_nlb=p->wbfs_sec_sz/p->wii_sec_sz; + copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz); + if(!copy_buffer) + ERROR("alloc memory"); + + for( i=0; i< p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]); + if (iwlba) + { + + if(spinner) + spinner(i,p->n_wbfs_sec_per_disc); + p->read_hdsector(p->callback_data, p->part_lba + iwlba*src_wbs_nlb, src_wbs_nlb, copy_buffer); + write_dst_wii_sector(callback_data, i*dst_wbs_nlb, dst_wbs_nlb, copy_buffer); + } + } + wbfs_iofree(copy_buffer); + return 0; +error: + return 1; +} +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); diff --git a/cios/sdhc/libwbfs/libwbfs.h b/cios/sdhc/libwbfs/libwbfs.h new file mode 100644 index 0000000..a1f3441 --- /dev/null +++ b/cios/sdhc/libwbfs/libwbfs.h @@ -0,0 +1,215 @@ +#ifndef LIBWBFS_H +#define LIBWBFS_H + +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs +#include "wiidisc.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef u32 be32_t; +typedef u16 be16_t; + + + +typedef struct wbfs_head +{ + be32_t magic; + // parameters copied in the partition for easy dumping, and bug reports + be32_t n_hd_sec; // total number of hd_sec in this partition + u8 hd_sec_sz_s; // sector size in this partition + u8 wbfs_sec_sz_s; // size of a wbfs sec + u8 padding3[2]; + u8 disc_table[0]; // size depends on hd sector size +}__attribute((packed)) wbfs_head_t ; + +typedef struct wbfs_disc_info +{ + u8 disc_header_copy[0x100]; + be16_t wlba_table[0]; +}wbfs_disc_info_t; + +// WBFS first wbfs_sector structure: +// +// ----------- +// | wbfs_head | (hd_sec_sz) +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | ... | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// |freeblk_tbl| +// | | +// ----------- +// + +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +typedef int (*rw_sector_callback_t)(void*fp,u32 lba,u32 count,void*iobuf); +typedef void (*progress_callback_t)(int status,int total); + + +typedef struct wbfs_s +{ + wbfs_head_t *head; + + /* hdsectors, the size of the sector provided by the hosting hard drive */ + u32 hd_sec_sz; + u8 hd_sec_sz_s; // the power of two of the last number + u32 n_hd_sec; // the number of hd sector in the wbfs partition + + /* standard wii sector (0x8000 bytes) */ + u32 wii_sec_sz; + u8 wii_sec_sz_s; + u32 n_wii_sec; + u32 n_wii_sec_per_disc; + + /* The size of a wbfs sector */ + u32 wbfs_sec_sz; + u32 wbfs_sec_sz_s; + u16 n_wbfs_sec; // this must fit in 16 bit! + u16 n_wbfs_sec_per_disc; // size of the lookup table + + u32 part_lba; + /* virtual methods to read write the partition */ + rw_sector_callback_t read_hdsector; + rw_sector_callback_t write_hdsector; + void *callback_data; + + u16 max_disc; + u32 freeblks_lba; + u32 *freeblks; + u16 disc_info_sz; + + u8 *tmp_buffer; // pre-allocated buffer for unaligned read + + u32 n_disc_open; + +}wbfs_t; + +typedef struct wbfs_disc_s +{ + wbfs_t *p; + wbfs_disc_info_t *header; // pointer to wii header + int i; // disc index in the wbfs header (disc_table) +}wbfs_disc_t; + + +#define WBFS_MAGIC (('W'<<24)|('B'<<16)|('F'<<8)|('S')) + +/*! @brief open a MSDOS partitionned harddrive. This tries to find a wbfs partition into the harddrive + @param read_hdsector,write_hdsector: accessors to a harddrive + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this disc. Can be set to zero if the partition in already initialized + @reset: not implemented, This will format the whole harddrive with one wbfs partition that fits the whole disk. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, int reset); + +/*! @brief open a wbfs partition + @param read_hdsector,write_hdsector: accessors to the partition + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this partition. Can be set to zero if the partition in already initialized + @partition_lba: The partitio offset if you provided accessors to the whole disc. + @reset: initialize the partition with an empty wbfs. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, u32 partition_lba, int reset); + + +/*! @brief close a wbfs partition, and sync the metadatas to the disc */ +void wbfs_close(wbfs_t*); + +/*! @brief open a disc inside a wbfs partition use a 6 char discid+vendorid + @return NULL if discid is not present +*/ +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *diskid); + +/*! @brief close a already open disc inside a wbfs partition */ +void wbfs_close_disc(wbfs_disc_t*d); + + +/*! @brief accessor to the wii disc + @param d: a pointer to already open disc + @param offset: an offset inside the disc, *points 32bit words*, allowing to access 16GB data + @param len: The length of the data to fetch, in *bytes* + */ +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len); + +/*! @return the number of discs inside the paritition */ +u32 wbfs_count_discs(wbfs_t*p); +/*! get the disc info of ith disc inside the partition. It correspond to the first 0x100 bytes of the wiidvd + http://www.wiibrew.org/wiki/Wiidisc#Header + @param i: index of the disc inside the partition + @param header: pointer to 0x100 bytes to write the header + @size: optional pointer to a 32bit word that will get the size in 32bit words of the DVD taken on the partition. +*/ +u32 wbfs_get_disc_info(wbfs_t*p, u32 i,u8 *header,int header_size,u32 *size); + +/*! get the number of used block of the partition. + to be multiplied by p->wbfs_sec_sz (use 64bit multiplication) to have the number in bytes +*/ +u32 wbfs_count_usedblocks(wbfs_t*p); + +/******************* write access ******************/ + +/*! add a wii dvd inside the partition + @param read_src_wii_disc: a callback to access the wii dvd. offsets are in 32bit, len in bytes! + @callback_data: private data passed to the callback + @spinner: a pointer to a function that is regulary called to update a progress bar. + @sel: selects which partitions to copy. + @copy_1_1: makes a 1:1 copy, whenever a game would not use the wii disc format, and some data is hidden outside the filesystem. + */ +u32 wbfs_add_disc(wbfs_t*p,read_wiidisc_callback_t read_src_wii_disc, void *callback_data, + progress_callback_t spinner,partition_selector_t sel,int copy_1_1); + + +/*! remove a wiidvd inside a partition */ +u32 wbfs_rm_disc(wbfs_t*p, u8* discid); + + +/*! trim the file-system to its minimum size + This allows to use wbfs as a wiidisc container + */ +u32 wbfs_trim(wbfs_t*p); + +/*! extract a disc from the wbfs, unused sectors are just untouched, allowing descent filesystem to only really usefull space to store the disc. +Even if the filesize is 4.7GB, the disc usage will be less. + */ +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner); + +/*! extract a file from the wii disc filesystem. + E.G. Allows to extract the opening.bnr to install a game as a system menu channel + */ +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); + +// remove some sanity checks +void wbfs_set_force_mode(int force); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/cios/sdhc/libwbfs/rijndael.c b/cios/sdhc/libwbfs/rijndael.c new file mode 100644 index 0000000..baf8c87 --- /dev/null +++ b/cios/sdhc/libwbfs/rijndael.c @@ -0,0 +1,398 @@ +/* Rijndael Block Cipher - rijndael.c + + Written by Mike Scott 21st April 1999 + mike@compapp.dcu.ie + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + +*/ + +#include +#include + +#define u8 unsigned char /* 8 bits */ +#define u32 unsigned long /* 32 bits */ +#define u64 unsigned long long + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 byte */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4]={0xB,0xD,0x9,0xE}; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256],ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; + +/* Parameter-dependent data */ + +int Nk,Nb,Nr; +u8 fi[24],ri[24]; +u32 fkey[120]; +u32 rkey[120]; + +static u32 pack(u8 *b) +{ /* pack bytes into a 32-bit Word */ + return ((u32)b[3]<<24)|((u32)b[2]<<16)|((u32)b[1]<<8)|(u32)b[0]; +} + +static void unpack(u32 a,u8 *b) +{ /* unpack bytes from a word */ + b[0]=(u8)a; + b[1]=(u8)(a>>8); + b[2]=(u8)(a>>16); + b[3]=(u8)(a>>24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a&0x80) b=0x1B; + else b=0; + a<<=1; + a^=b; + return a; +} + +static u8 bmul(u8 x,u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) return ptab[(ltab[x]+ltab[y])%255]; + else return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a,b); + b[0]=fbsub[b[0]]; + b[1]=fbsub[b[1]]; + b[2]=fbsub[b[2]]; + b[3]=fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x,u32 y) +{ /* dot product of two 4-byte arrays */ + u8 xb[4],yb[4]; + unpack(x,xb); + unpack(y,yb); + return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y,m; + u8 b[4]; + + m=pack(InCo); + b[3]=product(m,x); + m=ROTL24(m); + b[2]=product(m,x); + m=ROTL24(m); + b[1]=product(m,x); + m=ROTL24(m); + b[0]=product(m,x); + y=pack(b); + return y; +} + +u8 ByteSub(u8 x) +{ + u8 y=ptab[255-ltab[x]]; /* multiplicative inverse */ + x=y; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; y^=0x63; + return y; +} + +void gentables(void) +{ /* generate tables */ + int i; + u8 y,b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0]=0; + ptab[0]=1; ltab[1]=0; + ptab[1]=3; ltab[3]=1; + for (i=2;i<256;i++) + { + ptab[i]=ptab[i-1]^xtime(ptab[i-1]); + ltab[ptab[i]]=i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0]=0x63; + rbsub[0x63]=0; + for (i=1;i<256;i++) + { + y=ByteSub((u8)i); + fbsub[i]=y; rbsub[y]=i; + } + + for (i=0,y=1;i<30;i++) + { + rco[i]=y; + y=xtime(y); + } + + /* calculate forward and reverse tables */ + for (i=0;i<256;i++) + { + y=fbsub[i]; + b[3]=y^xtime(y); b[2]=y; + b[1]=y; b[0]=xtime(y); + ftable[i]=pack(b); + + y=rbsub[i]; + b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y); + b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y); + rtable[i]=pack(b); + } +} + +void gkey(int nb,int nk,char *key) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i,j,k,m,N; + int C1,C2,C3; + u32 CipherKey[8]; + + Nb=nb; Nk=nk; + + /* Nr is number of rounds */ + if (Nb>=Nk) Nr=6+Nb; + else Nr=6+Nk; + + C1=1; + if (Nb<8) { C2=2; C3=3; } + else { C2=3; C3=4; } + + /* pre-calculate forward and reverse increments */ + for (m=j=0;j>8)])^ + ROTL16(ftable[(u8)(x[fi[m+1]]>>16)])^ + ROTL24(ftable[x[fi[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)fbsub[(u8)(x[fi[m+1]]>>16)])^ + ROTL24((u32)fbsub[x[fi[m+2]]>>24]); + } + for (i=j=0;i>8)])^ + ROTL16(rtable[(u8)(x[ri[m+1]]>>16)])^ + ROTL24(rtable[x[ri[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)rbsub[(u8)(x[ri[m+1]]>>16)])^ + ROTL24((u32)rbsub[x[ri[m+2]]>>24]); + } + for (i=j=0;i +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include "wiidisc.h" + +void aes_set_key(u8 *key); +void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len); + +static void _decrypt_title_key(u8 *tik, u8 *title_key) +{ + u8 common_key[16]={ + 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, + 0x73, 0x81, 0xaa, 0xf7 + };; + u8 iv[16]; + + wbfs_memset(iv, 0, sizeof iv); + wbfs_memcpy(iv, tik + 0x01dc, 8); + aes_set_key(common_key); + //_aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key); + aes_decrypt(iv, tik + 0x01bf,title_key,16); +} +static u32 _be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static void disc_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + if(data){ + int ret=0; + if(len==0) + return ; + ret = d->read(d->fp,offset,len,data); + if(ret) + wbfs_fatal("error reading disc"); + } + if(d->sector_usage_table) + { + u32 blockno = offset>>13; + do + { + d->sector_usage_table[blockno]=1; + blockno+=1; + if(len>0x8000) + len-=0x8000; + }while(len>0x8000); + } +} + +static void partition_raw_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + disc_read(d, d->partition_raw_offset + offset, data, len); +} + +static void partition_read_block(wiidisc_t *d,u32 blockno, u8 *block) +{ + u8*raw = d->tmp_buffer; + u8 iv[16]; + u32 offset; + if(d->sector_usage_table) + d->sector_usage_table[d->partition_block+blockno]=1; + offset = d->partition_data_offset + ((0x8000>>2) * blockno); + partition_raw_read(d,offset, raw, 0x8000); + + // decrypt data + memcpy(iv, raw + 0x3d0, 16); + aes_set_key(d->disc_key); + aes_decrypt(iv, raw + 0x400,block,0x7c00); +} + +static void partition_read(wiidisc_t *d,u32 offset, u8 *data, u32 len,int fake) +{ + u8 *block = d->tmp_buffer2; + u32 offset_in_block; + u32 len_in_block; + if(fake && d->sector_usage_table==0) + return; + + while(len) { + offset_in_block = offset % (0x7c00>>2); + len_in_block = 0x7c00 - (offset_in_block<<2); + if (len_in_block > len) + len_in_block = len; + if(!fake){ + partition_read_block(d,offset / (0x7c00>>2), block); + wbfs_memcpy(data, block + (offset_in_block<<2), len_in_block); + }else + d->sector_usage_table[d->partition_block+(offset/(0x7c00>>2))]=1; + data += len_in_block; + offset += len_in_block>>2; + len -= len_in_block; + } +} + + +static u32 do_fst(wiidisc_t *d,u8 *fst, const char *names, u32 i) +{ + u32 offset; + u32 size; + const char *name; + u32 j; + + name = names + (_be32(fst + 12*i) & 0x00ffffff); + size = _be32(fst + 12*i + 8); + + if (i == 0) { + for (j = 1; j < size && !d->extracted_buffer; ){ + j = do_fst(d,fst, names, j); + } + return size; + } + //printf("name %s\n",name); + + if (fst[12*i]) { + + for (j = i + 1; j < size && !d->extracted_buffer; ) + j = do_fst(d,fst, names, j); + + return size; + } else { + offset = _be32(fst + 12*i + 4); + if(d->extract_pathname && strcmp(name, d->extract_pathname)==0) + { + d->extracted_buffer = wbfs_ioalloc(size); + partition_read(d,offset, d->extracted_buffer, size,0); + }else + partition_read(d,offset, 0, size,1); + return i + 1; + } +} + +static void do_files(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x480); // XXX: determine actual header size + u32 dol_offset; + u32 fst_offset; + u32 fst_size; + u32 apl_offset; + u32 apl_size; + u8 *apl_header = wbfs_ioalloc(0x20); + u8 *fst; + u32 n_files; + partition_read(d,0, b, 0x480,0); + + dol_offset = _be32(b + 0x0420); + fst_offset = _be32(b + 0x0424); + fst_size = _be32(b + 0x0428)<<2; + + apl_offset = 0x2440>>2; + partition_read(d,apl_offset, apl_header, 0x20,0); + apl_size = 0x20 + _be32(apl_header + 0x14) + _be32(apl_header + 0x18); + // fake read dol and partition + partition_read(d,apl_offset, 0, apl_size,1); + partition_read(d,dol_offset, 0, (fst_offset - dol_offset)<<2,1); + + + fst = wbfs_ioalloc(fst_size); + if (fst == 0) + wbfs_fatal("malloc fst"); + partition_read(d,fst_offset, fst, fst_size,0); + n_files = _be32(fst + 8); + + if (n_files > 1) + do_fst(d,fst, (char *)fst + 12*n_files, 0); + wbfs_iofree(b); + wbfs_iofree(apl_header); + wbfs_iofree(fst); +} + +static void do_partition(wiidisc_t*d) +{ + u8 *tik = wbfs_ioalloc(0x2a4); + u8 *b = wbfs_ioalloc(0x1c); + u64 tmd_offset; + u32 tmd_size; + u8 *tmd; + u64 cert_offset; + u32 cert_size; + u8 *cert; + u64 h3_offset; + + // read ticket, and read some offsets and sizes + partition_raw_read(d,0, tik, 0x2a4); + partition_raw_read(d,0x2a4>>2, b, 0x1c); + + tmd_size = _be32(b); + tmd_offset = _be32(b + 4); + cert_size = _be32(b + 8); + cert_offset = _be32(b + 0x0c); + h3_offset = _be32(b + 0x10); + d->partition_data_offset = _be32(b + 0x14); + d->partition_block = (d->partition_raw_offset+d->partition_data_offset)>>13; + tmd = wbfs_ioalloc(tmd_size); + if (tmd == 0) + wbfs_fatal("malloc tmd"); + partition_raw_read(d,tmd_offset, tmd, tmd_size); + + cert = wbfs_ioalloc(cert_size); + if (cert == 0) + wbfs_fatal("malloc cert"); + partition_raw_read(d,cert_offset, cert, cert_size); + + + _decrypt_title_key(tik, d->disc_key); + + partition_raw_read(d,h3_offset, 0, 0x18000); + wbfs_iofree(b); + wbfs_iofree(tik); + wbfs_iofree(cert); + wbfs_iofree(tmd); + + do_files(d); + +} +static int test_parition_skip(u32 partition_type,partition_selector_t part_sel) +{ + switch(part_sel) + { + case ALL_PARTITIONS: + return 0; + case REMOVE_UPDATE_PARTITION: + return (partition_type==1); + case ONLY_GAME_PARTITION: + return (partition_type!=0); + default: + return (partition_type!=part_sel); + } +} +static void do_disc(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x100); + u64 partition_offset[32]; // XXX: don't know the real maximum + u64 partition_type[32]; // XXX: don't know the real maximum + u32 n_partitions; + u32 magic; + u32 i; + disc_read(d,0, b, 0x100); + magic=_be32(b+24); + if(magic!=0x5D1C9EA3){ + wbfs_error("not a wii disc"); + return ; + } + disc_read(d,0x40000>>2, b, 0x100); + n_partitions = _be32(b); + disc_read(d,_be32(b + 4), b, 0x100); + for (i = 0; i < n_partitions; i++){ + partition_offset[i] = _be32(b + 8 * i); + partition_type[i] = _be32(b + 8 * i+4); + } + for (i = 0; i < n_partitions; i++) { + d->partition_raw_offset = partition_offset[i]; + if(!test_parition_skip(partition_type[i],d->part_sel)) + do_partition(d); + } + wbfs_iofree(b); +} + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp) +{ + wiidisc_t *d = wbfs_malloc(sizeof(wiidisc_t)); + if(!d) + return 0; + wbfs_memset(d,0,sizeof(wiidisc_t)); + d->read = read; + d->fp = fp; + d->part_sel = ALL_PARTITIONS; + d->tmp_buffer = wbfs_ioalloc(0x8000); + d->tmp_buffer2 = wbfs_malloc(0x8000); + + return d; +} +void wd_close_disc(wiidisc_t *d) +{ + wbfs_iofree(d->tmp_buffer); + wbfs_free(d->tmp_buffer2); + wbfs_free(d); +} +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +// XXX pathname not implemented. files are extracted by their name. +// first file found with that name is returned. +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname) +{ + u8 *retval = 0; + d->extract_pathname = pathname; + d->extracted_buffer = 0; + d->part_sel = partition_type; + do_disc(d); + d->extract_pathname = 0; + d->part_sel = ALL_PARTITIONS; + retval = d->extracted_buffer; + d->extracted_buffer = 0; + return retval; +} + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table) +{ + d->sector_usage_table = usage_table; + wbfs_memset(usage_table,0,143432*2); + d->part_sel = selector; + do_disc(d); + d->part_sel = ALL_PARTITIONS; + d->sector_usage_table = 0; +} + +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table) +{ + u8 *b = partition_table; + u32 partition_offset; + u32 partition_type; + u32 n_partitions,i,j; + u32 *b32; + if(selector == ALL_PARTITIONS) + return; + n_partitions = _be32(b); + if(_be32(b + 4)-(0x40000>>2) >0x50) + wbfs_fatal("cannot modify this partition table. Please report the bug."); + + b += (_be32(b + 4)-(0x40000>>2))*4; + j=0; + for (i = 0; i < n_partitions; i++){ + partition_offset = _be32(b + 8 * i); + partition_type = _be32(b + 8 * i+4); + if(!test_parition_skip(partition_type,selector)) + { + b32 = (u32*)(b + 8 * j); + b32[0] = wbfs_htonl(partition_offset); + b32[1] = wbfs_htonl(partition_type); + j++; + } + } + b32 = (u32*)(partition_table); + *b32 = wbfs_htonl(j); +} + diff --git a/cios/sdhc/libwbfs/wiidisc.h b/cios/sdhc/libwbfs/wiidisc.h new file mode 100644 index 0000000..24a27f4 --- /dev/null +++ b/cios/sdhc/libwbfs/wiidisc.h @@ -0,0 +1,67 @@ +#ifndef WIIDISC_H +#define WIIDISC_H +#include +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs and wiidisc + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ +#if 0 //removes extra automatic indentation by editors + } +#endif +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +// offset points 32bit words, count counts bytes +typedef int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf); + +typedef enum{ + UPDATE_PARTITION_TYPE=0, + GAME_PARTITION_TYPE, + OTHER_PARTITION_TYPE, + // value in between selects partition types of that value + ALL_PARTITIONS=0xffffffff-3, + REMOVE_UPDATE_PARTITION, // keeps game + channel installers + ONLY_GAME_PARTITION, +}partition_selector_t; + +typedef struct wiidisc_s +{ + read_wiidisc_callback_t read; + void *fp; + u8 *sector_usage_table; + + // everything points 32bit words. + u32 disc_raw_offset; + u32 partition_raw_offset; + u32 partition_data_offset; + u32 partition_data_size; + u32 partition_block; + + u8 *tmp_buffer; + u8 *tmp_buffer2; + u8 disc_key[16]; + int dont_decrypt; + + partition_selector_t part_sel; + + char *extract_pathname; + u8 *extracted_buffer; +}wiidisc_t; + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp); +void wd_close_disc(wiidisc_t *); +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname); + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table); + +// effectively remove not copied partition from the partition table. +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table); + +#if 0 +{ +#endif +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/cios/sdhc/libwbfs_os.h b/cios/sdhc/libwbfs_os.h new file mode 100644 index 0000000..ab6fe5c --- /dev/null +++ b/cios/sdhc/libwbfs_os.h @@ -0,0 +1,27 @@ +#ifndef _LIBWBFS_OS_H_ +#define _LIBWBFS_OS_H_ + +#include "mem.h" +#include "types.h" + +#define debug_printf(fmt, ...) + +#define wbfs_fatal(x) do { debug_printf("\nwbfs panic:%s\n\n", x); while(1); } while(0) +#define wbfs_error(x) do { debug_printf("\nwbfs error:%s\n\n", x); } while(0) +#define wbfs_malloc(x) Mem_Alloc(x) +#define wbfs_free(x) Mem_Free(x) +#define wbfs_ioalloc(x) Mem_Alloc(x) +#define wbfs_iofree(x) Mem_Free(x) +#define wbfs_ntohl(x) (x) +#define wbfs_htonl(x) (x) +#define wbfs_ntohs(x) (x) +#define wbfs_htons(x) (x) + +#include + +#define wbfs_memcmp(x,y,z) memcmp(x,y,z) +#define wbfs_memcpy(x,y,z) memcpy(x,y,z) +#define wbfs_memset(x,y,z) memset(x,y,z) + + +#endif diff --git a/cios/sdhc/link.ld b/cios/sdhc/link.ld new file mode 100644 index 0000000..cd599e6 --- /dev/null +++ b/cios/sdhc/link.ld @@ -0,0 +1,71 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +/* Sections area */ +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x1376B000, LENGTH = 0x2000 + ram(rwx) : ORIGIN = 0x1376D000, LENGTH = 0xB000 /* END 13778000 */ +} + +__exe_start_phys__ = 0x1376B000; +__ram_start_phys__ = 0x1376D000; + +/* +orig: +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x137e0000, LENGTH = 0x8000 + ram(rw) : ORIGIN = 0x137e8000, LENGTH = 0x10000 +} +__exe_start_phys__ = 0x137e0000; +__ram_start_phys__ = 0x137e8000; +*/ + + +SECTIONS { + .ios_info_table : { + KEEP (*(.ios_info_table)) + } > table + + .init : AT (__exe_start_phys__) { + *(.init) + . = ALIGN(4); + } > exe + + .text : { + *(.text*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } > exe + + .data : AT (__ram_start_phys__) { + *(.data*) + *(.data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(4); + } > ram + + .rodata : { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .bss : { + *(.dynsbss) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(COMMON) + KEEP(*(.ios_bss)) + . = ALIGN(4); + } > ram +} diff --git a/cios/sdhc/main.c b/cios/sdhc/main.c new file mode 100644 index 0000000..dd42b89 --- /dev/null +++ b/cios/sdhc/main.c @@ -0,0 +1,223 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "es.h" +#include "ipc.h" +#include "mem.h" +#include "module.h" +#include "sdio.h" +#include "syscalls.h" +#include "timer.h" +#include "types.h" +#include "wbfs.h" + + +s32 __SDHC_Ioctlv(u32 cmd, ioctlv *vector, u32 inlen, u32 iolen) +{ + s32 ret = IPC_EINVAL; + + /* Invalidate cache */ + InvalidateVector(vector, inlen, iolen); + + /* Parse IOCTLV command */ + switch (cmd) { + /** Initialize SDHC **/ + case IOCTL_SDHC_INIT: { + /* Initialize SDIO */ + ret = !sdio_Startup(); + + break; + } + + /** Read sectors **/ + case IOCTL_SDHC_READ: { + u32 sector = *(u32 *)(vector[0].data); + u32 numSectors = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Read sectors */ + ret = !sdio_ReadSectors(sector, numSectors, buffer); + + break; + } + + /** Write sectors **/ + case IOCTL_SDHC_WRITE: { + u32 sector = *(u32 *)(vector[0].data); + u32 numSectors = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Write sectors */ + ret = !sdio_WriteSectors(sector, numSectors, buffer); + + break; + } + + /** Check for SD card **/ + case IOCTL_SDHC_ISINSERTED: { + /* Check if SD card is inserted */ + ret = !sdio_IsInserted(); + + break; + } + + /** Open WBFS disc **/ + case IOCTL_WBFS_OPEN_DISC: { + u8 *discid = (u8 *)(vector[0].data); + + /* Open WBFS disc */ + ret = WBFS_OpenDisc(discid); + + break; + } + + /** Read WBFS disc **/ + case IOCTL_WBFS_READ_DISC: { + u32 offset = *(u32 *)(vector[0].data); + u32 len = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Read WBFS disc */ + ret = WBFS_Read(buffer, len, offset); + if (ret) + ret = 0x8000; + + break; + } + + default: + break; + } + + /* Flush cache */ + FlushVector(vector, inlen, iolen); + + return ret; +} + +s32 __SDHC_Initialize(u32 *queuehandle) +{ + void *buffer = NULL; + s32 ret; + + /* Initialize memory heap */ + Mem_Init(); + + /* Initialize timer subsystem */ + Timer_Init(); + + /* Allocate queue buffer */ + buffer = Mem_Alloc(0x80); + if (!buffer) + return IPC_ENOMEM; + + /* Create message queue */ + ret = os_message_queue_create(buffer, 32); + if (ret < 0) + return ret; + + /* Register devices */ + os_device_register(DEVICE_NAME, ret); + + /* Copy queue handler */ + *queuehandle = ret; + + return 0; +} + + +int main(void) +{ + u32 queuehandle; + s32 ret; + + /* Print info */ + write("$IOSVersion: SDHC: " __DATE__ " " __TIME__ " 64M$\n"); + + /* Initialize module */ + ret = __SDHC_Initialize(&queuehandle); + if (ret < 0) + return ret; + + /* Main loop */ + while (1) { + ipcmessage *message = NULL; + + /* Wait for message */ + os_message_queue_receive(queuehandle, (void *)&message, 0); + + switch (message->command) { + case IOS_OPEN: { + u64 tid; + + /* Get title ID */ + ret = ES_GetTitleID(&tid); + + /* Check title ID */ + if (ret >= 0) { + write("SDHC: Title identified. Blocking opening request.\n"); + + ret = IPC_ENOENT; + break; + } + + /* Check device path */ + if (!strcmp(message->open.device, DEVICE_NAME)) + ret = message->open.resultfd; + else + ret = IPC_ENOENT; + + break; + } + + case IOS_CLOSE: { + /* Close SDIO */ + ret = !sdio_Shutdown(); + + break; + } + + case IOS_IOCTLV: { + ioctlv *vector = message->ioctlv.vector; + u32 inlen = message->ioctlv.num_in; + u32 iolen = message->ioctlv.num_io; + u32 cmd = message->ioctlv.command; + + /* Parse IOCTLV message */ + ret = __SDHC_Ioctlv(cmd, vector, inlen, iolen); + + break; + } + + default: + /* Unknown command */ + ret = IPC_EINVAL; + } + + /* Acknowledge message */ + os_message_queue_ack(message, ret); + } + + return 0; +} diff --git a/cios/sdhc/mem.c b/cios/sdhc/mem.c new file mode 100644 index 0000000..b8ea978 --- /dev/null +++ b/cios/sdhc/mem.c @@ -0,0 +1,54 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "syscalls.h" + +/* Heap */ +static u32 heapspace[0x2000] ATTRIBUTE_ALIGN(32); + +/* Variables */ +static s32 hid = -1; + + +s32 Mem_Init(void) +{ + /* Heap already created */ + if (hid >= 0) + return 0; + + /* Create heap */ + hid = os_heap_create(heapspace, sizeof(heapspace)); + + return (hid >= 0) ? 0 : -1; +} + +void *Mem_Alloc(u32 size) +{ + /* Allocate memory */ + return os_heap_alloc_aligned(hid, size, 32); +} + +void Mem_Free(void *ptr) +{ + /* Free memory */ + os_heap_free(hid, ptr); +} diff --git a/cios/sdhc/mem.h b/cios/sdhc/mem.h new file mode 100644 index 0000000..84e8428 --- /dev/null +++ b/cios/sdhc/mem.h @@ -0,0 +1,11 @@ +#ifndef _MEM_H_ +#define _MEM_H_ + +#include "types.h" + +/* Prototypes */ +s32 Mem_Init(void); +void *Mem_Alloc(u32 size); +void Mem_Free(void *ptr); + +#endif diff --git a/cios/sdhc/module.h b/cios/sdhc/module.h new file mode 100644 index 0000000..42a5092 --- /dev/null +++ b/cios/sdhc/module.h @@ -0,0 +1,19 @@ +#ifndef _MODULE_H_ +#define _MODULE_H_ + +/* WBFS IOCTL base */ +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) + +/* IOCTL commands */ +#define IOCTL_SDHC_INIT 0x01 +#define IOCTL_SDHC_READ 0x02 +#define IOCTL_SDHC_WRITE 0x03 +#define IOCTL_SDHC_ISINSERTED 0x04 +#define IOCTL_WBFS_OPEN_DISC (WBFS_BASE + 0x1) +#define IOCTL_WBFS_READ_DISC (WBFS_BASE + 0x2) + +/* Device name */ +#define DEVICE_NAME "/dev/sdio/sdhc" + +#endif + diff --git a/cios/sdhc/sdio.c b/cios/sdhc/sdio.c new file mode 100644 index 0000000..f0cd253 --- /dev/null +++ b/cios/sdhc/sdio.c @@ -0,0 +1,704 @@ +/* + Hardware routines for reading and writing to the Wii's internal + SD slot. + + Copyright (c) 2008 + Michael Wiedenbauer (shagkur) + Dave Murphy (WinterMute) + Sven Peter + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "syscalls.h" +#include "timer.h" + + +/* Page size */ +#define PAGE_SIZE512 512 + +/* Commands */ +#define SDIOHCR_RESPONSE 0x10 +#define SDIOHCR_HOSTCONTROL 0x28 +#define SDIOHCR_POWERCONTROL 0x29 +#define SDIOHCR_CLOCKCONTROL 0x2c +#define SDIOHCR_TIMEOUTCONTROL 0x2e +#define SDIOHCR_SOFTWARERESET 0x2f +#define SDIOHCR_HOSTCONTROL_4BIT 0x02 + +#define SDIO_DEFAULT_TIMEOUT 0xe + +#define IOCTL_SDIO_WRITEHCREG 0x01 +#define IOCTL_SDIO_READHCREG 0x02 +#define IOCTL_SDIO_READCREG 0x03 +#define IOCTL_SDIO_RESETCARD 0x04 +#define IOCTL_SDIO_WRITECREG 0x05 +#define IOCTL_SDIO_SETCLK 0x06 +#define IOCTL_SDIO_SENDCMD 0x07 +#define IOCTL_SDIO_SETBUSWIDTH 0x08 +#define IOCTL_SDIO_READMCREG 0x09 +#define IOCTL_SDIO_WRITEMCREG 0x0A +#define IOCTL_SDIO_GETSTATUS 0x0B +#define IOCTL_SDIO_GETOCR 0x0C +#define IOCTL_SDIO_READDATA 0x0D +#define IOCTL_SDIO_WRITEDATA 0x0E + +#define SDIOCMD_TYPE_BC 1 +#define SDIOCMD_TYPE_BCR 2 +#define SDIOCMD_TYPE_AC 3 +#define SDIOCMD_TYPE_ADTC 4 + +#define SDIO_RESPONSE_NONE 0 +#define SDIO_RESPONSE_R1 1 +#define SDIO_RESPONSE_R1B 2 +#define SDIO_RESPOSNE_R2 3 +#define SDIO_RESPONSE_R3 4 +#define SDIO_RESPONSE_R4 5 +#define SDIO_RESPONSE_R5 6 +#define SDIO_RESPONSE_R6 7 + +#define SDIO_CMD_GOIDLE 0x00 +#define SDIO_CMD_ALL_SENDCID 0x02 +#define SDIO_CMD_SENDRCA 0x03 +#define SDIO_CMD_SELECT 0x07 +#define SDIO_CMD_DESELECT 0x07 +#define SDIO_CMD_SENDIFCOND 0x08 +#define SDIO_CMD_SENDCSD 0x09 +#define SDIO_CMD_SENDCID 0x0A +#define SDIO_CMD_SENDSTATUS 0x0D +#define SDIO_CMD_SETBLOCKLEN 0x10 +#define SDIO_CMD_READBLOCK 0x11 +#define SDIO_CMD_READMULTIBLOCK 0x12 +#define SDIO_CMD_WRITEBLOCK 0x18 +#define SDIO_CMD_WRITEMULTIBLOCK 0x19 +#define SDIO_CMD_APPCMD 0x37 + +#define SDIO_ACMD_SETBUSWIDTH 0x06 +#define SDIO_ACMD_SENDSCR 0x33 +#define SDIO_ACMD_SENDOPCOND 0x29 + +#define SDIO_STATUS_CARD_INSERTED 0x1 +#define SDIO_STATUS_CARD_INITIALIZED 0x10000 +#define SDIO_STATUS_CARD_SDHC 0x100000 + +/* SDIO structures */ +struct _sdiorequest +{ + u32 cmd; + u32 cmd_type; + u32 rsp_type; + u32 arg; + u32 blk_cnt; + u32 blk_size; + void *dma_addr; + u32 isdma; + u32 pad0; +}; + +struct _sdioresponse +{ + u32 rsp_fields[3]; + u32 acmd12_response; +}; + +/* Variables */ +static s32 __sd0_fd = -1; +static u16 __sd0_rca = 0; +static s32 __sd0_initialized = 0; +static s32 __sd0_sdhc = 0; +static u8 __sd0_cid[16]; +static s32 __sdio_initialized = 0; + +/* Device */ +static char _sd0_fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/slot0"; + +/* Buffers */ +static struct _sdiorequest __request ATTRIBUTE_ALIGN(32); +static struct _sdioresponse __response ATTRIBUTE_ALIGN(32); +static ioctlv __iovec[3] ATTRIBUTE_ALIGN(32); +static u8 __buffer1[32] ATTRIBUTE_ALIGN(32); +static u8 __buffer2[32] ATTRIBUTE_ALIGN(32); + + +static s32 __sdio_sendcommand(u32 cmd, u32 cmd_type, u32 rsp_type, u32 arg, u32 blk_cnt, u32 blk_size, void *buffer, void *reply, u32 rlen) +{ + ioctlv *iovec = __iovec; + struct _sdiorequest *request = &__request; + struct _sdioresponse *response = &__response; + + s32 ret; + + /* Prepare request */ + request->cmd = cmd; + request->cmd_type = cmd_type; + request->rsp_type = rsp_type; + request->arg = arg; + request->blk_cnt = blk_cnt; + request->blk_size = blk_size; + request->dma_addr = buffer; + request->isdma = ((buffer!=NULL)?1:0); + request->pad0 = 0; + + /* Flush cache */ + os_sync_after_write(request, sizeof(struct _sdiorequest)); + os_sync_after_write(response, sizeof(struct _sdioresponse)); + + if (buffer) + os_sync_after_write(buffer, blk_size * blk_cnt); + + /* DMA request */ + if(request->isdma || __sd0_sdhc == 1) { + /* Prepare vector */ + iovec[0].data = request; + iovec[0].len = sizeof(struct _sdiorequest); + iovec[1].data = buffer; + iovec[1].len = (blk_size*blk_cnt); + iovec[2].data = response; + iovec[2].len = sizeof(struct _sdioresponse); + + os_sync_after_write(iovec, sizeof(ioctlv) * 3); + + /* Send command */ + ret = os_ioctlv(__sd0_fd, IOCTL_SDIO_SENDCMD, 2, 1, iovec); + }else + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_SENDCMD, request, sizeof(struct _sdiorequest), response, sizeof(struct _sdioresponse)); + + /* Invalidate cache */ + os_sync_before_read(response, sizeof(struct _sdioresponse)); + + if (buffer) + os_sync_before_read(buffer, blk_size * blk_cnt); + + /* Copy response */ + if (reply && (rlen <= 16)) + memcpy(reply, response, rlen); + + return ret; +} + +static s32 __sdio_setclock(u32 set) +{ + u32 *clock = (u32 *)__buffer1; + + *clock = set; + os_sync_after_write(clock, 4); + + /* Send command */ + return os_ioctl(__sd0_fd, IOCTL_SDIO_SETCLK, clock, sizeof(u32), NULL, 0); +} + +static s32 __sdio_getstatus(void) +{ + u32 *status = (u32 *)__buffer1; + s32 ret; + + os_sync_after_write(status, 4); + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_GETSTATUS, NULL, 0, status, sizeof(u32)); + if (ret < 0) + return ret; + + return *status; +} + +static s32 __sdio_resetcard(void) +{ + u32 *status = (u32 *)__buffer1; + s32 ret; + + os_sync_after_write(status, 4); + + __sd0_rca = 0; + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_RESETCARD, NULL, 0, status, sizeof(u32)); + if (ret < 0) + return ret; + + __sd0_rca = (u16)(*status >> 16); + + return (*status & 0xffff); +} + +static s32 __sdio_gethcr(u8 reg, u8 size, u32 *val) +{ + u32 *hcr_value = (u32 *)__buffer1; + u32 *hcr_query = (u32 *)__buffer2; + + s32 ret; + + if (!val) + return -1; + + *hcr_value = 0; + *val = 0; + + /* Setup query */ + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = 0; + hcr_query[5] = 0; + + os_sync_after_write(hcr_value, sizeof(u32)); + os_sync_after_write(hcr_query, sizeof(u32) * 6); + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_READHCREG, (void *)hcr_query, 24, hcr_value, sizeof(u32)); + + *val = *hcr_value; + + return ret; +} + +static s32 __sdio_sethcr(u8 reg, u8 size, u32 data) +{ + u32 *hcr_query = (u32 *)__buffer1; + + /* Setup query */ + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = data; + hcr_query[5] = 0; + + os_sync_after_write(hcr_query, sizeof(u32) * 6); + + /* Send command */ + return os_ioctl(__sd0_fd, IOCTL_SDIO_WRITEHCREG, (void *)hcr_query, 24, NULL, 0); +} + +static s32 __sdio_waithcr(u8 reg, u8 size, u8 unset, u32 mask) +{ + u32 val; + s32 ret; + s32 tries = 10; + + while(tries-- > 0) + { + ret = __sdio_gethcr(reg, size, &val); + if (ret < 0) + return ret; + + if ((unset && !(val & mask)) || (!unset && (val & mask))) + return 0; + + usleep(10000); + } + + return -1; +} + +static s32 __sdio_setbuswidth(u32 bus_width) +{ + s32 ret; + u32 hc_reg = 0; + + /* Get HCR */ + ret = __sdio_gethcr(SDIOHCR_HOSTCONTROL, 1, &hc_reg); + if (ret < 0) + return ret; + + hc_reg &= 0xff; + hc_reg &= ~SDIOHCR_HOSTCONTROL_4BIT; + if (bus_width == 4) + hc_reg |= SDIOHCR_HOSTCONTROL_4BIT; + + /* Set HCR */ + return __sdio_sethcr(SDIOHCR_HOSTCONTROL, 1, hc_reg); +} + +static s32 __sd0_getrca(void) +{ + s32 ret; + u32 rca; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_SENDRCA, 0, SDIO_RESPONSE_R5, 0, 0, 0, NULL, &rca, sizeof(rca)); + if (ret < 0) + return ret; + + /* Get RCA */ + __sd0_rca = (u16)(rca >> 16); + + return (rca & 0xffff); +} + +static s32 __sd0_select(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_SELECT, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1B, (__sd0_rca << 16), 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_deselect(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_DESELECT, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1B, 0, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_setblocklength(u32 blk_len) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_SETBLOCKLEN, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, blk_len, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_setbuswidth(u32 bus_width) +{ + u16 val; + s32 ret; + + /* Set value */ + val = (bus_width == 4) ? 0x0002 : 0x0000; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_APPCMD, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, (__sd0_rca << 16), 0, 0, NULL, NULL, 0); + if (ret < 0) + return ret; + + /* Send command */ + return __sdio_sendcommand(SDIO_ACMD_SETBUSWIDTH, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, val, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_getcid(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_ALL_SENDCID, 0, SDIO_RESPOSNE_R2, (__sd0_rca << 16), 0, 0, NULL, __sd0_cid, 16); +} + +static bool __sd0_initio(void) +{ + struct _sdioresponse resp; + + s32 ret; + s32 tries; + u32 status; + + /* Reset card */ + __sdio_resetcard(); + + /* Get card status */ + status = __sdio_getstatus(); + + /* Card not inserted */ + if (!(status & SDIO_STATUS_CARD_INSERTED)) + return false; + + /* Card not initialized */ + if (!(status & SDIO_STATUS_CARD_INITIALIZED)) { + /* Close device */ + os_close(__sd0_fd); + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + if (__sd0_fd < 0) + return false; + + /* Reset the host controller */ + if (__sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7) < 0) + goto fail; + if (__sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7) < 0) + goto fail; + + /* Initialize interrupts */ + __sdio_sethcr(0x34, 4, 0x13f00c3); + __sdio_sethcr(0x38, 4, 0x13f00c3); + + /* Set SDHC flag */ + __sd0_sdhc = 1; + + /* Enable power */ + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xe); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xf); + if (ret < 0) + goto fail; + + /* Enable internal clock, wait until it gets stable and enable sd clock */ + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x101); + if (ret < 0) + goto fail; + + ret = __sdio_waithcr(SDIOHCR_CLOCKCONTROL, 2, 0, 2); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x107); + if (ret < 0) + goto fail; + + /* Setup timeout */ + ret = __sdio_sethcr(SDIOHCR_TIMEOUTCONTROL, 1, SDIO_DEFAULT_TIMEOUT); + if (ret < 0) + goto fail; + + /* Standard SDHC initialization process */ + ret = __sdio_sendcommand(SDIO_CMD_GOIDLE, 0, 0, 0, 0, 0, NULL, NULL, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sendcommand(SDIO_CMD_SENDIFCOND, 0, SDIO_RESPONSE_R6, 0x1aa, 0, 0, NULL, &resp, sizeof(resp)); + if (ret < 0) + goto fail; + + if ((resp.rsp_fields[0] & 0xff) != 0xaa) + goto fail; + + /* Do tries */ + for (tries = 10; tries; tries--) { + ret = __sdio_sendcommand(SDIO_CMD_APPCMD, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, 0, 0, 0, NULL, NULL, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sendcommand(SDIO_ACMD_SENDOPCOND, 0, SDIO_RESPONSE_R3, 0x40300000, 0, 0, NULL, &resp, sizeof(resp)); + if (ret < 0) + goto fail; + + if (resp.rsp_fields[0] & (1 << 31)) + break; + + usleep(10000); + } + + if (tries < 0) + goto fail; + + if (resp.rsp_fields[0] & (1 << 30)) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + /* Get CID */ + ret = __sd0_getcid(); + if (ret < 0) + goto fail; + + /* Get RCA */ + ret = __sd0_getrca(); + if (ret < 0) + goto fail; + } + else if (status & SDIO_STATUS_CARD_SDHC) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + /* Set bus bandwidth */ + ret = __sdio_setbuswidth(4); + if (ret < 0) + return false; + + /* Set clock */ + ret = __sdio_setclock(1); + if (ret < 0) + return false; + + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + return false; + + /* Set block length */ + ret = __sd0_setblocklength(PAGE_SIZE512); + if (ret < 0) { + ret = __sd0_deselect(); + return false; + } + + /* Set bus bandwidth */ + ret = __sd0_setbuswidth(4); + if (ret < 0) { + ret = __sd0_deselect(); + return false; + } + + /* Deselect card */ + __sd0_deselect(); + + /* Set initialized */ + __sd0_initialized = 1; + + return true; + +fail: + /* Software reset */ + __sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7); + __sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7); + + /* Close device */ + os_close(__sd0_fd); + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + + return false; +} + +bool sdio_Deinitialize(void) +{ + /* Close device */ + if (__sd0_fd >= 0) + os_close(__sd0_fd); + + /* Reset variables */ + __sd0_fd = -1; + __sdio_initialized = 0; + + return true; +} + +bool sdio_Startup(void) +{ + /* Already initialized */ + if (__sdio_initialized) + return true; + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + + if (__sd0_fd < 0) { + sdio_Deinitialize(); + return false; + } + + if (!__sd0_initio()) { + sdio_Deinitialize(); + return false; + } + + /* Set initialized */ + __sdio_initialized = 1; + + return true; +} + +bool sdio_Shutdown(void) +{ + /* Already deinitialized */ + if (!__sd0_initialized) + return false; + + /* Deinitialize */ + sdio_Deinitialize(); + + /* Set deinitialized */ + __sd0_initialized = 0; + + return true; +} + +bool sdio_ReadSectors(sec_t sector, sec_t numSectors,void* buffer) +{ + u32 i; + s32 ret; + + /* No buffer */ + if (!buffer) + return false; + + /* Read loop */ + for (i = 0; i < 10; i++) { + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + continue; + + /* Check if SDHC */ + if (!__sd0_sdhc) + sector *= PAGE_SIZE512; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, sector, numSectors, PAGE_SIZE512, buffer, NULL, 0); + + /* Deselect card */ + __sd0_deselect(); + + if (ret >= 0) + return true; + } + + return false; +} + +bool sdio_WriteSectors(sec_t sector, sec_t numSectors,const void* buffer) +{ + u32 i; + s32 ret; + + /* No buffer */ + if (!buffer) + return false; + + /* Write loop */ + for (i = 0; i < 10; i++) { + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + continue; + + /* Check if SDHC */ + if (!__sd0_sdhc) + sector *= PAGE_SIZE512; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, sector, numSectors, PAGE_SIZE512, (char *)buffer, NULL, 0); + + /* Deselect card */ + __sd0_deselect(); + + if (ret >= 0) + return true; + } + + return false; +} + +bool sdio_ClearStatus(void) +{ + return true; +} + +bool sdio_IsInserted(void) +{ + /* Check if card is inserted */ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INSERTED) == + SDIO_STATUS_CARD_INSERTED); +} + +bool sdio_IsInitialized(void) +{ + /* Check if card is initialized */ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INITIALIZED) == + SDIO_STATUS_CARD_INITIALIZED); +} diff --git a/cios/sdhc/sdio.h b/cios/sdhc/sdio.h new file mode 100644 index 0000000..fab61d9 --- /dev/null +++ b/cios/sdhc/sdio.h @@ -0,0 +1,13 @@ +#ifndef _SDIO_H_ +#define _SDIO_H_ + +#include "types.h" + +/* Prototypes */ +bool sdio_Startup(void); +bool sdio_Shutdown(void); +bool sdio_ReadSectors(sec_t sector, sec_t numSectors, void *buffer); +bool sdio_WriteSectors(sec_t sector, sec_t numSectors, const void *buffer); +bool sdio_IsInserted(void); + +#endif diff --git a/cios/sdhc/start.s b/cios/sdhc/start.s new file mode 100644 index 0000000..f4307af --- /dev/null +++ b/cios/sdhc/start.s @@ -0,0 +1,74 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .arm + + .EQU ios_thread_arg, 4 + .EQU ios_thread_priority, 0x48 + .EQU ios_thread_stacksize, 0x2000 + + + .global _start +_start: + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bx r3 + + + +/* + * IOS bss + */ + .section ".ios_bss", "a", %nobits + + .space ios_thread_stacksize + .global ios_thread_stack /* stack decrements from high address.. */ +ios_thread_stack: + + +/* + * IOS info table + */ + .section ".ios_info_table", "ax", %progbits + + .global ios_info_table +ios_info_table: + .long 0x0 + .long 0x28 @ numentries * 0x28 + .long 0x6 + + .long 0xB + .long ios_thread_arg @ passed to thread entry func, maybe module id + + .long 0x9 + .long _start + + .long 0x7D + .long ios_thread_priority + + .long 0x7E + .long ios_thread_stacksize + + .long 0x7F + .long ios_thread_stack diff --git a/cios/sdhc/syscalls.h b/cios/sdhc/syscalls.h new file mode 100644 index 0000000..03721d8 --- /dev/null +++ b/cios/sdhc/syscalls.h @@ -0,0 +1,72 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "ipc.h" +#include "types.h" + +/* IOS syscalls */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +s32 os_open(const char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, ioctlv *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 dummy, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* ARM syscalls */ +void write(const char *str); + +#endif diff --git a/cios/sdhc/syscalls.s b/cios/sdhc/syscalls.s new file mode 100644 index 0000000..9ec8e97 --- /dev/null +++ b/cios/sdhc/syscalls.s @@ -0,0 +1,284 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +.macro syscall vec_sys + .long 0xE6000010 + (\vec_sys<<5) + bx lr +.endm + + +/* + * IOS syscalls + */ + .code 32 + .global os_thread_create +os_thread_create: + syscall 0x0 + + .code 32 + .global os_thread_joint +os_thread_joint: + syscall 0x1 + + .code 32 + .global os_thread_cancel +os_thread_cancel: + syscall 0x2 + + .code 32 + .global os_get_thread_id +os_get_thread_id: + syscall 0x3 + + .code 32 + .global os_get_parent_thread_id +os_get_parent_thread_id: + syscall 0x4 + + .code 32 + .global os_thread_continue +os_thread_continue: + syscall 0x5 + + .code 32 + .global os_thread_stop +os_thread_stop: + syscall 0x6 + + .code 32 + .global os_thread_yield +os_thread_yiel: + syscall 0x7 + + .code 32 + .global os_thread_get_priority +os_thread_get_priority : + syscall 0x8 + + .code 32 + .global os_thread_set_priority +os_thread_set_priority: + syscall 0x9 + + .code 32 + .global os_message_queue_create +os_message_queue_create: + syscall 0xa + + .code 32 + .global os_message_queue_destroy +os_message_queue_destroy: + syscall 0xb + + .code 32 + .global os_message_queue_send +os_message_queue_send: + syscall 0xc + + .code 32 + .global os_message_queue_send_now +os_message_queue_send_now: + syscall 0xd + + .code 32 + .global os_message_queue_receive +os_message_queue_receive: + syscall 0xe + + .code 32 + .global os_register_event_handler +os_register_event_handler: + syscall 0xf + + .code 32 + .global os_unregister_event_handler +os_unregister_event_handler: + syscall 0x10 + + .code 32 + .global os_create_timer +os_create_timer: + syscall 0x11 + + .code 32 + .global os_restart_timer +os_restart_timer: + syscall 0x12 + + .code 32 + .global os_stop_timer +os_stop_timer: + syscall 0x13 + + .code 32 + .global os_destroy_timer +os_destroy_timer: + syscall 0x14 + + .code 32 + .global os_timer_now +os_timer_now: + syscall 0x15 + + .code 32 + .global os_heap_create +os_heap_create: + syscall 0x16 + + .code 32 + .global os_heap_destroy +os_heap_destroy: + syscall 0x17 + + .code 32 + .global os_heap_alloc +os_heap_alloc: + syscall 0x18 + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_device_register +os_device_register: + syscall 0x1b + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + .code 32 + .global os_ioctl +os_ioctl: + syscall 0x21 + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global os_open_async +os_open_async: + syscall 0x23 + + .code 32 + .global os_close_async +os_close_async: + syscall 0x24 + + .code 32 + .global os_read_async +os_read_async: + syscall 0x25 + + .code 32 + .global os_write_async +os_write_async: + syscall 0x26 + + .code 32 + .global os_seek_async +os_seek_async: + syscall 0x27 + + .code 32 + .global os_ioctl_async +os_ioctl_async: + syscall 0x28 + + .code 32 + .global os_ioctlv_async +os_ioctlv_async: + syscall 0x29 + + .code 32 + .global os_message_queue_ack +os_message_queue_ack: + syscall 0x2a + + .code 32 + .global os_software_IRQ +os_software_IRQ: + syscall 0x34 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3f + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_virt_to_phys +os_virt_to_phys: + syscall 0x4f + + .code 32 + .global os_syscall_50 +os_syscall_50: + syscall 0x50 + + +/* + * ARM syscalls + */ + .code 32 + .global write +write: + mov r2, lr + adds r1, r0, #0 + movs r0, #4 + svc 0xab + bx r2 diff --git a/cios/sdhc/timer.c b/cios/sdhc/timer.c new file mode 100644 index 0000000..ba5bcee --- /dev/null +++ b/cios/sdhc/timer.c @@ -0,0 +1,69 @@ +/* + Custom IOS Module (MLOAD) + + Copyright (C) 2008 neimod. + Copyright (C) 2010 Hermes. + Copyright (C) 2010 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mem.h" +#include "syscalls.h" +#include "types.h" + +/* Variables */ +static s32 queuehandle = -1; +static s32 timerId = -1; + + +void Timer_Init(void) +{ + void *queuespace = NULL; + + /* Queue already created */ + if (queuehandle >= 0) + return; + + /* Create queue */ + queuespace = Mem_Alloc(0x40); + queuehandle = os_message_queue_create(queuespace, 16); + + /* Create timer */ + timerId = os_create_timer(0, 0, queuehandle, 0x666); + + /* Stop timer */ + os_stop_timer(timerId); +} + +void Timer_Sleep(u32 time) +{ + u32 message; + + /* Restart timer */ + os_restart_timer(timerId, 0, time); + + while (1) { + /* Wait to receive message */ + os_message_queue_receive(queuehandle, (void *)&message, 0); + + /* Message received */ + if (message == 0x666) + break; + } + + /* Stop timer */ + os_stop_timer(timerId); +} diff --git a/cios/sdhc/timer.h b/cios/sdhc/timer.h new file mode 100644 index 0000000..e690925 --- /dev/null +++ b/cios/sdhc/timer.h @@ -0,0 +1,13 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +/* Macros */ +#define udelay(t) Timer_Sleep(t) +#define usleep(t) Timer_Sleep(t) +#define msleep(t) Timer_Sleep(t*1000) + +/* Prototypes */ +void Timer_Init(void); +void Timer_Sleep(u32 time); + +#endif diff --git a/cios/sdhc/types.h b/cios/sdhc/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/sdhc/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/sdhc/wbfs.c b/cios/sdhc/wbfs.c new file mode 100644 index 0000000..948c470 --- /dev/null +++ b/cios/sdhc/wbfs.c @@ -0,0 +1,92 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "sdio.h" +#include "syscalls.h" +#include "libwbfs/libwbfs.h" + +/* Constants */ +#define SECTOR_SIZE 0x200 + +/* Variables */ +static wbfs_t *hdd = NULL; +static wbfs_disc_t *disc = NULL; + + +static int __WBFS_ReadSector(void *cbdata, u32 lba, u32 count, void *buffer) +{ + s32 ret; + + /* Read data */ + ret = sdio_ReadSectors(lba, count, buffer); + if (!ret) + return 1; + + /* Invalidate range */ + os_sync_before_read(buffer, SECTOR_SIZE * count); + + return 0; +} + + +s32 WBFS_OpenDisc(u8 *discid) +{ + s32 ret; + + /* Initialize SDIO */ + ret = sdio_Startup(); + if (!ret) + return 1; + + /* Close disc */ + if (disc) + wbfs_close_disc(disc); + + /* Close SD */ + if (hdd) + wbfs_close(hdd); + + /* Open SD */ + hdd = wbfs_open_hd(__WBFS_ReadSector, NULL, NULL, SECTOR_SIZE, 0, 0); + if (!hdd) + return 2; + + /* Open disc */ + disc = wbfs_open_disc(hdd, discid); + if (!disc) + return 3; + + return 0; +} + +s32 WBFS_Read(void *buffer, u32 len, u32 offset) +{ + /* No disc opened */ + if (!disc) + return 1; + + /* Disc read */ + return wbfs_disc_read(disc, offset, buffer, len); +} diff --git a/cios/sdhc/wbfs.h b/cios/sdhc/wbfs.h new file mode 100644 index 0000000..90bf0e2 --- /dev/null +++ b/cios/sdhc/wbfs.h @@ -0,0 +1,9 @@ +#ifndef _WBFS_H_ +#define _WBFS_H_ + +/* Prototypes */ +s32 WBFS_OpenDisc(u8 *discid); +s32 WBFS_Read(void *buffer, u32 len, u32 offset); + +#endif + diff --git a/cios/sdhc_orig/LICENSE.txt b/cios/sdhc_orig/LICENSE.txt new file mode 100644 index 0000000..7e14f38 --- /dev/null +++ b/cios/sdhc_orig/LICENSE.txt @@ -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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin 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. + + , 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. \ No newline at end of file diff --git a/cios/sdhc_orig/Makefile b/cios/sdhc_orig/Makefile new file mode 100644 index 0000000..385639c --- /dev/null +++ b/cios/sdhc_orig/Makefile @@ -0,0 +1,53 @@ +# devkitARM path +DEVKITARM ?= /opt/devkitARM + +# Prefix +PREFIX = $(DEVKITARM)/bin/arm-eabi- + +# Executables +CC = $(PREFIX)gcc +LD = $(PREFIX)gcc +STRIP = ./stripios + +# Flags +ARCH = -mcpu=arm926ej-s -mthumb -mthumb-interwork -mbig-endian +CFLAGS = $(ARCH) -I. -fomit-frame-pointer -Os -Wall -Wstrict-prototypes -ffunction-sections +LDFLAGS = $(ARCH) -nostartfiles -Wl,-T,link.ld,-Map,$(TARGET).map -Wl,--gc-sections -Wl,-static + +# Libraries +LIBS = + +# Target +TARGET = sdhc-module + +# Objects +OBJS = es.o \ + ipc.o \ + main.o \ + mem.o \ + sdio.o \ + start.o \ + syscalls.o \ + timer.o \ + wbfs.o \ + libwbfs/libwbfs.o \ + libwbfs/rijndael.o \ + libwbfs/wiidisc.o + + +$(TARGET).elf: $(OBJS) + @echo -e " LD\t$@" + @$(LD) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.orig + @$(STRIP) $@.orig $@ + +%.o: %.s + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o: %.c + @echo -e " CC\t$@" + @$(CC) $(CFLAGS) -c -o $@ $< + +clean: + @echo -e "Cleaning..." + @rm -f $(OBJS) $(TARGET).elf $(TARGET).elf.orig $(TARGET).map diff --git a/cios/sdhc_orig/es.c b/cios/sdhc_orig/es.c new file mode 100644 index 0000000..ebce4a4 --- /dev/null +++ b/cios/sdhc_orig/es.c @@ -0,0 +1,65 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "syscalls.h" +#include "types.h" + +/* IOCTL commands */ +#define IOCTL_ES_GETTITLEID 0x20 + +/* Variables */ +static ioctlv vector[8] ATTRIBUTE_ALIGN(32); + + +s32 __ES_Init(void) +{ + /* Open /dev/es */ + return os_open("/dev/es", 0); +} + +void __ES_Close(s32 fd) +{ + /* Close /dev/es */ + os_close(fd); +} + +s32 ES_GetTitleID(u64 *tid) +{ + s32 fd, ret; + + /* Open ES */ + fd = __ES_Init(); + if (fd < 0) + return fd; + + /* Setup vector */ + vector[0].data = tid; + vector[0].len = sizeof(u64); + + /* Get title ID */ + ret = os_ioctlv(fd, IOCTL_ES_GETTITLEID, 0, 1, vector); + + /* Close ES */ + __ES_Close(fd); + + return ret; +} diff --git a/cios/sdhc_orig/es.h b/cios/sdhc_orig/es.h new file mode 100644 index 0000000..d7b60b9 --- /dev/null +++ b/cios/sdhc_orig/es.h @@ -0,0 +1,10 @@ +#ifndef _ES_H_ +#define _ES_H_ + +#include "types.h" + + +/* Prototypes */ +s32 ES_GetTitleID(u64 *tid); + +#endif diff --git a/cios/sdhc_orig/ipc.c b/cios/sdhc_orig/ipc.c new file mode 100644 index 0000000..0033265 --- /dev/null +++ b/cios/sdhc_orig/ipc.c @@ -0,0 +1,52 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ipc.h" +#include "syscalls.h" +#include "types.h" + + +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = 0; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Invalidate cache */ + os_sync_before_read(buffer, len); + } +} + +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen) +{ + u32 cnt; + + for (cnt = inlen; cnt < (inlen + iolen); cnt++) { + void *buffer = vector[cnt].data; + u32 len = vector[cnt].len; + + /* Flush cache */ + os_sync_after_write(buffer, len); + } +} diff --git a/cios/sdhc_orig/ipc.h b/cios/sdhc_orig/ipc.h new file mode 100644 index 0000000..ec5e1ab --- /dev/null +++ b/cios/sdhc_orig/ipc.h @@ -0,0 +1,81 @@ +#ifndef _IPC_H_ +#define _IPC_H_ + +#include "types.h" + +/* IPC error codes */ +#define IPC_ENOENT -6 +#define IPC_ENOMEM -22 +#define IPC_EINVAL -101 +#define IPC_EACCESS -102 +#define IPC_EEXIST -105 +#define IPC_NOENT -106 + +/* IOS calls */ +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +/* IOCTL vector */ +typedef struct iovec { + void *data; + u32 len; +} ioctlv; + +/* IPC message */ +typedef struct ipcmessage { + u32 command; + u32 result; + u32 fd; + + union + { + struct + { + char *device; + u32 mode; + u32 resultfd; + } open; + + struct + { + void *data; + u32 length; + } read, write; + + struct + { + s32 offset; + s32 origin; + } seek; + + struct + { + u32 command; + + u32 *buffer_in; + u32 length_in; + u32 *buffer_io; + u32 length_io; + } ioctl; + struct + { + u32 command; + + u32 num_in; + u32 num_io; + ioctlv *vector; + } ioctlv; + }; +} ATTRIBUTE_PACKED ipcmessage; + + +/* Prototypes */ +void InvalidateVector(ioctlv *vector, u32 inlen, u32 iolen); +void FlushVector(ioctlv *vector, u32 inlen, u32 iolen); + +#endif diff --git a/cios/sdhc_orig/libwbfs/libwbfs.c b/cios/sdhc_orig/libwbfs/libwbfs.c new file mode 100644 index 0000000..2faf17e --- /dev/null +++ b/cios/sdhc_orig/libwbfs/libwbfs.c @@ -0,0 +1,541 @@ +// Copyright 2009 Kwiirk +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + + +#include "libwbfs.h" + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define ERROR(x) do {wbfs_error(x);goto error;}while(0) +#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) +static int force_mode=0; +void wbfs_set_force_mode(int force) +{ + force_mode = force; +} +static u8 size_to_shift(u32 size) +{ + u8 ret = 0; + while(size) + { + ret++; + size>>=1; + } + return ret-1; +} +#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24)) + + +wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector __attribute((unused)), int reset) +{ + int i=num_hd_sector,ret; + u8 *ptr,*tmp_buffer = wbfs_ioalloc(hd_sector_size); + u8 part_table[16*4]; + ret = read_hdsector(callback_data,0,1,tmp_buffer); + if(ret) + return 0; + //find wbfs partition + wbfs_memcpy(part_table,tmp_buffer+0x1be,16*4); + ptr = part_table; + for(i=0;i<4;i++,ptr+=16) + { + u32 part_lba = read_le32_unaligned(ptr+0x8); + wbfs_head_t *head = (wbfs_head_t *)tmp_buffer; + ret = read_hdsector(callback_data,part_lba,1,tmp_buffer); + // verify there is the magic. + if (head->magic == wbfs_htonl(WBFS_MAGIC)) + { + wbfs_t*p = wbfs_open_partition(read_hdsector,write_hdsector, + callback_data,hd_sector_size,0,part_lba,reset); + wbfs_iofree(tmp_buffer); + return p; + } + } + if(reset)// XXX make a empty hd partition.. + { + } + return 0; +} +wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, u32 part_lba, int reset) +{ + wbfs_t *p = wbfs_malloc(sizeof(wbfs_t)); + + wbfs_head_t *head = wbfs_ioalloc(hd_sector_size?hd_sector_size:512); + + //constants, but put here for consistancy + p->wii_sec_sz = 0x8000; + p->wii_sec_sz_s = size_to_shift(0x8000); + p->n_wii_sec = (num_hd_sector/0x8000)*hd_sector_size; + p->n_wii_sec_per_disc = 143432*2;//support for double layers discs.. + p->head = head; + p->part_lba = part_lba; + // init the partition + if (reset) + { + u8 sz_s; + wbfs_memset(head,0,hd_sector_size); + head->magic = wbfs_htonl(WBFS_MAGIC); + head->hd_sec_sz_s = size_to_shift(hd_sector_size); + head->n_hd_sec = wbfs_htonl(num_hd_sector); + // choose minimum wblk_sz that fits this partition size + for(sz_s=6;sz_s<11;sz_s++) + { + // ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits + if(p->n_wii_sec <((1U<<16)*(1<wbfs_sec_sz_s = sz_s+p->wii_sec_sz_s; + }else + read_hdsector(callback_data,p->part_lba,1,head); + if (head->magic != wbfs_htonl(WBFS_MAGIC)) + ERROR("bad magic"); + if(!force_mode && hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) + ERROR("hd sector size doesn't match"); + if(!force_mode && num_hd_sector && head->n_hd_sec != wbfs_htonl(num_hd_sector)) + ERROR("hd num sector doesn't match"); + p->hd_sec_sz = 1<hd_sec_sz_s; + p->hd_sec_sz_s = head->hd_sec_sz_s; + p->n_hd_sec = wbfs_ntohl(head->n_hd_sec); + + p->n_wii_sec = (p->n_hd_sec/p->wii_sec_sz)*(p->hd_sec_sz); + + p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; + p->wbfs_sec_sz = 1<wbfs_sec_sz_s; + p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->disc_info_sz = ALIGN_LBA(sizeof(wbfs_disc_info_t) + p->n_wbfs_sec_per_disc*2); + + //printf("hd_sector_size %X wii_sector size %X wbfs sector_size %X\n",p->hd_sec_sz,p->wii_sec_sz,p->wbfs_sec_sz); + p->read_hdsector = read_hdsector; + p->write_hdsector = write_hdsector; + p->callback_data = callback_data; + + p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec/8)>>p->hd_sec_sz_s; + + if(!reset) + p->freeblks = 0; // will alloc and read only if needed + else + { + // init with all free blocks + p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8)); + wbfs_memset(p->freeblks,0xff,p->n_wbfs_sec/8); + } + p->max_disc = (p->freeblks_lba-1)/(p->disc_info_sz>>p->hd_sec_sz_s); + if(p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t)) + p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t); + + p->tmp_buffer = wbfs_ioalloc(p->hd_sec_sz); + p->n_disc_open = 0; + return p; +error: + wbfs_free(p); + wbfs_iofree(head); + return 0; + +} + +void wbfs_sync(wbfs_t*p) +{ + // copy back descriptors + if(p->write_hdsector){ + p->write_hdsector(p->callback_data,p->part_lba+0,1, p->head); + + if(p->freeblks) + p->write_hdsector(p->callback_data,p->part_lba+p->freeblks_lba,ALIGN_LBA(p->n_wbfs_sec/8)>>p->hd_sec_sz_s, p->freeblks); + } +} +void wbfs_close(wbfs_t*p) +{ + wbfs_sync(p); + + if(p->n_disc_open) + ERROR("trying to close wbfs while discs still open"); + + wbfs_iofree(p->head); + wbfs_iofree(p->tmp_buffer); + if(p->freeblks) + wbfs_iofree(p->freeblks); + + wbfs_free(p); + +error: + return; +} + +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *discid) +{ + u32 i; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + wbfs_disc_t *d = 0; + for(i=0;imax_disc;i++) + { + if (p->head->disc_table[i]){ + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer); + if(wbfs_memcmp(discid,p->tmp_buffer,6)==0){ + d = wbfs_malloc(sizeof(*d)); + if(!d) + ERROR("allocating memory"); + d->p = p; + d->i = i; + d->header = wbfs_ioalloc(p->disc_info_sz); + if(!d->header) + ERROR("allocating memory"); + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba, + disc_info_sz_lba,d->header); + p->n_disc_open ++; +// for(i=0;in_wbfs_sec_per_disc;i++) +// printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); + return d; + } + } + } + return 0; +error: + if(d) + wbfs_iofree(d); + return 0; + +} +void wbfs_close_disc(wbfs_disc_t*d) +{ + d->p->n_disc_open --; + wbfs_iofree(d->header); + wbfs_free(d); +} +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len) +{ + + wbfs_t *p = d->p; + u16 wlba = offset>>(p->wbfs_sec_sz_s-2); + u32 iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; + u32 lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s); + u32 lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask; + u32 off = offset&((p->hd_sec_sz>>2)-1); + u16 iwlba = wbfs_ntohs(d->header->wlba_table[wlba]); + u32 len_copied; + int err = 0; + u8 *ptr = data; + if(unlikely(iwlba==0)) + return 1; + if(unlikely(off)){ + off*=4; + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<tmp_buffer); + if(err) + return err; + len_copied = p->hd_sec_sz - off; + if(likely(len < len_copied)) + len_copied = len; + wbfs_memcpy(ptr, p->tmp_buffer + off, len_copied); + len -= len_copied; + ptr += len_copied; + lba++; + if(unlikely(lba>lba_mask && len)){ + lba=0; + iwlba = wbfs_ntohs(d->header->wlba_table[++wlba]); + if(unlikely(iwlba==0)) + return 1; + } + } + while(likely(len>=p->hd_sec_sz)) + { + u32 nlb = len>>(p->hd_sec_sz_s); + + if(unlikely(lba + nlb > p->wbfs_sec_sz)) // dont cross wbfs sectors.. + nlb = p->wbfs_sec_sz-lba; + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<hd_sec_sz_s; + ptr += nlb<hd_sec_sz_s; + lba += nlb; + if(unlikely(lba>lba_mask && len)){ + lba = 0; + iwlba =wbfs_ntohs(d->header->wlba_table[++wlba]); + if(unlikely(iwlba==0)) + return 1; + } + } + if(unlikely(len)){ + err = p->read_hdsector(p->callback_data, + p->part_lba + (iwlba<tmp_buffer); + if(err) + return err; + wbfs_memcpy(ptr, p->tmp_buffer, len); + } + return 0; +} + +// disc listing +u32 wbfs_count_discs(wbfs_t*p) +{ + u32 i,count=0; + for(i=0;imax_disc;i++) + if (p->head->disc_table[i]) + count++; + return count; + +} +static u32 wbfs_sector_used(wbfs_t *p,wbfs_disc_info_t *di) +{ + u32 tot_blk=0,j; + for(j=0;jn_wbfs_sec_per_disc;j++) + if(wbfs_ntohs(di->wlba_table[j])) + tot_blk++; + return tot_blk; + +} +u32 wbfs_get_disc_info(wbfs_t*p, u32 index,u8 *header,int header_size,u32 *size)//size in 32 bit +{ + u32 i,count=0; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + for(i=0;imax_disc;i++) + if (p->head->disc_table[i]){ + if(count++==index) + { + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer); + if(header_size > (int)p->hd_sec_sz) + header_size = p->hd_sec_sz; + u32 magic = wbfs_ntohl(*(u32*)(p->tmp_buffer+24)); + if(magic!=0x5D1C9EA3){ + p->head->disc_table[i]=0; + return 1; + } + memcpy(header,p->tmp_buffer,header_size); + if(size) + { + u8 *header = wbfs_ioalloc(p->disc_info_sz); + p->read_hdsector(p->callback_data, + p->part_lba+1+i*disc_info_sz_lba,disc_info_sz_lba,header); + u32 sec_used = wbfs_sector_used(p,(wbfs_disc_info_t *)header); + wbfs_iofree(header); + *size = sec_used<<(p->wbfs_sec_sz_s-2); + } + return 0; + } + } + return 1; +} + +static void load_freeblocks(wbfs_t*p) +{ + if(p->freeblks) + return; + // XXX should handle malloc error.. + p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8)); + p->read_hdsector(p->callback_data,p->part_lba+p->freeblks_lba,ALIGN_LBA(p->n_wbfs_sec/8)>>p->hd_sec_sz_s, p->freeblks); + +} +u32 wbfs_count_usedblocks(wbfs_t*p) +{ + u32 i,j,count=0; + load_freeblocks(p); + for(i=0;in_wbfs_sec/(8*4);i++) + { + u32 v = wbfs_ntohl(p->freeblks[i]); + if(v == ~0U) + count+=32; + else if(v!=0) + for(j=0;j<32;j++) + if (v & (1<n_wbfs_sec/(8*4);i++) + { + u32 v = wbfs_ntohl(p->freeblks[i]); + if(v != 0) + { + for(j=0;j<32;j++) + if (v & (1<freeblks[i] = wbfs_htonl(v & ~(1<freeblks[i]); + p->freeblks[i] = wbfs_htonl(v | 1<wbfs_sec_sz_s-p->wii_sec_sz_s); + wiidisc_t *d = 0; + u8 *used = 0; + wbfs_disc_info_t *info = 0; + u8* copy_buffer = 0; + used = wbfs_malloc(p->n_wii_sec_per_disc); + if(!used) + ERROR("unable to alloc memory"); + if(!copy_1_1) + { + d = wd_open_disc(read_src_wii_disc,callback_data); + if(!d) + ERROR("unable to open wii disc"); + wd_build_disc_usage(d,sel,used); + wd_close_disc(d); + d = 0; + } + + + for(i=0;imax_disc;i++)// find a free slot. + if(p->head->disc_table[i]==0) + break; + if(i==p->max_disc) + ERROR("no space left on device (table full)"); + p->head->disc_table[i] = 1; + discn = i; + load_freeblocks(p); + + // build disc info + info = wbfs_ioalloc(p->disc_info_sz); + u8*b = (u8*)info; + read_src_wii_disc(callback_data,0,0x100,info->disc_header_copy); + fprintf(stderr, "adding %c%c%c%c%c%c %s...\n",b[0], b[1], b[2], b[3], b[4], b[5], b + 0x20); + + copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz); + if(!copy_buffer) + ERROR("alloc memory"); + tot=0; + cur=0; + if(spinner){ + // count total number to write for spinner + for(i=0; in_wbfs_sec_per_disc;i++) + if(copy_1_1 || block_used(used,i,wii_sec_per_wbfs_sect)) tot++; + spinner(0,tot); + } + for(i=0; in_wbfs_sec_per_disc;i++){ + u16 bl = 0; + if(copy_1_1 || block_used(used,i,wii_sec_per_wbfs_sect)) { + bl = alloc_block(p); + if (bl==0xffff) + ERROR("no space left on device (disc full)"); + read_src_wii_disc(callback_data,i*(p->wbfs_sec_sz>>2),p->wbfs_sec_sz,copy_buffer); + + //fix the partition table. + if(i==(0x40000>>p->wbfs_sec_sz_s)) + wd_fix_partition_table(d, sel, copy_buffer+(0x40000&(p->wbfs_sec_sz-1))); + + p->write_hdsector(p->callback_data,p->part_lba+bl*(p->wbfs_sec_sz/p->hd_sec_sz), + p->wbfs_sec_sz/p->hd_sec_sz,copy_buffer); + cur++; + if(spinner) + spinner(cur,tot); + } + info->wlba_table[i] = wbfs_htons(bl); + } + // write disc info + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + p->write_hdsector(p->callback_data,p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,info); + wbfs_sync(p); +error: + if(d) + wd_close_disc(d); + if(used) + wbfs_free(used); + if(info) + wbfs_iofree(info); + if(copy_buffer) + wbfs_iofree(copy_buffer); + // init with all free blocks + + return 0; +} +u32 wbfs_rm_disc(wbfs_t*p, u8* discid) +{ + wbfs_disc_t *d = wbfs_open_disc(p,discid); + int i; + int discn = 0; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + if(!d) + return 1; + load_freeblocks(p); + discn = d->i; + for( i=0; i< p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]); + if (iwlba) + free_block(p,iwlba); + } + memset(d->header,0,p->disc_info_sz); + p->write_hdsector(p->callback_data,p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,d->header); + p->head->disc_table[discn] = 0; + wbfs_close_disc(d); + wbfs_sync(p); + return 0; +} + +// trim the file-system to its minimum size +u32 wbfs_trim(wbfs_t*p); + +// data extraction +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner) +{ + wbfs_t *p = d->p; + u8* copy_buffer = 0; + int i; + int src_wbs_nlb=p->wbfs_sec_sz/p->hd_sec_sz; + int dst_wbs_nlb=p->wbfs_sec_sz/p->wii_sec_sz; + copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz); + if(!copy_buffer) + ERROR("alloc memory"); + + for( i=0; i< p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]); + if (iwlba) + { + + if(spinner) + spinner(i,p->n_wbfs_sec_per_disc); + p->read_hdsector(p->callback_data, p->part_lba + iwlba*src_wbs_nlb, src_wbs_nlb, copy_buffer); + write_dst_wii_sector(callback_data, i*dst_wbs_nlb, dst_wbs_nlb, copy_buffer); + } + } + wbfs_iofree(copy_buffer); + return 0; +error: + return 1; +} +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); diff --git a/cios/sdhc_orig/libwbfs/libwbfs.h b/cios/sdhc_orig/libwbfs/libwbfs.h new file mode 100644 index 0000000..a1f3441 --- /dev/null +++ b/cios/sdhc_orig/libwbfs/libwbfs.h @@ -0,0 +1,215 @@ +#ifndef LIBWBFS_H +#define LIBWBFS_H + +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs +#include "wiidisc.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef u32 be32_t; +typedef u16 be16_t; + + + +typedef struct wbfs_head +{ + be32_t magic; + // parameters copied in the partition for easy dumping, and bug reports + be32_t n_hd_sec; // total number of hd_sec in this partition + u8 hd_sec_sz_s; // sector size in this partition + u8 wbfs_sec_sz_s; // size of a wbfs sec + u8 padding3[2]; + u8 disc_table[0]; // size depends on hd sector size +}__attribute((packed)) wbfs_head_t ; + +typedef struct wbfs_disc_info +{ + u8 disc_header_copy[0x100]; + be16_t wlba_table[0]; +}wbfs_disc_info_t; + +// WBFS first wbfs_sector structure: +// +// ----------- +// | wbfs_head | (hd_sec_sz) +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | ... | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// |freeblk_tbl| +// | | +// ----------- +// + +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +typedef int (*rw_sector_callback_t)(void*fp,u32 lba,u32 count,void*iobuf); +typedef void (*progress_callback_t)(int status,int total); + + +typedef struct wbfs_s +{ + wbfs_head_t *head; + + /* hdsectors, the size of the sector provided by the hosting hard drive */ + u32 hd_sec_sz; + u8 hd_sec_sz_s; // the power of two of the last number + u32 n_hd_sec; // the number of hd sector in the wbfs partition + + /* standard wii sector (0x8000 bytes) */ + u32 wii_sec_sz; + u8 wii_sec_sz_s; + u32 n_wii_sec; + u32 n_wii_sec_per_disc; + + /* The size of a wbfs sector */ + u32 wbfs_sec_sz; + u32 wbfs_sec_sz_s; + u16 n_wbfs_sec; // this must fit in 16 bit! + u16 n_wbfs_sec_per_disc; // size of the lookup table + + u32 part_lba; + /* virtual methods to read write the partition */ + rw_sector_callback_t read_hdsector; + rw_sector_callback_t write_hdsector; + void *callback_data; + + u16 max_disc; + u32 freeblks_lba; + u32 *freeblks; + u16 disc_info_sz; + + u8 *tmp_buffer; // pre-allocated buffer for unaligned read + + u32 n_disc_open; + +}wbfs_t; + +typedef struct wbfs_disc_s +{ + wbfs_t *p; + wbfs_disc_info_t *header; // pointer to wii header + int i; // disc index in the wbfs header (disc_table) +}wbfs_disc_t; + + +#define WBFS_MAGIC (('W'<<24)|('B'<<16)|('F'<<8)|('S')) + +/*! @brief open a MSDOS partitionned harddrive. This tries to find a wbfs partition into the harddrive + @param read_hdsector,write_hdsector: accessors to a harddrive + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this disc. Can be set to zero if the partition in already initialized + @reset: not implemented, This will format the whole harddrive with one wbfs partition that fits the whole disk. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, int reset); + +/*! @brief open a wbfs partition + @param read_hdsector,write_hdsector: accessors to the partition + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this partition. Can be set to zero if the partition in already initialized + @partition_lba: The partitio offset if you provided accessors to the whole disc. + @reset: initialize the partition with an empty wbfs. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, + rw_sector_callback_t write_hdsector, + void *callback_data, + int hd_sector_size, int num_hd_sector, u32 partition_lba, int reset); + + +/*! @brief close a wbfs partition, and sync the metadatas to the disc */ +void wbfs_close(wbfs_t*); + +/*! @brief open a disc inside a wbfs partition use a 6 char discid+vendorid + @return NULL if discid is not present +*/ +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *diskid); + +/*! @brief close a already open disc inside a wbfs partition */ +void wbfs_close_disc(wbfs_disc_t*d); + + +/*! @brief accessor to the wii disc + @param d: a pointer to already open disc + @param offset: an offset inside the disc, *points 32bit words*, allowing to access 16GB data + @param len: The length of the data to fetch, in *bytes* + */ +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len); + +/*! @return the number of discs inside the paritition */ +u32 wbfs_count_discs(wbfs_t*p); +/*! get the disc info of ith disc inside the partition. It correspond to the first 0x100 bytes of the wiidvd + http://www.wiibrew.org/wiki/Wiidisc#Header + @param i: index of the disc inside the partition + @param header: pointer to 0x100 bytes to write the header + @size: optional pointer to a 32bit word that will get the size in 32bit words of the DVD taken on the partition. +*/ +u32 wbfs_get_disc_info(wbfs_t*p, u32 i,u8 *header,int header_size,u32 *size); + +/*! get the number of used block of the partition. + to be multiplied by p->wbfs_sec_sz (use 64bit multiplication) to have the number in bytes +*/ +u32 wbfs_count_usedblocks(wbfs_t*p); + +/******************* write access ******************/ + +/*! add a wii dvd inside the partition + @param read_src_wii_disc: a callback to access the wii dvd. offsets are in 32bit, len in bytes! + @callback_data: private data passed to the callback + @spinner: a pointer to a function that is regulary called to update a progress bar. + @sel: selects which partitions to copy. + @copy_1_1: makes a 1:1 copy, whenever a game would not use the wii disc format, and some data is hidden outside the filesystem. + */ +u32 wbfs_add_disc(wbfs_t*p,read_wiidisc_callback_t read_src_wii_disc, void *callback_data, + progress_callback_t spinner,partition_selector_t sel,int copy_1_1); + + +/*! remove a wiidvd inside a partition */ +u32 wbfs_rm_disc(wbfs_t*p, u8* discid); + + +/*! trim the file-system to its minimum size + This allows to use wbfs as a wiidisc container + */ +u32 wbfs_trim(wbfs_t*p); + +/*! extract a disc from the wbfs, unused sectors are just untouched, allowing descent filesystem to only really usefull space to store the disc. +Even if the filesize is 4.7GB, the disc usage will be less. + */ +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner); + +/*! extract a file from the wii disc filesystem. + E.G. Allows to extract the opening.bnr to install a game as a system menu channel + */ +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); + +// remove some sanity checks +void wbfs_set_force_mode(int force); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/cios/sdhc_orig/libwbfs/rijndael.c b/cios/sdhc_orig/libwbfs/rijndael.c new file mode 100644 index 0000000..baf8c87 --- /dev/null +++ b/cios/sdhc_orig/libwbfs/rijndael.c @@ -0,0 +1,398 @@ +/* Rijndael Block Cipher - rijndael.c + + Written by Mike Scott 21st April 1999 + mike@compapp.dcu.ie + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + +*/ + +#include +#include + +#define u8 unsigned char /* 8 bits */ +#define u32 unsigned long /* 32 bits */ +#define u64 unsigned long long + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 byte */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4]={0xB,0xD,0x9,0xE}; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256],ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; + +/* Parameter-dependent data */ + +int Nk,Nb,Nr; +u8 fi[24],ri[24]; +u32 fkey[120]; +u32 rkey[120]; + +static u32 pack(u8 *b) +{ /* pack bytes into a 32-bit Word */ + return ((u32)b[3]<<24)|((u32)b[2]<<16)|((u32)b[1]<<8)|(u32)b[0]; +} + +static void unpack(u32 a,u8 *b) +{ /* unpack bytes from a word */ + b[0]=(u8)a; + b[1]=(u8)(a>>8); + b[2]=(u8)(a>>16); + b[3]=(u8)(a>>24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a&0x80) b=0x1B; + else b=0; + a<<=1; + a^=b; + return a; +} + +static u8 bmul(u8 x,u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) return ptab[(ltab[x]+ltab[y])%255]; + else return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a,b); + b[0]=fbsub[b[0]]; + b[1]=fbsub[b[1]]; + b[2]=fbsub[b[2]]; + b[3]=fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x,u32 y) +{ /* dot product of two 4-byte arrays */ + u8 xb[4],yb[4]; + unpack(x,xb); + unpack(y,yb); + return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y,m; + u8 b[4]; + + m=pack(InCo); + b[3]=product(m,x); + m=ROTL24(m); + b[2]=product(m,x); + m=ROTL24(m); + b[1]=product(m,x); + m=ROTL24(m); + b[0]=product(m,x); + y=pack(b); + return y; +} + +u8 ByteSub(u8 x) +{ + u8 y=ptab[255-ltab[x]]; /* multiplicative inverse */ + x=y; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; y^=0x63; + return y; +} + +void gentables(void) +{ /* generate tables */ + int i; + u8 y,b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0]=0; + ptab[0]=1; ltab[1]=0; + ptab[1]=3; ltab[3]=1; + for (i=2;i<256;i++) + { + ptab[i]=ptab[i-1]^xtime(ptab[i-1]); + ltab[ptab[i]]=i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0]=0x63; + rbsub[0x63]=0; + for (i=1;i<256;i++) + { + y=ByteSub((u8)i); + fbsub[i]=y; rbsub[y]=i; + } + + for (i=0,y=1;i<30;i++) + { + rco[i]=y; + y=xtime(y); + } + + /* calculate forward and reverse tables */ + for (i=0;i<256;i++) + { + y=fbsub[i]; + b[3]=y^xtime(y); b[2]=y; + b[1]=y; b[0]=xtime(y); + ftable[i]=pack(b); + + y=rbsub[i]; + b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y); + b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y); + rtable[i]=pack(b); + } +} + +void gkey(int nb,int nk,char *key) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i,j,k,m,N; + int C1,C2,C3; + u32 CipherKey[8]; + + Nb=nb; Nk=nk; + + /* Nr is number of rounds */ + if (Nb>=Nk) Nr=6+Nb; + else Nr=6+Nk; + + C1=1; + if (Nb<8) { C2=2; C3=3; } + else { C2=3; C3=4; } + + /* pre-calculate forward and reverse increments */ + for (m=j=0;j>8)])^ + ROTL16(ftable[(u8)(x[fi[m+1]]>>16)])^ + ROTL24(ftable[x[fi[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)fbsub[(u8)(x[fi[m+1]]>>16)])^ + ROTL24((u32)fbsub[x[fi[m+2]]>>24]); + } + for (i=j=0;i>8)])^ + ROTL16(rtable[(u8)(x[ri[m+1]]>>16)])^ + ROTL24(rtable[x[ri[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)rbsub[(u8)(x[ri[m+1]]>>16)])^ + ROTL24((u32)rbsub[x[ri[m+2]]>>24]); + } + for (i=j=0;i +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include "wiidisc.h" + +void aes_set_key(u8 *key); +void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len); + +static void _decrypt_title_key(u8 *tik, u8 *title_key) +{ + u8 common_key[16]={ + 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, + 0x73, 0x81, 0xaa, 0xf7 + };; + u8 iv[16]; + + wbfs_memset(iv, 0, sizeof iv); + wbfs_memcpy(iv, tik + 0x01dc, 8); + aes_set_key(common_key); + //_aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key); + aes_decrypt(iv, tik + 0x01bf,title_key,16); +} +static u32 _be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static void disc_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + if(data){ + int ret=0; + if(len==0) + return ; + ret = d->read(d->fp,offset,len,data); + if(ret) + wbfs_fatal("error reading disc"); + } + if(d->sector_usage_table) + { + u32 blockno = offset>>13; + do + { + d->sector_usage_table[blockno]=1; + blockno+=1; + if(len>0x8000) + len-=0x8000; + }while(len>0x8000); + } +} + +static void partition_raw_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + disc_read(d, d->partition_raw_offset + offset, data, len); +} + +static void partition_read_block(wiidisc_t *d,u32 blockno, u8 *block) +{ + u8*raw = d->tmp_buffer; + u8 iv[16]; + u32 offset; + if(d->sector_usage_table) + d->sector_usage_table[d->partition_block+blockno]=1; + offset = d->partition_data_offset + ((0x8000>>2) * blockno); + partition_raw_read(d,offset, raw, 0x8000); + + // decrypt data + memcpy(iv, raw + 0x3d0, 16); + aes_set_key(d->disc_key); + aes_decrypt(iv, raw + 0x400,block,0x7c00); +} + +static void partition_read(wiidisc_t *d,u32 offset, u8 *data, u32 len,int fake) +{ + u8 *block = d->tmp_buffer2; + u32 offset_in_block; + u32 len_in_block; + if(fake && d->sector_usage_table==0) + return; + + while(len) { + offset_in_block = offset % (0x7c00>>2); + len_in_block = 0x7c00 - (offset_in_block<<2); + if (len_in_block > len) + len_in_block = len; + if(!fake){ + partition_read_block(d,offset / (0x7c00>>2), block); + wbfs_memcpy(data, block + (offset_in_block<<2), len_in_block); + }else + d->sector_usage_table[d->partition_block+(offset/(0x7c00>>2))]=1; + data += len_in_block; + offset += len_in_block>>2; + len -= len_in_block; + } +} + + +static u32 do_fst(wiidisc_t *d,u8 *fst, const char *names, u32 i) +{ + u32 offset; + u32 size; + const char *name; + u32 j; + + name = names + (_be32(fst + 12*i) & 0x00ffffff); + size = _be32(fst + 12*i + 8); + + if (i == 0) { + for (j = 1; j < size && !d->extracted_buffer; ){ + j = do_fst(d,fst, names, j); + } + return size; + } + //printf("name %s\n",name); + + if (fst[12*i]) { + + for (j = i + 1; j < size && !d->extracted_buffer; ) + j = do_fst(d,fst, names, j); + + return size; + } else { + offset = _be32(fst + 12*i + 4); + if(d->extract_pathname && strcmp(name, d->extract_pathname)==0) + { + d->extracted_buffer = wbfs_ioalloc(size); + partition_read(d,offset, d->extracted_buffer, size,0); + }else + partition_read(d,offset, 0, size,1); + return i + 1; + } +} + +static void do_files(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x480); // XXX: determine actual header size + u32 dol_offset; + u32 fst_offset; + u32 fst_size; + u32 apl_offset; + u32 apl_size; + u8 *apl_header = wbfs_ioalloc(0x20); + u8 *fst; + u32 n_files; + partition_read(d,0, b, 0x480,0); + + dol_offset = _be32(b + 0x0420); + fst_offset = _be32(b + 0x0424); + fst_size = _be32(b + 0x0428)<<2; + + apl_offset = 0x2440>>2; + partition_read(d,apl_offset, apl_header, 0x20,0); + apl_size = 0x20 + _be32(apl_header + 0x14) + _be32(apl_header + 0x18); + // fake read dol and partition + partition_read(d,apl_offset, 0, apl_size,1); + partition_read(d,dol_offset, 0, (fst_offset - dol_offset)<<2,1); + + + fst = wbfs_ioalloc(fst_size); + if (fst == 0) + wbfs_fatal("malloc fst"); + partition_read(d,fst_offset, fst, fst_size,0); + n_files = _be32(fst + 8); + + if (n_files > 1) + do_fst(d,fst, (char *)fst + 12*n_files, 0); + wbfs_iofree(b); + wbfs_iofree(apl_header); + wbfs_iofree(fst); +} + +static void do_partition(wiidisc_t*d) +{ + u8 *tik = wbfs_ioalloc(0x2a4); + u8 *b = wbfs_ioalloc(0x1c); + u64 tmd_offset; + u32 tmd_size; + u8 *tmd; + u64 cert_offset; + u32 cert_size; + u8 *cert; + u64 h3_offset; + + // read ticket, and read some offsets and sizes + partition_raw_read(d,0, tik, 0x2a4); + partition_raw_read(d,0x2a4>>2, b, 0x1c); + + tmd_size = _be32(b); + tmd_offset = _be32(b + 4); + cert_size = _be32(b + 8); + cert_offset = _be32(b + 0x0c); + h3_offset = _be32(b + 0x10); + d->partition_data_offset = _be32(b + 0x14); + d->partition_block = (d->partition_raw_offset+d->partition_data_offset)>>13; + tmd = wbfs_ioalloc(tmd_size); + if (tmd == 0) + wbfs_fatal("malloc tmd"); + partition_raw_read(d,tmd_offset, tmd, tmd_size); + + cert = wbfs_ioalloc(cert_size); + if (cert == 0) + wbfs_fatal("malloc cert"); + partition_raw_read(d,cert_offset, cert, cert_size); + + + _decrypt_title_key(tik, d->disc_key); + + partition_raw_read(d,h3_offset, 0, 0x18000); + wbfs_iofree(b); + wbfs_iofree(tik); + wbfs_iofree(cert); + wbfs_iofree(tmd); + + do_files(d); + +} +static int test_parition_skip(u32 partition_type,partition_selector_t part_sel) +{ + switch(part_sel) + { + case ALL_PARTITIONS: + return 0; + case REMOVE_UPDATE_PARTITION: + return (partition_type==1); + case ONLY_GAME_PARTITION: + return (partition_type!=0); + default: + return (partition_type!=part_sel); + } +} +static void do_disc(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x100); + u64 partition_offset[32]; // XXX: don't know the real maximum + u64 partition_type[32]; // XXX: don't know the real maximum + u32 n_partitions; + u32 magic; + u32 i; + disc_read(d,0, b, 0x100); + magic=_be32(b+24); + if(magic!=0x5D1C9EA3){ + wbfs_error("not a wii disc"); + return ; + } + disc_read(d,0x40000>>2, b, 0x100); + n_partitions = _be32(b); + disc_read(d,_be32(b + 4), b, 0x100); + for (i = 0; i < n_partitions; i++){ + partition_offset[i] = _be32(b + 8 * i); + partition_type[i] = _be32(b + 8 * i+4); + } + for (i = 0; i < n_partitions; i++) { + d->partition_raw_offset = partition_offset[i]; + if(!test_parition_skip(partition_type[i],d->part_sel)) + do_partition(d); + } + wbfs_iofree(b); +} + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp) +{ + wiidisc_t *d = wbfs_malloc(sizeof(wiidisc_t)); + if(!d) + return 0; + wbfs_memset(d,0,sizeof(wiidisc_t)); + d->read = read; + d->fp = fp; + d->part_sel = ALL_PARTITIONS; + d->tmp_buffer = wbfs_ioalloc(0x8000); + d->tmp_buffer2 = wbfs_malloc(0x8000); + + return d; +} +void wd_close_disc(wiidisc_t *d) +{ + wbfs_iofree(d->tmp_buffer); + wbfs_free(d->tmp_buffer2); + wbfs_free(d); +} +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +// XXX pathname not implemented. files are extracted by their name. +// first file found with that name is returned. +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname) +{ + u8 *retval = 0; + d->extract_pathname = pathname; + d->extracted_buffer = 0; + d->part_sel = partition_type; + do_disc(d); + d->extract_pathname = 0; + d->part_sel = ALL_PARTITIONS; + retval = d->extracted_buffer; + d->extracted_buffer = 0; + return retval; +} + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table) +{ + d->sector_usage_table = usage_table; + wbfs_memset(usage_table,0,143432*2); + d->part_sel = selector; + do_disc(d); + d->part_sel = ALL_PARTITIONS; + d->sector_usage_table = 0; +} + +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table) +{ + u8 *b = partition_table; + u32 partition_offset; + u32 partition_type; + u32 n_partitions,i,j; + u32 *b32; + if(selector == ALL_PARTITIONS) + return; + n_partitions = _be32(b); + if(_be32(b + 4)-(0x40000>>2) >0x50) + wbfs_fatal("cannot modify this partition table. Please report the bug."); + + b += (_be32(b + 4)-(0x40000>>2))*4; + j=0; + for (i = 0; i < n_partitions; i++){ + partition_offset = _be32(b + 8 * i); + partition_type = _be32(b + 8 * i+4); + if(!test_parition_skip(partition_type,selector)) + { + b32 = (u32*)(b + 8 * j); + b32[0] = wbfs_htonl(partition_offset); + b32[1] = wbfs_htonl(partition_type); + j++; + } + } + b32 = (u32*)(partition_table); + *b32 = wbfs_htonl(j); +} + diff --git a/cios/sdhc_orig/libwbfs/wiidisc.h b/cios/sdhc_orig/libwbfs/wiidisc.h new file mode 100644 index 0000000..24a27f4 --- /dev/null +++ b/cios/sdhc_orig/libwbfs/wiidisc.h @@ -0,0 +1,67 @@ +#ifndef WIIDISC_H +#define WIIDISC_H +#include +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs and wiidisc + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ +#if 0 //removes extra automatic indentation by editors + } +#endif +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +// offset points 32bit words, count counts bytes +typedef int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf); + +typedef enum{ + UPDATE_PARTITION_TYPE=0, + GAME_PARTITION_TYPE, + OTHER_PARTITION_TYPE, + // value in between selects partition types of that value + ALL_PARTITIONS=0xffffffff-3, + REMOVE_UPDATE_PARTITION, // keeps game + channel installers + ONLY_GAME_PARTITION, +}partition_selector_t; + +typedef struct wiidisc_s +{ + read_wiidisc_callback_t read; + void *fp; + u8 *sector_usage_table; + + // everything points 32bit words. + u32 disc_raw_offset; + u32 partition_raw_offset; + u32 partition_data_offset; + u32 partition_data_size; + u32 partition_block; + + u8 *tmp_buffer; + u8 *tmp_buffer2; + u8 disc_key[16]; + int dont_decrypt; + + partition_selector_t part_sel; + + char *extract_pathname; + u8 *extracted_buffer; +}wiidisc_t; + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp); +void wd_close_disc(wiidisc_t *); +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname); + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table); + +// effectively remove not copied partition from the partition table. +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table); + +#if 0 +{ +#endif +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/cios/sdhc_orig/libwbfs_os.h b/cios/sdhc_orig/libwbfs_os.h new file mode 100644 index 0000000..ab6fe5c --- /dev/null +++ b/cios/sdhc_orig/libwbfs_os.h @@ -0,0 +1,27 @@ +#ifndef _LIBWBFS_OS_H_ +#define _LIBWBFS_OS_H_ + +#include "mem.h" +#include "types.h" + +#define debug_printf(fmt, ...) + +#define wbfs_fatal(x) do { debug_printf("\nwbfs panic:%s\n\n", x); while(1); } while(0) +#define wbfs_error(x) do { debug_printf("\nwbfs error:%s\n\n", x); } while(0) +#define wbfs_malloc(x) Mem_Alloc(x) +#define wbfs_free(x) Mem_Free(x) +#define wbfs_ioalloc(x) Mem_Alloc(x) +#define wbfs_iofree(x) Mem_Free(x) +#define wbfs_ntohl(x) (x) +#define wbfs_htonl(x) (x) +#define wbfs_ntohs(x) (x) +#define wbfs_htons(x) (x) + +#include + +#define wbfs_memcmp(x,y,z) memcmp(x,y,z) +#define wbfs_memcpy(x,y,z) memcpy(x,y,z) +#define wbfs_memset(x,y,z) memset(x,y,z) + + +#endif diff --git a/cios/sdhc_orig/link.ld b/cios/sdhc_orig/link.ld new file mode 100644 index 0000000..7df6e18 --- /dev/null +++ b/cios/sdhc_orig/link.ld @@ -0,0 +1,60 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +/* Sections area */ +MEMORY { + table : ORIGIN = 0x0, LENGTH = 0x4000 + exe(rwx) : ORIGIN = 0x137e0000, LENGTH = 0x8000 + ram(rw) : ORIGIN = 0x137e8000, LENGTH = 0x10000 +} + +__exe_start_phys__ = 0x137e0000; +__ram_start_phys__ = 0x137e8000; + + +SECTIONS { + .ios_info_table : { + KEEP (*(.ios_info_table)) + } > table + + .init : AT (__exe_start_phys__) { + *(.init) + . = ALIGN(4); + } > exe + + .text : { + *(.text*) + *(.gnu.warning) + *(.gnu.linkonce.t.*) + *(.init) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } > exe + + .data : AT (__ram_start_phys__) { + *(.data*) + *(.data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(4); + } > ram + + .rodata : { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .bss : { + *(.dynsbss) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(COMMON) + KEEP(*(.ios_bss)) + . = ALIGN(4); + } > ram +} diff --git a/cios/sdhc_orig/main.c b/cios/sdhc_orig/main.c new file mode 100644 index 0000000..dd42b89 --- /dev/null +++ b/cios/sdhc_orig/main.c @@ -0,0 +1,223 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "es.h" +#include "ipc.h" +#include "mem.h" +#include "module.h" +#include "sdio.h" +#include "syscalls.h" +#include "timer.h" +#include "types.h" +#include "wbfs.h" + + +s32 __SDHC_Ioctlv(u32 cmd, ioctlv *vector, u32 inlen, u32 iolen) +{ + s32 ret = IPC_EINVAL; + + /* Invalidate cache */ + InvalidateVector(vector, inlen, iolen); + + /* Parse IOCTLV command */ + switch (cmd) { + /** Initialize SDHC **/ + case IOCTL_SDHC_INIT: { + /* Initialize SDIO */ + ret = !sdio_Startup(); + + break; + } + + /** Read sectors **/ + case IOCTL_SDHC_READ: { + u32 sector = *(u32 *)(vector[0].data); + u32 numSectors = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Read sectors */ + ret = !sdio_ReadSectors(sector, numSectors, buffer); + + break; + } + + /** Write sectors **/ + case IOCTL_SDHC_WRITE: { + u32 sector = *(u32 *)(vector[0].data); + u32 numSectors = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Write sectors */ + ret = !sdio_WriteSectors(sector, numSectors, buffer); + + break; + } + + /** Check for SD card **/ + case IOCTL_SDHC_ISINSERTED: { + /* Check if SD card is inserted */ + ret = !sdio_IsInserted(); + + break; + } + + /** Open WBFS disc **/ + case IOCTL_WBFS_OPEN_DISC: { + u8 *discid = (u8 *)(vector[0].data); + + /* Open WBFS disc */ + ret = WBFS_OpenDisc(discid); + + break; + } + + /** Read WBFS disc **/ + case IOCTL_WBFS_READ_DISC: { + u32 offset = *(u32 *)(vector[0].data); + u32 len = *(u32 *)(vector[1].data); + void *buffer = vector[2].data; + + /* Read WBFS disc */ + ret = WBFS_Read(buffer, len, offset); + if (ret) + ret = 0x8000; + + break; + } + + default: + break; + } + + /* Flush cache */ + FlushVector(vector, inlen, iolen); + + return ret; +} + +s32 __SDHC_Initialize(u32 *queuehandle) +{ + void *buffer = NULL; + s32 ret; + + /* Initialize memory heap */ + Mem_Init(); + + /* Initialize timer subsystem */ + Timer_Init(); + + /* Allocate queue buffer */ + buffer = Mem_Alloc(0x80); + if (!buffer) + return IPC_ENOMEM; + + /* Create message queue */ + ret = os_message_queue_create(buffer, 32); + if (ret < 0) + return ret; + + /* Register devices */ + os_device_register(DEVICE_NAME, ret); + + /* Copy queue handler */ + *queuehandle = ret; + + return 0; +} + + +int main(void) +{ + u32 queuehandle; + s32 ret; + + /* Print info */ + write("$IOSVersion: SDHC: " __DATE__ " " __TIME__ " 64M$\n"); + + /* Initialize module */ + ret = __SDHC_Initialize(&queuehandle); + if (ret < 0) + return ret; + + /* Main loop */ + while (1) { + ipcmessage *message = NULL; + + /* Wait for message */ + os_message_queue_receive(queuehandle, (void *)&message, 0); + + switch (message->command) { + case IOS_OPEN: { + u64 tid; + + /* Get title ID */ + ret = ES_GetTitleID(&tid); + + /* Check title ID */ + if (ret >= 0) { + write("SDHC: Title identified. Blocking opening request.\n"); + + ret = IPC_ENOENT; + break; + } + + /* Check device path */ + if (!strcmp(message->open.device, DEVICE_NAME)) + ret = message->open.resultfd; + else + ret = IPC_ENOENT; + + break; + } + + case IOS_CLOSE: { + /* Close SDIO */ + ret = !sdio_Shutdown(); + + break; + } + + case IOS_IOCTLV: { + ioctlv *vector = message->ioctlv.vector; + u32 inlen = message->ioctlv.num_in; + u32 iolen = message->ioctlv.num_io; + u32 cmd = message->ioctlv.command; + + /* Parse IOCTLV message */ + ret = __SDHC_Ioctlv(cmd, vector, inlen, iolen); + + break; + } + + default: + /* Unknown command */ + ret = IPC_EINVAL; + } + + /* Acknowledge message */ + os_message_queue_ack(message, ret); + } + + return 0; +} diff --git a/cios/sdhc_orig/mem.c b/cios/sdhc_orig/mem.c new file mode 100644 index 0000000..b8ea978 --- /dev/null +++ b/cios/sdhc_orig/mem.c @@ -0,0 +1,54 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "syscalls.h" + +/* Heap */ +static u32 heapspace[0x2000] ATTRIBUTE_ALIGN(32); + +/* Variables */ +static s32 hid = -1; + + +s32 Mem_Init(void) +{ + /* Heap already created */ + if (hid >= 0) + return 0; + + /* Create heap */ + hid = os_heap_create(heapspace, sizeof(heapspace)); + + return (hid >= 0) ? 0 : -1; +} + +void *Mem_Alloc(u32 size) +{ + /* Allocate memory */ + return os_heap_alloc_aligned(hid, size, 32); +} + +void Mem_Free(void *ptr) +{ + /* Free memory */ + os_heap_free(hid, ptr); +} diff --git a/cios/sdhc_orig/mem.h b/cios/sdhc_orig/mem.h new file mode 100644 index 0000000..84e8428 --- /dev/null +++ b/cios/sdhc_orig/mem.h @@ -0,0 +1,11 @@ +#ifndef _MEM_H_ +#define _MEM_H_ + +#include "types.h" + +/* Prototypes */ +s32 Mem_Init(void); +void *Mem_Alloc(u32 size); +void Mem_Free(void *ptr); + +#endif diff --git a/cios/sdhc_orig/module.h b/cios/sdhc_orig/module.h new file mode 100644 index 0000000..42a5092 --- /dev/null +++ b/cios/sdhc_orig/module.h @@ -0,0 +1,19 @@ +#ifndef _MODULE_H_ +#define _MODULE_H_ + +/* WBFS IOCTL base */ +#define WBFS_BASE (('W'<<24)|('F'<<16)|('S'<<8)) + +/* IOCTL commands */ +#define IOCTL_SDHC_INIT 0x01 +#define IOCTL_SDHC_READ 0x02 +#define IOCTL_SDHC_WRITE 0x03 +#define IOCTL_SDHC_ISINSERTED 0x04 +#define IOCTL_WBFS_OPEN_DISC (WBFS_BASE + 0x1) +#define IOCTL_WBFS_READ_DISC (WBFS_BASE + 0x2) + +/* Device name */ +#define DEVICE_NAME "/dev/sdio/sdhc" + +#endif + diff --git a/cios/sdhc_orig/sdio.c b/cios/sdhc_orig/sdio.c new file mode 100644 index 0000000..f0cd253 --- /dev/null +++ b/cios/sdhc_orig/sdio.c @@ -0,0 +1,704 @@ +/* + Hardware routines for reading and writing to the Wii's internal + SD slot. + + Copyright (c) 2008 + Michael Wiedenbauer (shagkur) + Dave Murphy (WinterMute) + Sven Peter + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include + +#include "syscalls.h" +#include "timer.h" + + +/* Page size */ +#define PAGE_SIZE512 512 + +/* Commands */ +#define SDIOHCR_RESPONSE 0x10 +#define SDIOHCR_HOSTCONTROL 0x28 +#define SDIOHCR_POWERCONTROL 0x29 +#define SDIOHCR_CLOCKCONTROL 0x2c +#define SDIOHCR_TIMEOUTCONTROL 0x2e +#define SDIOHCR_SOFTWARERESET 0x2f +#define SDIOHCR_HOSTCONTROL_4BIT 0x02 + +#define SDIO_DEFAULT_TIMEOUT 0xe + +#define IOCTL_SDIO_WRITEHCREG 0x01 +#define IOCTL_SDIO_READHCREG 0x02 +#define IOCTL_SDIO_READCREG 0x03 +#define IOCTL_SDIO_RESETCARD 0x04 +#define IOCTL_SDIO_WRITECREG 0x05 +#define IOCTL_SDIO_SETCLK 0x06 +#define IOCTL_SDIO_SENDCMD 0x07 +#define IOCTL_SDIO_SETBUSWIDTH 0x08 +#define IOCTL_SDIO_READMCREG 0x09 +#define IOCTL_SDIO_WRITEMCREG 0x0A +#define IOCTL_SDIO_GETSTATUS 0x0B +#define IOCTL_SDIO_GETOCR 0x0C +#define IOCTL_SDIO_READDATA 0x0D +#define IOCTL_SDIO_WRITEDATA 0x0E + +#define SDIOCMD_TYPE_BC 1 +#define SDIOCMD_TYPE_BCR 2 +#define SDIOCMD_TYPE_AC 3 +#define SDIOCMD_TYPE_ADTC 4 + +#define SDIO_RESPONSE_NONE 0 +#define SDIO_RESPONSE_R1 1 +#define SDIO_RESPONSE_R1B 2 +#define SDIO_RESPOSNE_R2 3 +#define SDIO_RESPONSE_R3 4 +#define SDIO_RESPONSE_R4 5 +#define SDIO_RESPONSE_R5 6 +#define SDIO_RESPONSE_R6 7 + +#define SDIO_CMD_GOIDLE 0x00 +#define SDIO_CMD_ALL_SENDCID 0x02 +#define SDIO_CMD_SENDRCA 0x03 +#define SDIO_CMD_SELECT 0x07 +#define SDIO_CMD_DESELECT 0x07 +#define SDIO_CMD_SENDIFCOND 0x08 +#define SDIO_CMD_SENDCSD 0x09 +#define SDIO_CMD_SENDCID 0x0A +#define SDIO_CMD_SENDSTATUS 0x0D +#define SDIO_CMD_SETBLOCKLEN 0x10 +#define SDIO_CMD_READBLOCK 0x11 +#define SDIO_CMD_READMULTIBLOCK 0x12 +#define SDIO_CMD_WRITEBLOCK 0x18 +#define SDIO_CMD_WRITEMULTIBLOCK 0x19 +#define SDIO_CMD_APPCMD 0x37 + +#define SDIO_ACMD_SETBUSWIDTH 0x06 +#define SDIO_ACMD_SENDSCR 0x33 +#define SDIO_ACMD_SENDOPCOND 0x29 + +#define SDIO_STATUS_CARD_INSERTED 0x1 +#define SDIO_STATUS_CARD_INITIALIZED 0x10000 +#define SDIO_STATUS_CARD_SDHC 0x100000 + +/* SDIO structures */ +struct _sdiorequest +{ + u32 cmd; + u32 cmd_type; + u32 rsp_type; + u32 arg; + u32 blk_cnt; + u32 blk_size; + void *dma_addr; + u32 isdma; + u32 pad0; +}; + +struct _sdioresponse +{ + u32 rsp_fields[3]; + u32 acmd12_response; +}; + +/* Variables */ +static s32 __sd0_fd = -1; +static u16 __sd0_rca = 0; +static s32 __sd0_initialized = 0; +static s32 __sd0_sdhc = 0; +static u8 __sd0_cid[16]; +static s32 __sdio_initialized = 0; + +/* Device */ +static char _sd0_fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/slot0"; + +/* Buffers */ +static struct _sdiorequest __request ATTRIBUTE_ALIGN(32); +static struct _sdioresponse __response ATTRIBUTE_ALIGN(32); +static ioctlv __iovec[3] ATTRIBUTE_ALIGN(32); +static u8 __buffer1[32] ATTRIBUTE_ALIGN(32); +static u8 __buffer2[32] ATTRIBUTE_ALIGN(32); + + +static s32 __sdio_sendcommand(u32 cmd, u32 cmd_type, u32 rsp_type, u32 arg, u32 blk_cnt, u32 blk_size, void *buffer, void *reply, u32 rlen) +{ + ioctlv *iovec = __iovec; + struct _sdiorequest *request = &__request; + struct _sdioresponse *response = &__response; + + s32 ret; + + /* Prepare request */ + request->cmd = cmd; + request->cmd_type = cmd_type; + request->rsp_type = rsp_type; + request->arg = arg; + request->blk_cnt = blk_cnt; + request->blk_size = blk_size; + request->dma_addr = buffer; + request->isdma = ((buffer!=NULL)?1:0); + request->pad0 = 0; + + /* Flush cache */ + os_sync_after_write(request, sizeof(struct _sdiorequest)); + os_sync_after_write(response, sizeof(struct _sdioresponse)); + + if (buffer) + os_sync_after_write(buffer, blk_size * blk_cnt); + + /* DMA request */ + if(request->isdma || __sd0_sdhc == 1) { + /* Prepare vector */ + iovec[0].data = request; + iovec[0].len = sizeof(struct _sdiorequest); + iovec[1].data = buffer; + iovec[1].len = (blk_size*blk_cnt); + iovec[2].data = response; + iovec[2].len = sizeof(struct _sdioresponse); + + os_sync_after_write(iovec, sizeof(ioctlv) * 3); + + /* Send command */ + ret = os_ioctlv(__sd0_fd, IOCTL_SDIO_SENDCMD, 2, 1, iovec); + }else + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_SENDCMD, request, sizeof(struct _sdiorequest), response, sizeof(struct _sdioresponse)); + + /* Invalidate cache */ + os_sync_before_read(response, sizeof(struct _sdioresponse)); + + if (buffer) + os_sync_before_read(buffer, blk_size * blk_cnt); + + /* Copy response */ + if (reply && (rlen <= 16)) + memcpy(reply, response, rlen); + + return ret; +} + +static s32 __sdio_setclock(u32 set) +{ + u32 *clock = (u32 *)__buffer1; + + *clock = set; + os_sync_after_write(clock, 4); + + /* Send command */ + return os_ioctl(__sd0_fd, IOCTL_SDIO_SETCLK, clock, sizeof(u32), NULL, 0); +} + +static s32 __sdio_getstatus(void) +{ + u32 *status = (u32 *)__buffer1; + s32 ret; + + os_sync_after_write(status, 4); + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_GETSTATUS, NULL, 0, status, sizeof(u32)); + if (ret < 0) + return ret; + + return *status; +} + +static s32 __sdio_resetcard(void) +{ + u32 *status = (u32 *)__buffer1; + s32 ret; + + os_sync_after_write(status, 4); + + __sd0_rca = 0; + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_RESETCARD, NULL, 0, status, sizeof(u32)); + if (ret < 0) + return ret; + + __sd0_rca = (u16)(*status >> 16); + + return (*status & 0xffff); +} + +static s32 __sdio_gethcr(u8 reg, u8 size, u32 *val) +{ + u32 *hcr_value = (u32 *)__buffer1; + u32 *hcr_query = (u32 *)__buffer2; + + s32 ret; + + if (!val) + return -1; + + *hcr_value = 0; + *val = 0; + + /* Setup query */ + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = 0; + hcr_query[5] = 0; + + os_sync_after_write(hcr_value, sizeof(u32)); + os_sync_after_write(hcr_query, sizeof(u32) * 6); + + /* Send command */ + ret = os_ioctl(__sd0_fd, IOCTL_SDIO_READHCREG, (void *)hcr_query, 24, hcr_value, sizeof(u32)); + + *val = *hcr_value; + + return ret; +} + +static s32 __sdio_sethcr(u8 reg, u8 size, u32 data) +{ + u32 *hcr_query = (u32 *)__buffer1; + + /* Setup query */ + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = data; + hcr_query[5] = 0; + + os_sync_after_write(hcr_query, sizeof(u32) * 6); + + /* Send command */ + return os_ioctl(__sd0_fd, IOCTL_SDIO_WRITEHCREG, (void *)hcr_query, 24, NULL, 0); +} + +static s32 __sdio_waithcr(u8 reg, u8 size, u8 unset, u32 mask) +{ + u32 val; + s32 ret; + s32 tries = 10; + + while(tries-- > 0) + { + ret = __sdio_gethcr(reg, size, &val); + if (ret < 0) + return ret; + + if ((unset && !(val & mask)) || (!unset && (val & mask))) + return 0; + + usleep(10000); + } + + return -1; +} + +static s32 __sdio_setbuswidth(u32 bus_width) +{ + s32 ret; + u32 hc_reg = 0; + + /* Get HCR */ + ret = __sdio_gethcr(SDIOHCR_HOSTCONTROL, 1, &hc_reg); + if (ret < 0) + return ret; + + hc_reg &= 0xff; + hc_reg &= ~SDIOHCR_HOSTCONTROL_4BIT; + if (bus_width == 4) + hc_reg |= SDIOHCR_HOSTCONTROL_4BIT; + + /* Set HCR */ + return __sdio_sethcr(SDIOHCR_HOSTCONTROL, 1, hc_reg); +} + +static s32 __sd0_getrca(void) +{ + s32 ret; + u32 rca; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_SENDRCA, 0, SDIO_RESPONSE_R5, 0, 0, 0, NULL, &rca, sizeof(rca)); + if (ret < 0) + return ret; + + /* Get RCA */ + __sd0_rca = (u16)(rca >> 16); + + return (rca & 0xffff); +} + +static s32 __sd0_select(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_SELECT, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1B, (__sd0_rca << 16), 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_deselect(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_DESELECT, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1B, 0, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_setblocklength(u32 blk_len) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_SETBLOCKLEN, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, blk_len, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_setbuswidth(u32 bus_width) +{ + u16 val; + s32 ret; + + /* Set value */ + val = (bus_width == 4) ? 0x0002 : 0x0000; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_APPCMD, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, (__sd0_rca << 16), 0, 0, NULL, NULL, 0); + if (ret < 0) + return ret; + + /* Send command */ + return __sdio_sendcommand(SDIO_ACMD_SETBUSWIDTH, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, val, 0, 0, NULL, NULL, 0); +} + +static s32 __sd0_getcid(void) +{ + /* Send command */ + return __sdio_sendcommand(SDIO_CMD_ALL_SENDCID, 0, SDIO_RESPOSNE_R2, (__sd0_rca << 16), 0, 0, NULL, __sd0_cid, 16); +} + +static bool __sd0_initio(void) +{ + struct _sdioresponse resp; + + s32 ret; + s32 tries; + u32 status; + + /* Reset card */ + __sdio_resetcard(); + + /* Get card status */ + status = __sdio_getstatus(); + + /* Card not inserted */ + if (!(status & SDIO_STATUS_CARD_INSERTED)) + return false; + + /* Card not initialized */ + if (!(status & SDIO_STATUS_CARD_INITIALIZED)) { + /* Close device */ + os_close(__sd0_fd); + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + if (__sd0_fd < 0) + return false; + + /* Reset the host controller */ + if (__sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7) < 0) + goto fail; + if (__sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7) < 0) + goto fail; + + /* Initialize interrupts */ + __sdio_sethcr(0x34, 4, 0x13f00c3); + __sdio_sethcr(0x38, 4, 0x13f00c3); + + /* Set SDHC flag */ + __sd0_sdhc = 1; + + /* Enable power */ + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xe); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xf); + if (ret < 0) + goto fail; + + /* Enable internal clock, wait until it gets stable and enable sd clock */ + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x101); + if (ret < 0) + goto fail; + + ret = __sdio_waithcr(SDIOHCR_CLOCKCONTROL, 2, 0, 2); + if (ret < 0) + goto fail; + + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x107); + if (ret < 0) + goto fail; + + /* Setup timeout */ + ret = __sdio_sethcr(SDIOHCR_TIMEOUTCONTROL, 1, SDIO_DEFAULT_TIMEOUT); + if (ret < 0) + goto fail; + + /* Standard SDHC initialization process */ + ret = __sdio_sendcommand(SDIO_CMD_GOIDLE, 0, 0, 0, 0, 0, NULL, NULL, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sendcommand(SDIO_CMD_SENDIFCOND, 0, SDIO_RESPONSE_R6, 0x1aa, 0, 0, NULL, &resp, sizeof(resp)); + if (ret < 0) + goto fail; + + if ((resp.rsp_fields[0] & 0xff) != 0xaa) + goto fail; + + /* Do tries */ + for (tries = 10; tries; tries--) { + ret = __sdio_sendcommand(SDIO_CMD_APPCMD, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, 0, 0, 0, NULL, NULL, 0); + if (ret < 0) + goto fail; + + ret = __sdio_sendcommand(SDIO_ACMD_SENDOPCOND, 0, SDIO_RESPONSE_R3, 0x40300000, 0, 0, NULL, &resp, sizeof(resp)); + if (ret < 0) + goto fail; + + if (resp.rsp_fields[0] & (1 << 31)) + break; + + usleep(10000); + } + + if (tries < 0) + goto fail; + + if (resp.rsp_fields[0] & (1 << 30)) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + /* Get CID */ + ret = __sd0_getcid(); + if (ret < 0) + goto fail; + + /* Get RCA */ + ret = __sd0_getrca(); + if (ret < 0) + goto fail; + } + else if (status & SDIO_STATUS_CARD_SDHC) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + /* Set bus bandwidth */ + ret = __sdio_setbuswidth(4); + if (ret < 0) + return false; + + /* Set clock */ + ret = __sdio_setclock(1); + if (ret < 0) + return false; + + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + return false; + + /* Set block length */ + ret = __sd0_setblocklength(PAGE_SIZE512); + if (ret < 0) { + ret = __sd0_deselect(); + return false; + } + + /* Set bus bandwidth */ + ret = __sd0_setbuswidth(4); + if (ret < 0) { + ret = __sd0_deselect(); + return false; + } + + /* Deselect card */ + __sd0_deselect(); + + /* Set initialized */ + __sd0_initialized = 1; + + return true; + +fail: + /* Software reset */ + __sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7); + __sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7); + + /* Close device */ + os_close(__sd0_fd); + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + + return false; +} + +bool sdio_Deinitialize(void) +{ + /* Close device */ + if (__sd0_fd >= 0) + os_close(__sd0_fd); + + /* Reset variables */ + __sd0_fd = -1; + __sdio_initialized = 0; + + return true; +} + +bool sdio_Startup(void) +{ + /* Already initialized */ + if (__sdio_initialized) + return true; + + /* Open device */ + __sd0_fd = os_open(_sd0_fs, 1); + + if (__sd0_fd < 0) { + sdio_Deinitialize(); + return false; + } + + if (!__sd0_initio()) { + sdio_Deinitialize(); + return false; + } + + /* Set initialized */ + __sdio_initialized = 1; + + return true; +} + +bool sdio_Shutdown(void) +{ + /* Already deinitialized */ + if (!__sd0_initialized) + return false; + + /* Deinitialize */ + sdio_Deinitialize(); + + /* Set deinitialized */ + __sd0_initialized = 0; + + return true; +} + +bool sdio_ReadSectors(sec_t sector, sec_t numSectors,void* buffer) +{ + u32 i; + s32 ret; + + /* No buffer */ + if (!buffer) + return false; + + /* Read loop */ + for (i = 0; i < 10; i++) { + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + continue; + + /* Check if SDHC */ + if (!__sd0_sdhc) + sector *= PAGE_SIZE512; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, sector, numSectors, PAGE_SIZE512, buffer, NULL, 0); + + /* Deselect card */ + __sd0_deselect(); + + if (ret >= 0) + return true; + } + + return false; +} + +bool sdio_WriteSectors(sec_t sector, sec_t numSectors,const void* buffer) +{ + u32 i; + s32 ret; + + /* No buffer */ + if (!buffer) + return false; + + /* Write loop */ + for (i = 0; i < 10; i++) { + /* Select card */ + ret = __sd0_select(); + if (ret < 0) + continue; + + /* Check if SDHC */ + if (!__sd0_sdhc) + sector *= PAGE_SIZE512; + + /* Send command */ + ret = __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK, SDIOCMD_TYPE_AC, SDIO_RESPONSE_R1, sector, numSectors, PAGE_SIZE512, (char *)buffer, NULL, 0); + + /* Deselect card */ + __sd0_deselect(); + + if (ret >= 0) + return true; + } + + return false; +} + +bool sdio_ClearStatus(void) +{ + return true; +} + +bool sdio_IsInserted(void) +{ + /* Check if card is inserted */ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INSERTED) == + SDIO_STATUS_CARD_INSERTED); +} + +bool sdio_IsInitialized(void) +{ + /* Check if card is initialized */ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INITIALIZED) == + SDIO_STATUS_CARD_INITIALIZED); +} diff --git a/cios/sdhc_orig/sdio.h b/cios/sdhc_orig/sdio.h new file mode 100644 index 0000000..fab61d9 --- /dev/null +++ b/cios/sdhc_orig/sdio.h @@ -0,0 +1,13 @@ +#ifndef _SDIO_H_ +#define _SDIO_H_ + +#include "types.h" + +/* Prototypes */ +bool sdio_Startup(void); +bool sdio_Shutdown(void); +bool sdio_ReadSectors(sec_t sector, sec_t numSectors, void *buffer); +bool sdio_WriteSectors(sec_t sector, sec_t numSectors, const void *buffer); +bool sdio_IsInserted(void); + +#endif diff --git a/cios/sdhc_orig/start.s b/cios/sdhc_orig/start.s new file mode 100644 index 0000000..f4307af --- /dev/null +++ b/cios/sdhc_orig/start.s @@ -0,0 +1,74 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + .section ".init" + .arm + + .EQU ios_thread_arg, 4 + .EQU ios_thread_priority, 0x48 + .EQU ios_thread_stacksize, 0x2000 + + + .global _start +_start: + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bx r3 + + + +/* + * IOS bss + */ + .section ".ios_bss", "a", %nobits + + .space ios_thread_stacksize + .global ios_thread_stack /* stack decrements from high address.. */ +ios_thread_stack: + + +/* + * IOS info table + */ + .section ".ios_info_table", "ax", %progbits + + .global ios_info_table +ios_info_table: + .long 0x0 + .long 0x28 @ numentries * 0x28 + .long 0x6 + + .long 0xB + .long ios_thread_arg @ passed to thread entry func, maybe module id + + .long 0x9 + .long _start + + .long 0x7D + .long ios_thread_priority + + .long 0x7E + .long ios_thread_stacksize + + .long 0x7F + .long ios_thread_stack diff --git a/cios/sdhc_orig/syscalls.h b/cios/sdhc_orig/syscalls.h new file mode 100644 index 0000000..03721d8 --- /dev/null +++ b/cios/sdhc_orig/syscalls.h @@ -0,0 +1,72 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _IOS_SYSCALLS_H_ +#define _IOS_SYSCALLS_H_ + +#include "ipc.h" +#include "types.h" + +/* IOS syscalls */ +s32 os_thread_create(u32 (*entry)(void *arg), void *arg, void *stack, u32 stacksize, u32 priority, s32 autostart); +void os_thread_set_priority(u32 priority); +s32 os_thread_get_priority(void); +s32 os_get_thread_id(void); +s32 os_get_parent_thread_id(void); +s32 os_thread_continue(s32 id); +s32 os_thread_stop(s32 id); +s32 os_message_queue_create(void *ptr, u32 id); +s32 os_message_queue_receive(s32 queue, u32 *message, u32 flags); +s32 os_message_queue_send(s32 queue, u32 message, s32 flags); +s32 os_message_queue_now(s32 queue, u32 message, s32 flags); +s32 os_heap_create(void *ptr, s32 size); +s32 os_heap_destroy(s32 heap); +void *os_heap_alloc(s32 heap, u32 size); +void *os_heap_alloc_aligned(s32 heap, s32 size, s32 align); +void os_heap_free(s32 heap, void *ptr); +s32 os_device_register(const char *devicename, s32 queuehandle); +void os_message_queue_ack(void *message, s32 result); +void os_sync_before_read(void *ptr, s32 size); +void os_sync_after_write(void *ptr, s32 size); +void os_syscall_50(u32 unknown); +s32 os_open(const char *device, s32 mode); +s32 os_close(s32 fd); +s32 os_read(s32 fd, void *d, s32 len); +s32 os_write(s32 fd, void *s, s32 len); +s32 os_seek(s32 fd, s32 offset, s32 mode); +s32 os_ioctlv(s32 fd, s32 request, s32 bytes_in, s32 bytes_out, ioctlv *vector); +s32 os_ioctl(s32 fd, s32 request, void *in, s32 bytes_in, void *out, s32 bytes_out); +s32 os_create_timer(s32 time_us, s32 repeat_time_us, s32 message_queue, s32 message); +s32 os_destroy_timer(s32 time_id); +s32 os_stop_timer(s32 timer_id); +s32 os_restart_timer(s32 timer_id, s32 dummy, s32 time_us); +s32 os_timer_now(s32 time_id); +s32 os_register_event_handler(s32 device, s32 queue, s32 message); +s32 os_unregister_event_handler(s32 device); +s32 os_software_IRQ(s32 dev); +void os_get_key(s32 keyid, void *out); +void *os_virt_to_phys(void *ptr); + +/* ARM syscalls */ +void write(const char *str); + +#endif diff --git a/cios/sdhc_orig/syscalls.s b/cios/sdhc_orig/syscalls.s new file mode 100644 index 0000000..9ec8e97 --- /dev/null +++ b/cios/sdhc_orig/syscalls.s @@ -0,0 +1,284 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +.macro syscall vec_sys + .long 0xE6000010 + (\vec_sys<<5) + bx lr +.endm + + +/* + * IOS syscalls + */ + .code 32 + .global os_thread_create +os_thread_create: + syscall 0x0 + + .code 32 + .global os_thread_joint +os_thread_joint: + syscall 0x1 + + .code 32 + .global os_thread_cancel +os_thread_cancel: + syscall 0x2 + + .code 32 + .global os_get_thread_id +os_get_thread_id: + syscall 0x3 + + .code 32 + .global os_get_parent_thread_id +os_get_parent_thread_id: + syscall 0x4 + + .code 32 + .global os_thread_continue +os_thread_continue: + syscall 0x5 + + .code 32 + .global os_thread_stop +os_thread_stop: + syscall 0x6 + + .code 32 + .global os_thread_yield +os_thread_yiel: + syscall 0x7 + + .code 32 + .global os_thread_get_priority +os_thread_get_priority : + syscall 0x8 + + .code 32 + .global os_thread_set_priority +os_thread_set_priority: + syscall 0x9 + + .code 32 + .global os_message_queue_create +os_message_queue_create: + syscall 0xa + + .code 32 + .global os_message_queue_destroy +os_message_queue_destroy: + syscall 0xb + + .code 32 + .global os_message_queue_send +os_message_queue_send: + syscall 0xc + + .code 32 + .global os_message_queue_send_now +os_message_queue_send_now: + syscall 0xd + + .code 32 + .global os_message_queue_receive +os_message_queue_receive: + syscall 0xe + + .code 32 + .global os_register_event_handler +os_register_event_handler: + syscall 0xf + + .code 32 + .global os_unregister_event_handler +os_unregister_event_handler: + syscall 0x10 + + .code 32 + .global os_create_timer +os_create_timer: + syscall 0x11 + + .code 32 + .global os_restart_timer +os_restart_timer: + syscall 0x12 + + .code 32 + .global os_stop_timer +os_stop_timer: + syscall 0x13 + + .code 32 + .global os_destroy_timer +os_destroy_timer: + syscall 0x14 + + .code 32 + .global os_timer_now +os_timer_now: + syscall 0x15 + + .code 32 + .global os_heap_create +os_heap_create: + syscall 0x16 + + .code 32 + .global os_heap_destroy +os_heap_destroy: + syscall 0x17 + + .code 32 + .global os_heap_alloc +os_heap_alloc: + syscall 0x18 + + .code 32 + .global os_heap_alloc_aligned +os_heap_alloc_aligned: + syscall 0x19 + + .code 32 + .global os_heap_free +os_heap_free: + syscall 0x1a + + .code 32 + .global os_device_register +os_device_register: + syscall 0x1b + + .code 32 + .global os_open +os_open: + syscall 0x1c + + .code 32 + .global os_close +os_close: + syscall 0x1d + + .code 32 + .global os_read +os_read: + syscall 0x1e + + .code 32 + .global os_write +os_write: + syscall 0x1f + + .code 32 + .global os_seek +os_seek: + syscall 0x20 + + .code 32 + .global os_ioctl +os_ioctl: + syscall 0x21 + + .code 32 + .global os_ioctlv +os_ioctlv: + syscall 0x22 + + .code 32 + .global os_open_async +os_open_async: + syscall 0x23 + + .code 32 + .global os_close_async +os_close_async: + syscall 0x24 + + .code 32 + .global os_read_async +os_read_async: + syscall 0x25 + + .code 32 + .global os_write_async +os_write_async: + syscall 0x26 + + .code 32 + .global os_seek_async +os_seek_async: + syscall 0x27 + + .code 32 + .global os_ioctl_async +os_ioctl_async: + syscall 0x28 + + .code 32 + .global os_ioctlv_async +os_ioctlv_async: + syscall 0x29 + + .code 32 + .global os_message_queue_ack +os_message_queue_ack: + syscall 0x2a + + .code 32 + .global os_software_IRQ +os_software_IRQ: + syscall 0x34 + + .code 32 + .global os_sync_before_read +os_sync_before_read: + syscall 0x3f + + .code 32 + .global os_sync_after_write +os_sync_after_write: + syscall 0x40 + + .code 32 + .global os_virt_to_phys +os_virt_to_phys: + syscall 0x4f + + .code 32 + .global os_syscall_50 +os_syscall_50: + syscall 0x50 + + +/* + * ARM syscalls + */ + .code 32 + .global write +write: + mov r2, lr + adds r1, r0, #0 + movs r0, #4 + svc 0xab + bx r2 diff --git a/cios/sdhc_orig/timer.c b/cios/sdhc_orig/timer.c new file mode 100644 index 0000000..ba5bcee --- /dev/null +++ b/cios/sdhc_orig/timer.c @@ -0,0 +1,69 @@ +/* + Custom IOS Module (MLOAD) + + Copyright (C) 2008 neimod. + Copyright (C) 2010 Hermes. + Copyright (C) 2010 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mem.h" +#include "syscalls.h" +#include "types.h" + +/* Variables */ +static s32 queuehandle = -1; +static s32 timerId = -1; + + +void Timer_Init(void) +{ + void *queuespace = NULL; + + /* Queue already created */ + if (queuehandle >= 0) + return; + + /* Create queue */ + queuespace = Mem_Alloc(0x40); + queuehandle = os_message_queue_create(queuespace, 16); + + /* Create timer */ + timerId = os_create_timer(0, 0, queuehandle, 0x666); + + /* Stop timer */ + os_stop_timer(timerId); +} + +void Timer_Sleep(u32 time) +{ + u32 message; + + /* Restart timer */ + os_restart_timer(timerId, 0, time); + + while (1) { + /* Wait to receive message */ + os_message_queue_receive(queuehandle, (void *)&message, 0); + + /* Message received */ + if (message == 0x666) + break; + } + + /* Stop timer */ + os_stop_timer(timerId); +} diff --git a/cios/sdhc_orig/timer.h b/cios/sdhc_orig/timer.h new file mode 100644 index 0000000..e690925 --- /dev/null +++ b/cios/sdhc_orig/timer.h @@ -0,0 +1,13 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +/* Macros */ +#define udelay(t) Timer_Sleep(t) +#define usleep(t) Timer_Sleep(t) +#define msleep(t) Timer_Sleep(t*1000) + +/* Prototypes */ +void Timer_Init(void); +void Timer_Sleep(u32 time); + +#endif diff --git a/cios/sdhc_orig/types.h b/cios/sdhc_orig/types.h new file mode 100644 index 0000000..2e1783d --- /dev/null +++ b/cios/sdhc_orig/types.h @@ -0,0 +1,40 @@ +#ifndef _IOS_TYPES_H_ +#define _IOS_TYPES_H_ + +#include + +/* NULL pointer */ +#ifndef NULL +# define NULL ((void *)0) +#endif + +/* Data types */ +typedef char s8; +typedef short s16; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef uint32_t sec_t; + +/* Boolean values */ +#define true 1 +#define false 0 + +/* Attributes */ +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +/* Stack align */ +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#endif diff --git a/cios/sdhc_orig/wbfs.c b/cios/sdhc_orig/wbfs.c new file mode 100644 index 0000000..948c470 --- /dev/null +++ b/cios/sdhc_orig/wbfs.c @@ -0,0 +1,92 @@ +/* + Custom IOS Module (SDHC) + + Copyright (C) 2008 neimod. + Copyright (C) 2009 WiiGator. + Copyright (C) 2009 Waninkoko. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "sdio.h" +#include "syscalls.h" +#include "libwbfs/libwbfs.h" + +/* Constants */ +#define SECTOR_SIZE 0x200 + +/* Variables */ +static wbfs_t *hdd = NULL; +static wbfs_disc_t *disc = NULL; + + +static int __WBFS_ReadSector(void *cbdata, u32 lba, u32 count, void *buffer) +{ + s32 ret; + + /* Read data */ + ret = sdio_ReadSectors(lba, count, buffer); + if (!ret) + return 1; + + /* Invalidate range */ + os_sync_before_read(buffer, SECTOR_SIZE * count); + + return 0; +} + + +s32 WBFS_OpenDisc(u8 *discid) +{ + s32 ret; + + /* Initialize SDIO */ + ret = sdio_Startup(); + if (!ret) + return 1; + + /* Close disc */ + if (disc) + wbfs_close_disc(disc); + + /* Close SD */ + if (hdd) + wbfs_close(hdd); + + /* Open SD */ + hdd = wbfs_open_hd(__WBFS_ReadSector, NULL, NULL, SECTOR_SIZE, 0, 0); + if (!hdd) + return 2; + + /* Open disc */ + disc = wbfs_open_disc(hdd, discid); + if (!disc) + return 3; + + return 0; +} + +s32 WBFS_Read(void *buffer, u32 len, u32 offset) +{ + /* No disc opened */ + if (!disc) + return 1; + + /* Disc read */ + return wbfs_disc_read(disc, offset, buffer, len); +} diff --git a/cios/sdhc_orig/wbfs.h b/cios/sdhc_orig/wbfs.h new file mode 100644 index 0000000..90bf0e2 --- /dev/null +++ b/cios/sdhc_orig/wbfs.h @@ -0,0 +1,9 @@ +#ifndef _WBFS_H_ +#define _WBFS_H_ + +/* Prototypes */ +s32 WBFS_OpenDisc(u8 *discid); +s32 WBFS_Read(void *buffer, u32 len, u32 offset); + +#endif + diff --git a/cios/stripios/MakeIt.bat b/cios/stripios/MakeIt.bat new file mode 100644 index 0000000..1c4c717 --- /dev/null +++ b/cios/stripios/MakeIt.bat @@ -0,0 +1,4 @@ +path=%path%;C:\Dev-Cpp\bin +make + +pause diff --git a/cios/stripios/Makefile b/cios/stripios/Makefile new file mode 100644 index 0000000..bced68a --- /dev/null +++ b/cios/stripios/Makefile @@ -0,0 +1,5 @@ +stripios.exe:main.cpp + g++ main.cpp -o stripios.exe +clean: + @echo "clean ..." + @rm -f stripios.exe \ No newline at end of file diff --git a/cios/stripios/main.cpp b/cios/stripios/main.cpp new file mode 100644 index 0000000..3ddc710 --- /dev/null +++ b/cios/stripios/main.cpp @@ -0,0 +1,371 @@ +/* + IOS ELF stripper, converts traditional ELF files into the format IOS wants. + Copyright (C) 2008 neimod. + Copyright (C) 2009-2010 Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#define ELF_NIDENT 16 + +typedef struct +{ + unsigned long ident0; + unsigned long ident1; + unsigned long ident2; + unsigned long ident3; + unsigned long machinetype; + unsigned long version; + unsigned long entry; + unsigned long phoff; + unsigned long shoff; + unsigned long flags; + unsigned short ehsize; + unsigned short phentsize; + unsigned short phnum; + unsigned short shentsize; + unsigned short shnum; + unsigned short shtrndx; +} elfheader; + +typedef struct +{ + unsigned long type; + unsigned long offset; + unsigned long vaddr; + unsigned long paddr; + unsigned long filesz; + unsigned long memsz; + unsigned long flags; + unsigned long align; +} elfphentry; + +typedef struct +{ + unsigned long offset; + unsigned long size; +} offset_size_pair; + +unsigned short getbe16(void* pvoid) +{ + unsigned char* p = (unsigned char*)pvoid; + + return (p[0] << 8) | (p[1] << 0); +} + +unsigned long getbe32(void* pvoid) +{ + unsigned char* p = (unsigned char*)pvoid; + + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0); +} + +void putbe16(void* pvoid, unsigned short val) +{ + unsigned char* p = (unsigned char*)pvoid; + + p[0] = val >> 8; + p[1] = val >> 0; +} + +void putbe32(void* pvoid, unsigned long val) +{ + unsigned char* p = (unsigned char*)pvoid; + + p[0] = val >> 24; + p[1] = val >> 16; + p[2] = val >> 8; + p[3] = val >> 0; +} + +int main(int argc, char* argv[]) +{ + int result = 0; + +unsigned long strip=0; + + fprintf(stdout, "stripios - IOS ELF stripper - by neimod\n"); + if (argc < 3 || argc==4) + { + fprintf(stderr,"Usage: %s [strip addr]\n", argv[0]); + + return -1; + } + else + if(argc==5) + { + if(!strcmp(argv[3],"strip")) + { + sscanf( argv[4], "%x",&strip ); + + printf("strip: %x\n",strip); + } + else + { + fprintf(stderr,"Usage: %s [strip addr]\n", argv[0]); + + return -1; + } + } + + FILE* fin = fopen(argv[1], "rb"); + FILE* fout = fopen(argv[2], "wb"); + + if (fin == 0 || fout == 0) + { + if (fin == 0) + fprintf(stderr,"ERROR opening file %s\n", argv[1]); + if (fout == 0) + fprintf(stderr,"ERROR opening file %s\n", argv[2]); + return 1; + } + + elfheader header; + + if (fread(&header, sizeof(elfheader), 1, fin) != 1) + { + fprintf(stderr,"ERROR reading ELF header\n"); + return 1; + } + + unsigned long elfmagicword = getbe32(&header.ident0); + + if (elfmagicword != 0x7F454C46) + { + fprintf(stderr,"ERROR not a valid ELF\n"); + return 1; + } + + unsigned long phoff = getbe32(&header.phoff); + unsigned short phnum = getbe16(&header.phnum); + unsigned long memsz = 0, filesz = 0; + unsigned long vaddr = 0, paddr = 0; + + putbe32(&header.ident1, 0x01020161); + putbe32(&header.ident2, 0x01000000); + putbe32(&header.ident3, 0); + putbe32(&header.machinetype, 0x20028); + putbe32(&header.version, 1); + putbe32(&header.flags, 0x606); + putbe16(&header.ehsize, 0x34); + putbe16(&header.phentsize, 0x20); + putbe16(&header.shentsize, 0); + putbe16(&header.shnum, 0); + putbe16(&header.shtrndx, 0); + putbe32(&header.phoff, 0x34); + putbe32(&header.shoff, 0); + + putbe16(&header.phnum, phnum + 2); + + elfphentry* origentries = new elfphentry[phnum]; + + fseek(fin, phoff, SEEK_SET); + if (fread(origentries, sizeof(elfphentry), phnum, fin) != phnum) + { + fprintf(stderr,"ERROR reading program header\n"); + return 1; + } + + + elfphentry* iosphentry = 0; + + + // Find zero-address phentry + for(int i=0; ipaddr) == 0) + { + iosphentry = phentry; + } + } + + if (0 == iosphentry) + { + fprintf(stderr,"ERROR IOS table not found in program header\n"); + return 1; + } + + + elfphentry* entries = new elfphentry[phnum+2]; + offset_size_pair* offsetsizes = new offset_size_pair[phnum]; + + elfphentry* q = entries; + phoff = 0x34; + + for(int i=0; ioffset); + offsetsizes[i].size = getbe32(&p->filesz); + + if (p == iosphentry) + { + unsigned long startoffset = phoff; + unsigned long startvaddr = vaddr; + unsigned long startpaddr = paddr; + unsigned long totalsize = 0; + + filesz = memsz = (phnum+2) * 0x20; + + // PHDR + putbe32(&phentry.type, 6); + putbe32(&phentry.offset, phoff); + putbe32(&phentry.vaddr, paddr); + putbe32(&phentry.paddr, vaddr); + putbe32(&phentry.filesz, filesz); + putbe32(&phentry.memsz, memsz); + putbe32(&phentry.flags, 0x00F00000); + putbe32(&phentry.align, 0x4); + + *q++ = phentry; + + phoff += memsz; + paddr += memsz; + vaddr += memsz; + totalsize += memsz; + + filesz = memsz = getbe32(&p->memsz); + + + + // NOTE + putbe32(&phentry.type, 4); + putbe32(&phentry.offset, phoff); + putbe32(&phentry.vaddr, vaddr); + putbe32(&phentry.paddr, paddr); + putbe32(&phentry.filesz, filesz); + putbe32(&phentry.memsz, memsz); + putbe32(&phentry.flags, 0x00F00000); + putbe32(&phentry.align, 0x4); + + + + *q++ = phentry; + + phoff += memsz; + paddr += memsz; + vaddr += memsz; + totalsize += memsz; + + filesz = memsz = totalsize; + + // LOAD + putbe32(&phentry.type, 1); + putbe32(&phentry.offset, startoffset); + putbe32(&phentry.vaddr, startvaddr); + putbe32(&phentry.paddr, startpaddr); + putbe32(&phentry.filesz, totalsize); + putbe32(&phentry.memsz, totalsize); + putbe32(&phentry.flags, 0x00F00000); + putbe32(&phentry.align, 0x4000); + + *q++ = phentry; + } + else + { + + filesz = getbe32(&p->filesz); + memsz = getbe32(&p->memsz); + //printf("flags %x\n",getbe32(&p->flags)); + if(strip==getbe32(&p->vaddr) && strip!=0) // strip zeroes + { + filesz=1; + putbe32(&p->filesz,filesz); + } + + + putbe32(&phentry.type, getbe32(&p->type)); + putbe32(&phentry.offset, phoff); + putbe32(&phentry.vaddr, getbe32(&p->vaddr)); + putbe32(&phentry.paddr, getbe32(&p->paddr)); + putbe32(&phentry.filesz, filesz); + putbe32(&phentry.memsz, memsz); + putbe32(&phentry.flags, (getbe32(&p->flags) | 0xf00000)); + putbe32(&phentry.align, 0x4); + + *q++ = phentry; + + phoff += filesz; + } + } + + if (fwrite(&header, sizeof(elfheader), 1, fout) != 1) + { + fprintf(stderr,"ERROR writing ELF header\n"); + return 1; + } + + if (fwrite(entries, sizeof(elfphentry), phnum+2, fout) != (phnum+2)) + { + fprintf(stderr,"ERROR writing ELF program header\n"); + return 1; + } + + for(int i=0; ioffset); + unsigned long filesz = getbe32(&p->filesz); + + if (filesz) + { + fseek(fin, offset, SEEK_SET); + + fprintf(stdout,"Writing segment 0x%08X to 0x%08X (%d bytes) - %x %s\n", getbe32(&p->vaddr), ftell(fout), filesz,getbe32(&p->memsz), + (strip==getbe32(&p->vaddr) && strip!=0 )? "- Stripped" : ""); + + unsigned char* data = new unsigned char[filesz]; + + + if (fread(data, filesz, 1, fin) != 1 || fwrite(data, filesz, 1, fout) != 1) + { + fprintf(stderr,"ERROR writing segment\n"); + delete[] data; + return 1; + } + + delete[] data; + } + else + { + fprintf(stdout,"Skipping segment 0x%08X\n", getbe32(&p->vaddr)); + } + } + +cleanup: + if (offsetsizes) + delete[] offsetsizes; + if (entries) + delete[] entries; + if (origentries) + delete[] origentries; + if (fout) + fclose(fout); + + if (fin) + fclose(fin); + + return result; +} + diff --git a/cios/stripios/stripios.bin b/cios/stripios/stripios.bin new file mode 100644 index 0000000000000000000000000000000000000000..c95cdd9f8350f70006ab4ddd78724d683c47b1d9 GIT binary patch literal 11074 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 OKzpB%*YIQ-iT?q-iD32s literal 0 HcmV?d00001 diff --git a/cios/stripios/stripios.exe b/cios/stripios/stripios.exe new file mode 100644 index 0000000000000000000000000000000000000000..d0a4b749842bee1c554662bf475939ac6f116817 GIT binary patch literal 51620 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 literal 0 HcmV?d00001 diff --git a/data/bg_gui b/data/bg_gui new file mode 100644 index 0000000000000000000000000000000000000000..4113fe56d050329d2dff6dbf5afd2fb271b85e08 GIT binary patch literal 5053 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 F{sr?x!e9UZ literal 0 HcmV?d00001 diff --git a/data/button.png b/data/button.png new file mode 100644 index 0000000000000000000000000000000000000000..40a47d72f89008b9f1047beb95a454759e4f3601 GIT binary patch literal 17380 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 b%?ST5xd-hwscq8}00000NkvXXu0mjf5m>&T literal 0 HcmV?d00001 diff --git a/data/button_old.png b/data/button_old.png new file mode 100644 index 0000000000000000000000000000000000000000..9db6452aea5cc27faccf8d6e17b73d8327c90e3e GIT binary patch literal 7717 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 fhgF{LQLx_YNDZ!!30|L?xf=Y)7GDu2E${-*) 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 literal 0 HcmV?d00001 diff --git a/data/dip_plugin2_bin b/data/dip_plugin2_bin new file mode 100644 index 0000000000000000000000000000000000000000..9a64dc10b079644aa70db609e214a4fb06bb26b4 GIT binary patch literal 3304 zcmbVOeQZ1Re+{xy_#aoYdH)u}RnhsVZY9Hy>|-mW0wZL48iBb{&|O zf!ahHT1{Id+WtYnl>i478YT^zcCtjgshcZ#{rF>LLpD)SvUaK`iXQ|--Le(Rmv^51 zU}?0b>TI2N?mhRO-#s7qocH+Q3yYRCB1W+N?M*>2uMq^}2SA4fp?9|+pbkNBJR=A{ z+=9O4#}8k+LlSfUZ6mXYZ6=94mo(n1@otT;(YQM1o>f;i^Qm$zsoFdg z-76Wf0h`J8Fp>iYSU}i~J=jSoAaq2H@gY^?gt~W*P0@X$+cXq;X0FguKqKK~LRO^#7k3{sAQ>Kqw4o7sPxAGk6WV<7*FEYJZL}9%h z9p!AHmZxwN_ApDb=*CZ1qEx*DCF{lzHuw(BFts_|SEet?0Xvdh#3%D2lDp+F_&r6W z0I%wS)wRT1@cY))Mh5FaKe4iGCR2x!@n{)#%#T4tUlEQlzVHk)rms9Fgczh|jX#JE9z~VSGPRk~K&<6Vc-)%j^fJl z=k6KhGm^;1*p1D2so}lU5~&Ql;jKt2wY)2pg8z(UuB`CAKBdP--^poH^??6S8VM-w zR%0$ExZcVNda^+vlbGcj=@}LVmm*WE=H6NSFdeS*cmxV2{RFyM<#%@W@T-^>j0N($FLVtxpvW|V)E1V210^TVHq zfNur*Ma+%4_A+dyFVH_ncUyWbk)RIjDWiUP7Qsm?0PmeBQ}9@upH{w7M_>U`zECkg ze-V6bn+$EJ#vE-Xhd_6+$jHt4j5+CfmoZs_1=xQ9Dxx{gkY zE{Bk3Z>+py8QAd({Sdq<23wX;37*AyYc@O3$GYISWcEdprb1=sU1sm_$=n4kehf3lk39rHtfc0pnigwQ=<9Pe=D--O*BBq9ZAp}#cqgxR;{+wF|nraGT@(LBL`Q_XcV$g!CS`p&X9K#l`u~nY-K&SKA zA%B~y8yVlR>6@)DCIvE3S!Fy*4x>Mj2t;x**I-!1di<4s6JAFA{?(RBD?+b$m`6YI zy*+iarKdHOG!TD8tj}R3FrlST!PENmXZIx-8MFiI!Z+Z^CWd@8ZNmnKh}0DF{x<1PIifm zuUBqR;`i+5hp(i;2L|Q|com>ab>7g0qeC|?92`QI5vsfRjyLqr`dqap|4frVI8UC` z#dl$ur1UuAZMkMcD$^jcPoq@ubGJb?#Ah4zzr7R}5lu z#}sGdyV{KEmkHg28L70*0^f5xFe7lEPPCQ7&A$I-BpGQ{n+5u1InYudb&P=oAiGeG zpl_#3=6($##2~IoMoc?mTbcsMQZjdf5I=n37|<@Dl?%|01=beQQ`gQFbTh^|80k6V z@6!LC>lxoX?73%cpSSisu@{Wd|BE%(zHh7ufqh@H2nphcmwi`W?_4 uKoOvqfkNMnli5+f_33BppV`@84{sW^KJ)XPTZ2EZ-?>$lK>a3wlK%lX8vRuO literal 0 HcmV?d00001 diff --git a/data/dip_plugin3_bin b/data/dip_plugin3_bin new file mode 100644 index 0000000000000000000000000000000000000000..55218bb150b32a1df0fb2c9aa7c5c46eb7cbecaf GIT binary patch literal 3352 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 literal 0 HcmV?d00001 diff --git a/data/dip_plugin4_bin_31 b/data/dip_plugin4_bin_31 new file mode 100644 index 0000000000000000000000000000000000000000..23236f0951fc2295b6ea3f1323596043e68e44a2 GIT binary patch literal 3080 zcmcgueN0=|6~FKK**GS~1gDEJxAfW1v<`-12eKuhsvH}xfw#~PNYj->eFmyshg4&Y zI#^pRW?Q6ml^TL4H8D|=!lX50)t(fP*3GNf)=FEOMW(0-)K2x3n3qDAdEJx`LgJnG z>~sOG^uO6U@7#ONJ->U-x#!&XtbLbum((GKcmCya4+W?XtLoBhDSHx&YZui4~eXkoxQbqln7 zvjJr9VBWz4Plz0~C5m$xW+WjX48%`l$kXHrvWql~XH4&txVTY%xdPz=TttqM17w)I zK{_OX1f?!%l(b0~@iro2UswRl5km2Hyal{7Y-QYN$_PF_LK`X+Pi#`j}CIjbVKZBG=)+> zFg;F=2~j6v%1F$ajWN0p#tcJ<98VR*bq(hlpPG9(uD6{tebjIwt|J}x8PdYG#o0i7 zc8`E!dKWs)Iin3v<89c-lw{ELZ&#tI8aGN-4{9G_$E&+4w4J|{yiBzj0dvIm!tX5buj;X_$#IEq5OWOBlbxh4xX(;v~_%LBF! zTRfukpeYLt!m|MLcr(OcmYE!4eqN?rRYw_g<74HYWYXG~fwO(2Okgl4)yo4~9uCoq z9%Iso`3zc`fqd5khE_X~(TaDejeZLveKs=6yh+Qj+hL@8X*o!`MHA=pmj~-w*GW|% z=cGE2He;`Y1Kz0O@JN}{_H-kZ%_8JGGEv;T5p(nv`W|>+2!5AP5uSybAHQ;=k93g{ zaeW}3H03%)lfR{W?f~Czyr^6e4Nj)l-roT>S6~;DvY3l$A3imz-Xl=Adk7hy#9Tj; z2XGPIh5p%$o!c0n4WhaMd%d7WmE0#j*tj z!dUVsembUH*2ZJXjurb=f06NT6Dcjye!yaLbp!8^L8SLVbw&}iTvn@}R?;}0B2l=1 ze_Dx>wGJI-{U(944`Z9(*eK2V;MVZD;QV%%*D--JvrCOXPV%J7US>Q^PN3hCIAn4I zS8Le9`hxbL3ExA4!L2sC1EE)Z%%dL$-kM%&Xm6ZK8c49zR>$-jyiDWIW|tB;cXLA} zG9g4H<$dsT8v4HXn;Wo!^70R{xsweWx|#4!N5i%3atv|WZG&x!5Q=xfX3<(TK z;&8g0nyKYuG7A;1-Rsc%RY2Z7q>Y`($cJHtWk}~{XT@DLTJ^THK1jXLgDOI}r3#Hc zdEB9ndn8pF&~!fP#@iw1a7Qlhq3*LfC+GFU_%T}P;;^-rm#nl>Fi35*($N5OzZ9b$ z>0|1KHTBd(y-qfUn!hF>n1^U(-ohj8)yDbNvOd?1JQMIM*TuqCqvR2oK!;SToP&P) zO_Ndc>psb=jBDqp#bxbVT!z>fh|v$@e2olF#Plj#ct^^767(ayc)*XO;Y9UT(P~pGU@Gb^m!(kjZh~V4ll)p3L*So_kcC z18w>TLg&@-_)8Ozxm)E4Ht|x(sJ_wZVa$j{Asc)LJ%<^d`(!LseD~X1G}5w=&DxhL z2HFUujxi7q9bhrrjXUs2Y+OP>M#38;S!dV3AsT6UmIJ9D<__Wxp};q2Dj?<+9Z zvQ59b5_`p7zh0?Z3+Ml(TjAfA#GO91hTp4n^F?c{{U7-A25a9u3+J>1bQNeGXa?x~ vSL3ALuIYZ}do|A=?5u&e1$?@oJM%4f#s|A2->Es+t!kk^Q$SE}i^=~0y#~&j literal 0 HcmV?d00001 diff --git a/data/dip_plugin_249 b/data/dip_plugin_249 new file mode 100644 index 0000000000000000000000000000000000000000..8683ba11927b90cc6d6f31e08eb8e9a5c406ac9e GIT binary patch literal 5264 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 literal 0 HcmV?d00001 diff --git a/data/ehcmodule3_elf b/data/ehcmodule3_elf new file mode 100644 index 0000000000000000000000000000000000000000..81c48af7b7cb26ada9b79070a6345394077decf8 GIT binary patch literal 22264 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 literal 0 HcmV?d00001 diff --git a/data/ehcmodule4_elf b/data/ehcmodule4_elf new file mode 100644 index 0000000000000000000000000000000000000000..4c4c4231321931af1a5c0d8bdff5305a2c3b4c60 GIT binary patch literal 32400 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�A~PD^<7Ez2YgwjlCg2`1OK=XKlFuZVKYB;+C$7 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 z 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 literal 0 HcmV?d00001 diff --git a/data/gc.wav b/data/gc.wav new file mode 100644 index 0000000000000000000000000000000000000000..9ffdfd6d51189da9b416c3707963d115ce886f9c GIT binary patch literal 214316 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 literal 0 HcmV?d00001 diff --git a/data/gui_font b/data/gui_font new file mode 100644 index 0000000000000000000000000000000000000000..712b502e54a18591bb1245fada1ec60f02f1a546 GIT binary patch literal 26189 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 literal 0 HcmV?d00001 diff --git a/data/hourglass b/data/hourglass new file mode 100644 index 0000000000000000000000000000000000000000..f1315a6f48a371b24ccbf7d480621dc45e5530f5 GIT binary patch literal 5593 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 L$4X@i=E465kCSk^ literal 0 HcmV?d00001 diff --git a/data/intro3 b/data/intro3 new file mode 100644 index 0000000000000000000000000000000000000000..ff66dd355ab149dc2c04f76f8f135e6cd59dd431 GIT binary patch literal 20761 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 Z^YgBl;hy^~Gzz|M-&B+TAZr}_KLCY1vlajV literal 0 HcmV?d00001 diff --git a/data/intro4.jpg b/data/intro4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b33f93f42889313e632273e0cc0b7202654ad869 GIT binary patch literal 16723 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 literal 0 HcmV?d00001 diff --git a/data/intro41 b/data/intro41 new file mode 100644 index 0000000000000000000000000000000000000000..eaaacbfae5ac97d5025f973c5d9ba890d97a1eaf GIT binary patch literal 20776 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 P00000NkvXXu0mjf&nUi> literal 0 HcmV?d00001 diff --git a/data/nocover b/data/nocover new file mode 100644 index 0000000000000000000000000000000000000000..89f7983fc5748e1df567d73dafab2773e2730171 GIT binary patch literal 4997 zcmai0XEfa1yZudw(MduOC4^|f=%V))E&Ax)5D_96-RLDk^p-)2&Jbl}lw|ZWy681Q zvi*aT00QQa#xN7(t()?MrJpJ~@rVo^ zFC%(Vb|aPN2)a~mehxC)2k{)QXXy>wuc@mOGxrt66D21jlIRTuC=yL!H?2`NR8b9n=_ zr1j1m&(1kPz;T$I+`T(k@J9d?FhM~IVD<8`!Ypx@G}|h<)*wVQ2^r3=VowQ7XWG!zEq~aFYe9 zM$8h`0jMxQ=c>~s4a^7v!rJDpT0m_RfE}i#r~^nT0b#@V7(ReF7_jf-@+F7G%xu3oH_Pxie+wZ?g7Ca} zb9a~6&6c9bkMP{Jjz*~wDB7AcKlJ{~jr!}CnAzF&wY8ZCT`E=%1LmO@uns${`T66M zFu60r(aOiKToIx+5t_uuD;(#WD67rv!vo;6Wip0Q5c>C$$@K>6ub4X-~< zR?AB|6fM4!ppwSdeRurQ`RlU4DUrgm1W@}(0Ti4nSZ3oy^h<|_7V3-rN&tX^7O(ct zyre`3x2P{8@XI~LbIqc=0K!!}?I{4*s&NUK4Ad+3k^+EQQ3QXjGV{-NMgc4dOZ(M@ zc8W85$!Ha>t_~Gi6>?_;i;q2DRip}U>`*N$zdhu$6011Y&@DFIn_8&D^aHiLH_dMc z(t>v0)(A3UM2r$m*iE%q z59EEWNXJ=gG^fh;0iuy-v8|dS84-1c@}DUiRWW5!x1u%PP>eeJisYr-EXp7CX}Bey zB>8${w9<`cE=57LR~Wm=2o~#lEG*R1sq+5509m>d?){xA%F3QwwPsaBEeS_XBk$qF z*CW)iWRk>i6?O+UWi6;KRDVXRjGcu`{MsCu6d6&pVF%Bh^fH|W?&fPJU5t*RB5_bH z0nVxGDK{Xr!SSkHQe4*)R7H6(FRy67e`hj&eOzPwi4o_PKzY8n)-BHT{yArlIFK*;ul-nAIqBKm!j91IaK4(>$Rv;hl7&9Bg%4N&qDqv+^ zCW`OEEY1^1EIM^N4OHJ57QMGyZS7&FaAK+t5a95js*LrEkm%xmKQtTS&)<@B&f4-^ zzM}lKw5r$xG4pz(!wgqN@M_M}<5#}=WE;Gt*@t0?OLTjDU-_6z#C0(uF4i6uS=m|8 zO07!6O3j@%Nff>mS<T(`tiFF967 z@B9x#Rhv#F)nbQChwLxWshBajx04z#G-#6Cd#qQvRy}(>d){)BaC30$=xyum<{9Z> zO881-b!ByXhX#k<3{B-qH`!YTSPG!a&>vj1>((qwEOybYPhu>k zEsCM0mNxYo)yV3*Q!UjkrPoR^`sw&6l9@BV>Mkeh0p6zGhc) z*L^o`p>R;Vkgvc(?Ad5%pX!R?`j{d`VWzOha#fgtszC&-af1`$1W7foo~~^whF1_A z_81g?DfkqwDy+&$Er~8!*bPcyoBEn?2Ay67b}`GU;hdMwmqC|fKq=xb2u73;p&Vg# z<@7ixgyU1PqRhO@uew>ha#9gSnO-?1vO03Ty_&x{Gv5Lvw@z7giBH0q~u?5v<%WA*XvecIHVIl_7HG&T$_!TOI9?8u~ zHH$YZ_6WIJ3{FCkQ0>P$EYd>!D9I;}KQ5o)597Wfzf!CQGvq+}Y`gGpg47Z2AXU{CYuY-&0#9 z0=c@yiUrm&(LCiZTXm^)7xQHbZsz&3dy2}-w24HerM`TsQo*5@j)rZ+zPbA2s$62~ zF1nvQZ-TI8g8Mz{UsheU5F|&dlx&(<@=o)E>tHVCuAhby2Bfnp3+@f!2kQssUrN2& zE4;*C@3{UY4P6|oV=l-F{nl3XNV-n$Y2ZBhD*e@av1TMyavu`W{GO8rm3Z@PR9G8# zke)|`ZTmoEdn3qWhr*P6JtlN#VH$5^0EJjAH#+=|MKUYUCnTci<{7f-!)JIG0@j)1 zyaqnyE@U*6S9W2XM_k8Ux4tb27FbbdRb+q5rhY;-JJ&$9Y}SB3`lUV($sXdJZ;3*y zV%~m|`_SrA+CMxbt7!7h}eb1-(UZQcLb1)}0dK&Y2l673(#3*Iz-i{`9@9mEl z$UV3ql_Ztvi&s5gIla{a*R}{`%LFzmnK}QY;Hod+iQE3HFjmMTmAr#IjeO(0Q!7WS zW`jY6v8nB)pfk<++cdAl^f;pIm( zUVF{7zF)@GFQ0G7w#p7$AAImVsGDEEVgA89uSUksbK7@Y?jn7{ptt2!OCAbi?Sa`1 zYHJL3C2TPlNDVLkYzzEx^KxpkKw5sW?J&rT&`mg6zPT9Y!*f22Yng@f>{hoLwA%et z!Yj|JF2+qNT3sy6>@9>p!orIV#}+gRBr#DV7pX@nG^$rqa#L`zRIxh+_Z6h2zbg8i zZ=IE@jyR8S7cgABxxk?qZbHJ2CwHcY$8yGAXCGvzfAS0|KHtXfFxAZWbo8)&><yUKQbOIJxymyProE4$~io95)+*NlQ7A=wJe?i z0Cnfz0`+>8IRgOcg0`BnNx&Q)S?<3e#~3^LidJV~<5sAU?d8-8fX)H%2Mk`<>S4sZ zw4NUGYaC+D4qAy%WIcsZBm)6-kE z@V^AD+>;|tpgYI2$$DcHR{Y7)TE1BIl#cJg-_=_i4ynQvlVo?JdLyMCL3MCy<14cz z-@_(Lc%{*mO3#mB0s*3Iuz^ESh+-i*Kw+5B1uRLQkXL#7Yvx}{c=zc@bb&&Qp9hjS zEw`N(hNd4eYnk8GE2*f6Z^!Y>h^hGGl>`>(oJwrEWvn83d~^LD5}{JKMJf+&YR@&E z&wLI51%P@`nJiz*?jf>MyPUFJi5THjD^3sU-USE~1qd3O1?y5?*{9C235GL_1GcR7 zirE8&3mDLzxE`pL&pq46shuYdAMQ;QkL1386yVJd6k1bC{*QzH@e!8vUjQ`z;yqvU zWQ=*!i&@<#x3g6D=owg7KKh}8`LxsamOtHwclK^_FP**n^0-HV<>{iH zJvoHtXxDK&3E_NjxjVp;vWrDeI^~ssYUhW)!b$es*MEH{+0PC2TZ4#!_N#-!!@>Kx zM<*vt&xA?#MceONYErFq&-DU5@$iSfHkqeSxPJZk@#Ew~dap@uq6t!xLCD z^Z7Qn_QCJpx8{p(2+3r$`mOt52DM5#le&Q2my}1J+S-sS7JyuI;M#mQ5Muv_u=IN! zU5r=?uoF=DN(4dH`OlPpI*IU=f3Uxl7-B1ncyCW^Z!L~6&><*!w1MO_Aldh>Lezh)|VGun0rn+%& zZ?Cg5`cO`BvjQ6omWxGg#y=0zuFDW{T_X>78XSXre$vFIOFm3=kQD?BaJi*NoH`BaWPno>|gHw>-7KCUPDac zS!iui1I{Pn4X;e8^erdntEp2#ZSM4X6CaM+y}Z0=ZF(YEZHi4yP2nxVYYhmZH-y&a z9U(_=VJ8x#aDPX|r8iF@K{{;9<_B0 z-2LuIX$fy~`@+PE>Jj-R+yX$dagnDv5Xu>^cXs#yq79ND=v!)P_MboC8jxuxhN!sg z;Fp4TWNzG%W?QH@otxzptWJt>v_NkwqCf#fm5V78oi-aHtxwwhXsMSUjmqxOyiTSx z4SVmK?0eEJ>K-}IU(X-406CU%ow=W;cic;<-_(-qV(q2jV!4`n+v|r-CxJh~wZ7c|Yb1vu zxA}JQ!TRX$DDXZ%iOjyaCOlu@Iu~%8lZf!YVarf0D!i-z8Xr!KJq^$Vt7XY literal 0 HcmV?d00001 diff --git a/data/nocover_full b/data/nocover_full new file mode 100644 index 0000000000000000000000000000000000000000..7a84ee1af31b2f6055204546568af451c5730b97 GIT binary patch literal 17500 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 is the containing + * directory inode, and is the inode number itself. If + * is zero, then ext2fs_get_pathname will return pathname + * of the the directory . + * + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct get_pathname_struct { + ext2_ino_t search_ino; + ext2_ino_t parent; + char *name; + errcode_t errcode; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int get_pathname_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct get_pathname_struct *gp; + errcode_t retval; + + gp = (struct get_pathname_struct *) priv_data; + + if (((dirent->name_len & 0xFF) == 2) && + !strncmp(dirent->name, "..", 2)) + gp->parent = dirent->inode; + if (dirent->inode == gp->search_ino) { + retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, + &gp->name); + if (retval) { + gp->errcode = retval; + return DIRENT_ABORT; + } + strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); + gp->name[dirent->name_len & 0xFF] = '\0'; + return DIRENT_ABORT; + } + return 0; +} + +static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, + ext2_ino_t ino, int maxdepth, + char *buf, char **name) +{ + struct get_pathname_struct gp; + char *parent_name, *ret; + errcode_t retval; + + if (dir == ino) { + retval = ext2fs_get_mem(2, name); + if (retval) + return retval; + strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); + return 0; + } + + if (!dir || (maxdepth < 0)) { + retval = ext2fs_get_mem(4, name); + if (retval) + return retval; + strcpy(*name, "..."); + return 0; + } + + gp.search_ino = ino; + gp.parent = 0; + gp.name = 0; + gp.errcode = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); + if (retval) + goto cleanup; + if (gp.errcode) { + retval = gp.errcode; + goto cleanup; + } + + retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, + buf, &parent_name); + if (retval) + goto cleanup; + if (!ino) { + *name = parent_name; + return 0; + } + + if (gp.name) + retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, + &ret); + else + retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); + if (retval) + goto cleanup; + + ret[0] = 0; + if (parent_name[1]) + strcat(ret, parent_name); + strcat(ret, "/"); + if (gp.name) + strcat(ret, gp.name); + else + strcat(ret, "???"); + *name = ret; + ext2fs_free_mem(&parent_name); + retval = 0; + +cleanup: + if (gp.name) + ext2fs_free_mem(&gp.name); + return retval; +} + +errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + if (dir == ino) + ino = 0; + retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); + ext2fs_free_mem(&buf); + return retval; + +} diff --git a/lib/libext2fs/orig/getsectsize.c b/lib/libext2fs/orig/getsectsize.c new file mode 100644 index 0000000..64f42a6 --- /dev/null +++ b/lib/libext2fs/orig/getsectsize.c @@ -0,0 +1,91 @@ +/* + * getsectsize.c --- get the sector size of a device. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#include +#endif + +#if defined(__linux__) && defined(_IO) +#if !defined(BLKSSZGET) +#define BLKSSZGET _IO(0x12,104)/* get block device sector size */ +#endif +#if !defined(BLKPBSZGET) +#define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ +#endif +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Returns the logical sector size of a device + */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) +{ + int fd; + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef BLKSSZGET + if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { + close(fd); + return 0; + } +#endif + *sectsize = 0; + close(fd); + return 0; +} + +/* + * Returns the physical sector size of a device + */ +errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize) +{ + int fd; + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, sectsize) >= 0) { + close(fd); + return 0; + } +#endif + *sectsize = 0; + close(fd); + return 0; +} diff --git a/lib/libext2fs/orig/getsize.c b/lib/libext2fs/orig/getsize.c new file mode 100644 index 0000000..56ec4b6 --- /dev/null +++ b/lib/libext2fs/orig/getsize.c @@ -0,0 +1,311 @@ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * Windows version of ext2fs_get_device_size by Chris Li, VMware. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#include +#endif +#ifdef HAVE_SYS_DISK_H +#ifdef HAVE_SYS_QUEUE_H +#include /* for LIST_HEAD */ +#endif +#include +#endif +#ifdef __linux__ +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#include + +#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +#endif + +#ifdef APPLE_DARWIN +#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif /* APPLE_DARWIN */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__CYGWIN__) || defined (WIN32) +#include "windows.h" +#include "winioctl.h" + +#if (_WIN32_WINNT >= 0x0500) +#define HAVE_GET_FILE_SIZE_EX 1 +#endif + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + HANDLE dev; + PARTITION_INFORMATION pi; + DISK_GEOMETRY gi; + DWORD retbytes; +#ifdef HAVE_GET_FILE_SIZE_EX + LARGE_INTEGER filesize; +#else + DWORD filesize; +#endif /* HAVE_GET_FILE_SIZE_EX */ + + dev = CreateFile(file, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE , + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (dev == INVALID_HANDLE_VALUE) + return EBADF; + if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, + &pi, sizeof(PARTITION_INFORMATION), + &pi, sizeof(PARTITION_INFORMATION), + &retbytes, NULL)) { + + *retblocks = pi.PartitionLength.QuadPart / blocksize; + + } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, + &gi, sizeof(DISK_GEOMETRY), + &gi, sizeof(DISK_GEOMETRY), + &retbytes, NULL)) { + + *retblocks = gi.BytesPerSector * + gi.SectorsPerTrack * + gi.TracksPerCylinder * + gi.Cylinders.QuadPart / blocksize; + +#ifdef HAVE_GET_FILE_SIZE_EX + } else if (GetFileSizeEx(dev, &filesize)) { + *retblocks = filesize.QuadPart / blocksize; + } +#else + } else { + filesize = GetFileSize(dev, NULL); + if (INVALID_FILE_SIZE != filesize) { + *retblocks = filesize / blocksize; + } + } +#endif /* HAVE_GET_FILE_SIZE_EX */ + + CloseHandle(dev); + return 0; +} + +#else + +static int valid_offset (int fd, ext2_loff_t offset) +{ + char ch; + + if (ext2fs_llseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +/* + * Returns the number of blocks in a partition + */ +errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks) +{ + int fd, rc = 0; +#ifdef __linux__ + struct utsname ut; +#endif + unsigned long long size64; + ext2_loff_t high, low; +#ifdef FDGETPRM + struct floppy_struct this_floppy; +#endif +#ifdef HAVE_SYS_DISKLABEL_H + int part; + struct disklabel lab; + struct partition *pp; + char ch; +#endif /* HAVE_SYS_DISKLABEL_H */ + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { + *retblocks = size64 / (blocksize / 512); + goto out; + } +#endif + +#ifdef BLKGETSIZE64 +#ifdef __linux__ + if ((uname(&ut) == 0) && + ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.'))) + valid_blkgetsize64 = 0; +#endif + if (valid_blkgetsize64 && + ioctl(fd, BLKGETSIZE64, &size64) >= 0) { + *retblocks = size64 / blocksize; + goto out; + } +#endif + +#ifdef BLKGETSIZE + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + *retblocks = size / (blocksize / 512); + goto out; + } +#endif + +#ifdef FDGETPRM + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + *retblocks = this_floppy.size / (blocksize / 512); + goto out; + } +#endif + +#ifdef HAVE_SYS_DISKLABEL_H +#if defined(DIOCGMEDIASIZE) + { + off_t ms; + u_int bs; + if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { + *retblocks = ms / blocksize; + goto out; + } + } +#elif defined(DIOCGDINFO) + /* old disklabel interface */ + part = strlen(file) - 1; + if (part >= 0) { + ch = file[part]; + if (isdigit(ch)) + part = 0; + else if (ch >= 'a' && ch <= 'h') + part = ch - 'a'; + else + part = -1; + } + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) { + *retblocks = pp->p_size / (blocksize / 512); + goto out; + } + } +#endif /* defined(DIOCG*) */ +#endif /* HAVE_SYS_DISKLABEL_H */ + + { +#ifdef HAVE_FSTAT64 + struct stat64 st; + if (fstat64(fd, &st) == 0) +#else + struct stat st; + if (fstat(fd, &st) == 0) +#endif + if (S_ISREG(st.st_mode)) { + *retblocks = st.st_size / blocksize; + goto out; + } + } + + /* + * OK, we couldn't figure it out by using a specialized ioctl, + * which is generally the best way. So do binary search to + * find the size of the partition. + */ + low = 0; + for (high = 1024; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const ext2_loff_t mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + size64 = low + 1; + *retblocks = size64 / blocksize; +out: + close(fd); + return rc; +} + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + errcode_t retval; + blk64_t blocks; + + retval = ext2fs_get_device_size2(file, blocksize, &blocks); + if (retval) + return retval; + if (blocks >= (1ULL << 32)) + return EFBIG; + *retblocks = (blk_t) blocks; + return 0; +} + +#endif /* WIN32 */ + +#ifdef DEBUG +int main(int argc, char **argv) +{ + blk_t blocks; + int retval; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + retval = ext2fs_get_device_size(argv[1], 1024, &blocks); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_get_device_size"); + exit(1); + } + printf("Device %s has %u 1k blocks.\n", argv[1], blocks); + exit(0); +} +#endif diff --git a/lib/libext2fs/orig/i_block.c b/lib/libext2fs/orig/i_block.c new file mode 100644 index 0000000..39d93ee --- /dev/null +++ b/lib/libext2fs/orig/i_block.c @@ -0,0 +1,89 @@ +/* + * i_block.c --- Manage the i_block field for i_blocks + * + * Copyright (C) 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks) +{ + unsigned long long b = inode->i_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; + + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + b += num_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + else if (b > 0xFFFFFFFF) + return EOVERFLOW; + inode->i_blocks = b & 0xFFFFFFFF; + return 0; +} + +errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks) +{ + unsigned long long b = inode->i_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; + + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + if (num_blocks > b) + return EOVERFLOW; + + b -= num_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + inode->i_blocks = b & 0xFFFFFFFF; + return 0; +} + +errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b) +{ + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + b *= fs->blocksize / 512; + + inode->i_blocks = b & 0xFFFFFFFF; + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + else if (b >> 32) + return EOVERFLOW; + return 0; +} diff --git a/lib/libext2fs/orig/icount.c b/lib/libext2fs/orig/icount.c new file mode 100644 index 0000000..43cc53e --- /dev/null +++ b/lib/libext2fs/orig/icount.c @@ -0,0 +1,862 @@ +/* + * icount.c --- an efficient inode count abstraction + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "tdb.h" + +/* + * The data storage strategy used by icount relies on the observation + * that most inode counts are either zero (for non-allocated inodes), + * one (for most files), and only a few that are two or more + * (directories and files that are linked to more than one directory). + * + * Also, e2fsck tends to load the icount data sequentially. + * + * So, we use an inode bitmap to indicate which inodes have a count of + * one, and then use a sorted list to store the counts for inodes + * which are greater than one. + * + * We also use an optional bitmap to indicate which inodes are already + * in the sorted list, to speed up the use of this abstraction by + * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, + * so this extra bitmap avoids searching the sorted list to see if a + * particular inode is on the sorted list already. + */ + +struct ext2_icount_el { + ext2_ino_t ino; + __u32 count; +}; + +struct ext2_icount { + errcode_t magic; + ext2fs_inode_bitmap single; + ext2fs_inode_bitmap multiple; + ext2_ino_t count; + ext2_ino_t size; + ext2_ino_t num_inodes; + ext2_ino_t cursor; + struct ext2_icount_el *list; + struct ext2_icount_el *last_lookup; + char *tdb_fn; + TDB_CONTEXT *tdb; +}; + +/* + * We now use a 32-bit counter field because it doesn't cost us + * anything extra for the in-memory data structure, due to alignment + * padding. But there's no point changing the interface if most of + * the time we only care if the number is bigger than 65,000 or not. + * So use the following translation function to return a 16-bit count. + */ +#define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x)) + +void ext2fs_free_icount(ext2_icount_t icount) +{ + if (!icount) + return; + + icount->magic = 0; + if (icount->list) + ext2fs_free_mem(&icount->list); + if (icount->single) + ext2fs_free_inode_bitmap(icount->single); + if (icount->multiple) + ext2fs_free_inode_bitmap(icount->multiple); + if (icount->tdb) + tdb_close(icount->tdb); + if (icount->tdb_fn) { + unlink(icount->tdb_fn); + free(icount->tdb_fn); + } + + ext2fs_free_mem(&icount); +} + +static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + + *ret = 0; + + retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); + if (retval) + return retval; + memset(icount, 0, sizeof(struct ext2_icount)); + + retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single); + if (retval) + goto errout; + + if (flags & EXT2_ICOUNT_OPT_INCREMENT) { + retval = ext2fs_allocate_inode_bitmap(fs, 0, + &icount->multiple); + if (retval) + goto errout; + } else + icount->multiple = 0; + + icount->magic = EXT2_ET_MAGIC_ICOUNT; + icount->num_inodes = fs->super->s_inodes_count; + + *ret = icount; + return 0; + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +struct uuid { + __u32 time_low; + __u16 time_mid; + __u16 time_hi_and_version; + __u16 clock_seq; + __u8 node[6]; +}; + +static void unpack_uuid(void *in, struct uuid *uu) +{ + __u8 *ptr = in; + __u32 tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +static void uuid_unparse(void *uu, char *out) +{ + struct uuid uuid; + + unpack_uuid(uu, &uuid); + sprintf(out, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, + int flags, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + char *fn, uuid[40]; + int fd; + + retval = alloc_icount(fs, flags, &icount); + if (retval) + return retval; + + retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn); + if (retval) + goto errout; + uuid_unparse(fs->super->s_uuid, uuid); + sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid); + fd = mkstemp(fn); + + icount->tdb_fn = fn; + icount->tdb = tdb_open(fn, 0, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (icount->tdb) { + close(fd); + *ret = icount; + return 0; + } + + retval = errno; + close(fd); + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + size_t bytes; + ext2_ino_t i; + + if (hint) { + EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); + if (hint->size > size) + size = (size_t) hint->size; + } + + retval = alloc_icount(fs, flags, &icount); + if (retval) + return retval; + + if (size) { + icount->size = size; + } else { + /* + * Figure out how many special case inode counts we will + * have. We know we will need one for each directory; + * we also need to reserve some extra room for file links + */ + retval = ext2fs_get_num_dirs(fs, &icount->size); + if (retval) + goto errout; + icount->size += fs->super->s_inodes_count / 50; + } + + bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); +#if 0 + printf("Icount allocated %u entries, %d bytes.\n", + icount->size, bytes); +#endif + retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el), + &icount->list); + if (retval) + goto errout; + memset(icount->list, 0, bytes); + + icount->count = 0; + icount->cursor = 0; + + /* + * Populate the sorted list with those entries which were + * found in the hint icount (since those are ones which will + * likely need to be in the sorted list this time around). + */ + if (hint) { + for (i=0; i < hint->count; i++) + icount->list[i].ino = hint->list[i].ino; + icount->count = hint->count; + } + + *ret = icount; + return 0; + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret) +{ + return ext2fs_create_icount2(fs, flags, size, 0, ret); +} + +/* + * insert_icount_el() --- Insert a new entry into the sorted list at a + * specified position. + */ +static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int pos) +{ + struct ext2_icount_el *el; + errcode_t retval; + ext2_ino_t new_size = 0; + int num; + + if (icount->last_lookup && icount->last_lookup->ino == ino) + return icount->last_lookup; + + if (icount->count >= icount->size) { + if (icount->count) { + new_size = icount->list[(unsigned)icount->count-1].ino; + new_size = (ext2_ino_t) (icount->count * + ((float) icount->num_inodes / new_size)); + } + if (new_size < (icount->size + 100)) + new_size = icount->size + 100; +#if 0 + printf("Reallocating icount %u entries...\n", new_size); +#endif + retval = ext2fs_resize_mem((size_t) icount->size * + sizeof(struct ext2_icount_el), + (size_t) new_size * + sizeof(struct ext2_icount_el), + &icount->list); + if (retval) + return 0; + icount->size = new_size; + } + num = (int) icount->count - pos; + if (num < 0) + return 0; /* should never happen */ + if (num) { + memmove(&icount->list[pos+1], &icount->list[pos], + sizeof(struct ext2_icount_el) * num); + } + icount->count++; + el = &icount->list[pos]; + el->count = 0; + el->ino = ino; + icount->last_lookup = el; + return el; +} + +/* + * get_icount_el() --- given an inode number, try to find icount + * information in the sorted list. If the create flag is set, + * and we can't find an entry, create one in the sorted list. + */ +static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int create) +{ + float range; + int low, high, mid; + ext2_ino_t lowval, highval; + + if (!icount || !icount->list) + return 0; + + if (create && ((icount->count == 0) || + (ino > icount->list[(unsigned)icount->count-1].ino))) { + return insert_icount_el(icount, ino, (unsigned) icount->count); + } + if (icount->count == 0) + return 0; + + if (icount->cursor >= icount->count) + icount->cursor = 0; + if (ino == icount->list[icount->cursor].ino) + return &icount->list[icount->cursor++]; +#if 0 + printf("Non-cursor get_icount_el: %u\n", ino); +#endif + low = 0; + high = (int) icount->count-1; + while (low <= high) { +#if 0 + mid = (low+high)/2; +#else + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = icount->list[low].ino; + highval = icount->list[high].ino; + + if (ino < lowval) + range = 0; + else if (ino > highval) + range = 1; + else { + range = ((float) (ino - lowval)) / + (highval - lowval); + if (range > 0.9) + range = 0.9; + if (range < 0.1) + range = 0.1; + } + mid = low + ((int) (range * (high-low))); + } +#endif + if (ino == icount->list[mid].ino) { + icount->cursor = mid+1; + return &icount->list[mid]; + } + if (ino < icount->list[mid].ino) + high = mid-1; + else + low = mid+1; + } + /* + * If we need to create a new entry, it should be right at + * low (where high will be left at low-1). + */ + if (create) + return insert_icount_el(icount, ino, low); + return 0; +} + +static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino, + __u32 count) +{ + struct ext2_icount_el *el; + TDB_DATA key, data; + + if (icount->tdb) { + key.dptr = (unsigned char *) &ino; + key.dsize = sizeof(ext2_ino_t); + data.dptr = (unsigned char *) &count; + data.dsize = sizeof(__u32); + if (count) { + if (tdb_store(icount->tdb, key, data, TDB_REPLACE)) + return tdb_error(icount->tdb) + + EXT2_ET_TDB_SUCCESS; + } else { + if (tdb_delete(icount->tdb, key)) + return tdb_error(icount->tdb) + + EXT2_ET_TDB_SUCCESS; + } + return 0; + } + + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + + el->count = count; + return 0; +} + +static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino, + __u32 *count) +{ + struct ext2_icount_el *el; + TDB_DATA key, data; + + if (icount->tdb) { + key.dptr = (unsigned char *) &ino; + key.dsize = sizeof(ext2_ino_t); + + data = tdb_fetch(icount->tdb, key); + if (data.dptr == NULL) { + *count = 0; + return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; + } + + *count = *((__u32 *) data.dptr); + free(data.dptr); + return 0; + } + el = get_icount_el(icount, ino, 0); + if (!el) { + *count = 0; + return ENOENT; + } + + *count = el->count; + return 0; +} + +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) +{ + errcode_t ret = 0; + unsigned int i; + const char *bad = "bad icount"; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (icount->count > icount->size) { + fprintf(out, "%s: count > size\n", bad); + return EXT2_ET_INVALID_ARGUMENT; + } + for (i=1; i < icount->count; i++) { + if (icount->list[i-1].ino >= icount->list[i].ino) { + fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", + bad, i-1, icount->list[i-1].ino, + i, icount->list[i].ino); + ret = EXT2_ET_INVALID_ARGUMENT; + } + } + return ret; +} + +errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) +{ + __u32 val; + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + *ret = 1; + return 0; + } + if (icount->multiple && + !ext2fs_test_inode_bitmap2(icount->multiple, ino)) { + *ret = 0; + return 0; + } + get_inode_count(icount, ino, &val); + *ret = icount_16_xlate(val); + return 0; +} + +errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + __u32 curr_value; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + /* + * If the existing count is 1, then we know there is + * no entry in the list. + */ + if (set_inode_count(icount, ino, 2)) + return EXT2_ET_NO_MEMORY; + curr_value = 2; + ext2fs_unmark_inode_bitmap2(icount->single, ino); + } else if (icount->multiple) { + /* + * The count is either zero or greater than 1; if the + * inode is set in icount->multiple, then there should + * be an entry in the list, so we need to fix it. + */ + if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) { + get_inode_count(icount, ino, &curr_value); + curr_value++; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + } else { + /* + * The count was zero; mark the single bitmap + * and return. + */ + ext2fs_mark_inode_bitmap2(icount->single, ino); + if (ret) + *ret = 1; + return 0; + } + } else { + /* + * The count is either zero or greater than 1; try to + * find an entry in the list to determine which. + */ + get_inode_count(icount, ino, &curr_value); + curr_value++; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + } + if (icount->multiple) + ext2fs_mark_inode_bitmap2(icount->multiple, ino); + if (ret) + *ret = icount_16_xlate(curr_value); + return 0; +} + +errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + __u32 curr_value; + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + else { + set_inode_count(icount, ino, 0); + } + if (ret) + *ret = 0; + return 0; + } + + if (icount->multiple && + !ext2fs_test_inode_bitmap2(icount->multiple, ino)) + return EXT2_ET_INVALID_ARGUMENT; + + get_inode_count(icount, ino, &curr_value); + if (!curr_value) + return EXT2_ET_INVALID_ARGUMENT; + curr_value--; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + + if (curr_value == 1) + ext2fs_mark_inode_bitmap2(icount->single, ino); + if ((curr_value == 0) && icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + + if (ret) + *ret = icount_16_xlate(curr_value); + return 0; +} + +errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count) +{ + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (count == 1) { + ext2fs_mark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + return 0; + } + if (count == 0) { + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) { + /* + * If the icount->multiple bitmap is enabled, + * we can just clear both bitmaps and we're done + */ + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + } else + set_inode_count(icount, ino, 0); + return 0; + } + + if (set_inode_count(icount, ino, count)) + return EXT2_ET_NO_MEMORY; + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_mark_inode_bitmap2(icount->multiple, ino); + return 0; +} + +ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) +{ + if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) + return 0; + + return icount->size; +} + +#ifdef DEBUG + +ext2_filsys test_fs; +ext2_icount_t icount; + +#define EXIT 0x00 +#define FETCH 0x01 +#define STORE 0x02 +#define INCREMENT 0x03 +#define DECREMENT 0x04 + +struct test_program { + int cmd; + ext2_ino_t ino; + __u16 arg; + __u16 expected; +}; + +struct test_program prog[] = { + { STORE, 42, 42, 42 }, + { STORE, 1, 1, 1 }, + { STORE, 2, 2, 2 }, + { STORE, 3, 3, 3 }, + { STORE, 10, 1, 1 }, + { STORE, 42, 0, 0 }, + { INCREMENT, 5, 0, 1 }, + { INCREMENT, 5, 0, 2 }, + { INCREMENT, 5, 0, 3 }, + { INCREMENT, 5, 0, 4 }, + { DECREMENT, 5, 0, 3 }, + { DECREMENT, 5, 0, 2 }, + { DECREMENT, 5, 0, 1 }, + { DECREMENT, 5, 0, 0 }, + { FETCH, 10, 0, 1 }, + { FETCH, 1, 0, 1 }, + { FETCH, 2, 0, 2 }, + { FETCH, 3, 0, 3 }, + { INCREMENT, 1, 0, 2 }, + { DECREMENT, 2, 0, 1 }, + { DECREMENT, 2, 0, 0 }, + { FETCH, 12, 0, 0 }, + { EXIT, 0, 0, 0 } +}; + +struct test_program extended[] = { + { STORE, 1, 1, 1 }, + { STORE, 2, 2, 2 }, + { STORE, 3, 3, 3 }, + { STORE, 4, 4, 4 }, + { STORE, 5, 5, 5 }, + { STORE, 6, 1, 1 }, + { STORE, 7, 2, 2 }, + { STORE, 8, 3, 3 }, + { STORE, 9, 4, 4 }, + { STORE, 10, 5, 5 }, + { STORE, 11, 1, 1 }, + { STORE, 12, 2, 2 }, + { STORE, 13, 3, 3 }, + { STORE, 14, 4, 4 }, + { STORE, 15, 5, 5 }, + { STORE, 16, 1, 1 }, + { STORE, 17, 2, 2 }, + { STORE, 18, 3, 3 }, + { STORE, 19, 4, 4 }, + { STORE, 20, 5, 5 }, + { STORE, 21, 1, 1 }, + { STORE, 22, 2, 2 }, + { STORE, 23, 3, 3 }, + { STORE, 24, 4, 4 }, + { STORE, 25, 5, 5 }, + { STORE, 26, 1, 1 }, + { STORE, 27, 2, 2 }, + { STORE, 28, 3, 3 }, + { STORE, 29, 4, 4 }, + { STORE, 30, 5, 5 }, + { EXIT, 0, 0, 0 } +}; + +/* + * Setup the variables for doing the inode scan test. + */ +static void setup(void) +{ + errcode_t retval; + struct ext2_super_block param; + + initialize_ext2_error_table(); + + memset(¶m, 0, sizeof(param)); + ext2fs_blocks_count_set(¶m, 12000); + + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + test_io_manager, &test_fs); + if (retval) { + com_err("setup", retval, + "while initializing filesystem"); + exit(1); + } + retval = ext2fs_allocate_tables(test_fs); + if (retval) { + com_err("setup", retval, + "while allocating tables for test filesystem"); + exit(1); + } +} + +int run_test(int flags, int size, char *dir, struct test_program *prog) +{ + errcode_t retval; + ext2_icount_t icount; + struct test_program *pc; + __u16 result; + int problem = 0; + + if (dir) { + retval = ext2fs_create_icount_tdb(test_fs, dir, + flags, &icount); + if (retval) { + com_err("run_test", retval, + "while creating icount using tdb"); + exit(1); + } + } else { + retval = ext2fs_create_icount2(test_fs, flags, size, 0, + &icount); + if (retval) { + com_err("run_test", retval, "while creating icount"); + exit(1); + } + } + for (pc = prog; pc->cmd != EXIT; pc++) { + switch (pc->cmd) { + case FETCH: + printf("icount_fetch(%u) = ", pc->ino); + break; + case STORE: + retval = ext2fs_icount_store(icount, pc->ino, pc->arg); + if (retval) { + com_err("run_test", retval, + "while calling icount_store"); + exit(1); + } + printf("icount_store(%u, %u) = ", pc->ino, pc->arg); + break; + case INCREMENT: + retval = ext2fs_icount_increment(icount, pc->ino, 0); + if (retval) { + com_err("run_test", retval, + "while calling icount_increment"); + exit(1); + } + printf("icount_increment(%u) = ", pc->ino); + break; + case DECREMENT: + retval = ext2fs_icount_decrement(icount, pc->ino, 0); + if (retval) { + com_err("run_test", retval, + "while calling icount_decrement"); + exit(1); + } + printf("icount_decrement(%u) = ", pc->ino); + break; + } + retval = ext2fs_icount_fetch(icount, pc->ino, &result); + if (retval) { + com_err("run_test", retval, + "while calling icount_fetch"); + exit(1); + } + printf("%u (%s)\n", result, (result == pc->expected) ? + "OK" : "NOT OK"); + if (result != pc->expected) + problem++; + } + printf("icount size is %u\n", ext2fs_get_icount_size(icount)); + retval = ext2fs_icount_validate(icount, stdout); + if (retval) { + com_err("run_test", retval, "while calling icount_validate"); + exit(1); + } + ext2fs_free_icount(icount); + return problem; +} + + +int main(int argc, char **argv) +{ + int failed = 0; + + setup(); + printf("Standard icount run:\n"); + failed += run_test(0, 0, 0, prog); + printf("\nMultiple bitmap test:\n"); + failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog); + printf("\nResizing icount:\n"); + failed += run_test(0, 3, 0, extended); + printf("\nStandard icount run with tdb:\n"); + failed += run_test(0, 0, ".", prog); + printf("\nMultiple bitmap test with tdb:\n"); + failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog); + if (failed) + printf("FAILED!\n"); + return failed; +} +#endif diff --git a/lib/libext2fs/orig/imager.c b/lib/libext2fs/orig/imager.c new file mode 100644 index 0000000..5a6d0b9 --- /dev/null +++ b/lib/libext2fs/orig/imager.c @@ -0,0 +1,408 @@ +/* + * image.c --- writes out the critical parts of the filesystem as a + * flat file. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef HAVE_TYPE_SSIZE_T +typedef int ssize_t; +#endif + +/* + * This function returns 1 if the specified block is all zeros + */ +static int check_zero_block(char *buf, int blocksize) +{ + char *cp = buf; + int left = blocksize; + + while (left > 0) { + if (*cp++) + return 0; + left--; + } + return 1; +} + +/* + * Write the inode table out as a single block. + */ +#define BUF_BLOCKS 32 + +errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) +{ + unsigned int group, left, c, d; + char *buf, *cp; + blk64_t blk; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize * BUF_BLOCKS); + if (!buf) + return ENOMEM; + + for (group = 0; group < fs->group_desc_count; group++) { + blk = ext2fs_inode_table_loc(fs, (unsigned)group); + if (!blk) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + retval = io_channel_read_blk64(fs->io, blk, c, buf); + if (retval) + goto errout; + cp = buf; + while (c) { + if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { + d = c; + goto skip_sparse; + } + /* Skip zero blocks */ + if (check_zero_block(cp, fs->blocksize)) { + c--; + blk++; + left--; + cp += fs->blocksize; + lseek(fd, fs->blocksize, SEEK_CUR); + continue; + } + /* Find non-zero blocks */ + for (d=1; d < c; d++) { + if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) + break; + } + skip_sparse: + actual = write(fd, cp, fs->blocksize * d); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * d)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + blk += d; + left -= d; + cp += fs->blocksize * d; + c -= d; + } + } + } + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read in the inode table and stuff it into place + */ +errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + unsigned int group, c, left; + char *buf; + blk64_t blk; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize * BUF_BLOCKS); + if (!buf) + return ENOMEM; + + for (group = 0; group < fs->group_desc_count; group++) { + blk = ext2fs_inode_table_loc(fs, (unsigned)group); + if (!blk) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + actual = read(fd, buf, fs->blocksize * c); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * c)) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + retval = io_channel_write_blk64(fs->io, blk, c, buf); + if (retval) + goto errout; + + blk += c; + left -= c; + } + } + retval = ext2fs_flush_icache(fs); + +errout: + free(buf); + return retval; +} + +/* + * Write out superblock and group descriptors + */ +errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf, *cp; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize); + if (!buf) + return ENOMEM; + + /* + * Write out the superblock + */ + memset(buf, 0, fs->blocksize); + memcpy(buf, fs->super, SUPERBLOCK_SIZE); + actual = write(fd, buf, fs->blocksize); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) fs->blocksize) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + /* + * Now write out the block group descriptors + */ + cp = (char *) fs->group_desc; + actual = write(fd, cp, fs->blocksize * fs->desc_blocks); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read the superblock and group descriptors and overwrite them. + */ +errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf; + ssize_t actual, size; + errcode_t retval; + + size = fs->blocksize * (fs->group_desc_count + 1); + buf = malloc(size); + if (!buf) + return ENOMEM; + + /* + * Read it all in. + */ + actual = read(fd, buf, size); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != size) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + + /* + * Now copy in the superblock and group descriptors + */ + memcpy(fs->super, buf, SUPERBLOCK_SIZE); + + memcpy(fs->group_desc, buf + fs->blocksize, + fs->blocksize * fs->group_desc_count); + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Write the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) +{ + ext2fs_generic_bitmap bmap; + errcode_t err, retval; + ssize_t actual; + __u32 itr, cnt, size; + int c, total_size; + char buf[1024]; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->inode_map; + err = EXT2_ET_MAGIC_INODE_BITMAP; + itr = 1; + cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->block_map; + err = EXT2_ET_MAGIC_BLOCK_BITMAP; + itr = fs->super->s_first_data_block; + cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + total_size = size * fs->group_desc_count; + + while (cnt > 0) { + size = sizeof(buf); + if (size > (cnt >> 3)) + size = (cnt >> 3); + + retval = ext2fs_get_generic_bmap_range(bmap, itr, + size << 3, buf); + if (retval) + return retval; + + actual = write(fd, buf, size); + if (actual == -1) + return errno; + if (actual != (int) size) + return EXT2_ET_SHORT_READ; + + itr += size << 3; + cnt -= size << 3; + } + + size = total_size % fs->blocksize; + memset(buf, 0, sizeof(buf)); + if (size) { + size = fs->blocksize - size; + while (size) { + c = size; + if (c > (int) sizeof(buf)) + c = sizeof(buf); + actual = write(fd, buf, c); + if (actual == -1) + return errno; + if (actual != c) + return EXT2_ET_SHORT_WRITE; + size -= c; + } + } + return 0; +} + + +/* + * Read the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) +{ + ext2fs_generic_bitmap bmap; + errcode_t err, retval; + __u32 itr, cnt; + char buf[1024]; + unsigned int size; + ssize_t actual; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->inode_map; + err = EXT2_ET_MAGIC_INODE_BITMAP; + itr = 1; + cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->block_map; + err = EXT2_ET_MAGIC_BLOCK_BITMAP; + itr = fs->super->s_first_data_block; + cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + + while (cnt > 0) { + size = sizeof(buf); + if (size > (cnt >> 3)) + size = (cnt >> 3); + + actual = read(fd, buf, size); + if (actual == -1) + return errno; + if (actual != (int) size) + return EXT2_ET_SHORT_READ; + + retval = ext2fs_set_generic_bmap_range(bmap, itr, + size << 3, buf); + if (retval) + return retval; + + itr += size << 3; + cnt -= size << 3; + } + return 0; +} diff --git a/lib/libext2fs/orig/ind_block.c b/lib/libext2fs/orig/ind_block.c new file mode 100644 index 0000000..722d3bd --- /dev/null +++ b/lib/libext2fs/orig/ind_block.c @@ -0,0 +1,66 @@ +/* + * ind_block.c --- indirect block I/O routines + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ + errcode_t retval; +#ifdef WORDS_BIGENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && + (fs->io != fs->image_io)) + memset(buf, 0, fs->blocksize); + else { + retval = io_channel_read_blk(fs->io, blk, 1, buf); + if (retval) + return retval; + } +#ifdef WORDS_BIGENDIAN + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); +#endif + return 0; +} + +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ +#ifdef WORDS_BIGENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) + return 0; + +#ifdef WORDS_BIGENDIAN + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); +#endif + return io_channel_write_blk(fs->io, blk, 1, buf); +} + + diff --git a/lib/libext2fs/orig/initialize.c b/lib/libext2fs/orig/initialize.c new file mode 100644 index 0000000..4706773 --- /dev/null +++ b/lib/libext2fs/orig/initialize.c @@ -0,0 +1,447 @@ +/* + * initialize.c --- initialize a filesystem handle given superblock + * parameters. Used by mke2fs when initializing a filesystem. + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__linux__) && defined(EXT2_OS_LINUX) +#define CREATOR_OS EXT2_OS_LINUX +#else +#if defined(__GNU__) && defined(EXT2_OS_HURD) +#define CREATOR_OS EXT2_OS_HURD +#else +#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) +#define CREATOR_OS EXT2_OS_FREEBSD +#else +#if defined(LITES) && defined(EXT2_OS_LITES) +#define CREATOR_OS EXT2_OS_LITES +#else +#define CREATOR_OS EXT2_OS_LINUX /* by default */ +#endif /* defined(LITES) && defined(EXT2_OS_LITES) */ +#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ +#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ +#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ + +/* + * Calculate the number of GDT blocks to reserve for online filesystem growth. + * The absolute maximum number of GDT blocks we can reserve is determined by + * the number of block pointers that can fit into a single block. + */ +static unsigned int calc_reserved_gdt_blocks(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + unsigned long bpg = sb->s_blocks_per_group; + unsigned int gdpb = EXT2_DESC_PER_BLOCK(sb); + unsigned long max_blocks = 0xffffffff; + unsigned long rsv_groups; + unsigned int rsv_gdb; + + /* We set it at 1024x the current filesystem size, or + * the upper block count limit (2^32), whichever is lower. + */ + if (ext2fs_blocks_count(sb) < max_blocks / 1024) + max_blocks = ext2fs_blocks_count(sb) * 1024; + /* + * ext2fs_div64_ceil() is unnecessary because max_blocks is + * max _GDT_ blocks, which is limited to 32 bits. + */ + rsv_groups = ext2fs_div_ceil(max_blocks - sb->s_first_data_block, bpg); + rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - fs->desc_blocks; + if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) + rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); +#ifdef RES_GDT_DEBUG + printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n", + max_blocks, rsv_groups, rsv_gdb); +#endif + + return rsv_gdb; +} + +errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs) +{ + ext2_filsys fs; + errcode_t retval; + struct ext2_super_block *super; + unsigned int rem; + unsigned int overhead = 0; + unsigned int ipg; + dgrp_t i; + blk_t numblocks; + int rsv_gdt; + int csum_flag; + int io_flags; + char *buf = 0; + char c; + + if (!param || !ext2fs_blocks_count(param)) + return EXT2_ET_INVALID_ARGUMENT; + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->flags = flags | EXT2_FLAG_RW; + fs->umask = 022; +#ifdef WORDS_BIGENDIAN + fs->flags |= EXT2_FLAG_SWAP_BYTES; +#endif + io_flags = IO_FLAG_RW; + if (flags & EXT2_FLAG_EXCLUSIVE) + io_flags |= IO_FLAG_EXCLUSIVE; + retval = manager->open(name, io_flags, &fs->io); + if (retval) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + + strcpy(fs->device_name, name); + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); + if (retval) + goto cleanup; + fs->super = super; + + memset(super, 0, SUPERBLOCK_SIZE); + +#define set_field(field, default) (super->field = param->field ? \ + param->field : (default)) + + super->s_magic = EXT2_SUPER_MAGIC; + super->s_state = EXT2_VALID_FS; + + set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ + set_field(s_log_cluster_size, 0); + set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); + set_field(s_max_mnt_count, 0); + set_field(s_errors, EXT2_ERRORS_DEFAULT); + set_field(s_feature_compat, 0); + set_field(s_feature_incompat, 0); + set_field(s_feature_ro_compat, 0); + set_field(s_default_mount_opts, 0); + set_field(s_first_meta_bg, 0); + set_field(s_raid_stride, 0); /* default stride size: 0 */ + set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ + set_field(s_log_groups_per_flex, 0); + set_field(s_flags, 0); + if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + + set_field(s_rev_level, EXT2_GOOD_OLD_REV); + if (super->s_rev_level >= EXT2_DYNAMIC_REV) { + set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); + set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); + if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { + int extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + set_field(s_min_extra_isize, extra_isize); + set_field(s_want_extra_isize, extra_isize); + } + } else { + super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + } + + set_field(s_checkinterval, 0); + super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); + + super->s_creator_os = CREATOR_OS; + + fs->blocksize = EXT2_BLOCK_SIZE(super); + fs->clustersize = EXT2_CLUSTER_SIZE(super); + + /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ + set_field(s_blocks_per_group, fs->blocksize * 8); + if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) + super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); + super->s_clusters_per_group = super->s_blocks_per_group; + + ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); + ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); + if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + + /* + * If we're creating an external journal device, we don't need + * to bother with the rest. + */ + if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + ext2fs_mark_super_dirty(fs); + *ret_fs = fs; + return 0; + } + +retry: + fs->group_desc_count = (blk_t) ext2fs_div64_ceil( + ext2fs_blocks_count(super) - super->s_first_data_block, + EXT2_BLOCKS_PER_GROUP(super)); + if (fs->group_desc_count == 0) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } + + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT; + + fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, + EXT2_DESC_PER_BLOCK(super)); + + i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; + + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && + (ext2fs_blocks_count(super) / i) > (1ULL << 32)) + set_field(s_inodes_count, ~0U); + else + set_field(s_inodes_count, ext2fs_blocks_count(super) / i); + + /* + * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so + * that we have enough inodes for the filesystem(!) + */ + if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) + super->s_inodes_count = EXT2_FIRST_INODE(super)+1; + + /* + * There should be at least as many inodes as the user + * requested. Figure out how many inodes per group that + * should be. But make sure that we don't allocate more than + * one bitmap's worth of inodes each group. + */ + ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); + if (ipg > fs->blocksize * 8) { + if (super->s_blocks_per_group >= 256) { + /* Try again with slightly different parameters */ + super->s_blocks_per_group -= 8; + ext2fs_blocks_count_set(super, + ext2fs_blocks_count(param)); + super->s_clusters_per_group = super->s_blocks_per_group; + goto retry; + } else { + retval = EXT2_ET_TOO_MANY_INODES; + goto cleanup; + } + } + + if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) + ipg = EXT2_MAX_INODES_PER_GROUP(super); + +ipg_retry: + super->s_inodes_per_group = ipg; + + /* + * Make sure the number of inodes per group completely fills + * the inode table blocks in the descriptor. If not, add some + * additional inodes/group. Waste not, want not... + */ + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + super->s_inodes_per_group = ((fs->inode_blocks_per_group * + EXT2_BLOCK_SIZE(super)) / + EXT2_INODE_SIZE(super)); + /* + * Finally, make sure the number of inodes per group is a + * multiple of 8. This is needed to simplify the bitmap + * splicing code. + */ + if (super->s_inodes_per_group < 8) + super->s_inodes_per_group = 8; + super->s_inodes_per_group &= ~7; + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + + /* + * adjust inode count to reflect the adjusted inodes_per_group + */ + if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { + ipg--; + goto ipg_retry; + } + super->s_inodes_count = super->s_inodes_per_group * + fs->group_desc_count; + super->s_free_inodes_count = super->s_inodes_count; + + /* + * check the number of reserved group descriptor table blocks + */ + if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) + rsv_gdt = calc_reserved_gdt_blocks(fs); + else + rsv_gdt = 0; + set_field(s_reserved_gdt_blocks, rsv_gdt); + if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { + retval = EXT2_ET_RES_GDT_BLOCKS; + goto cleanup; + } + + /* + * Calculate the maximum number of bookkeeping blocks per + * group. It includes the superblock, the block group + * descriptors, the block bitmap, the inode bitmap, the inode + * table, and the reserved gdt blocks. + */ + overhead = (int) (3 + fs->inode_blocks_per_group + + fs->desc_blocks + super->s_reserved_gdt_blocks); + + /* This can only happen if the user requested too many inodes */ + if (overhead > super->s_blocks_per_group) { + retval = EXT2_ET_TOO_MANY_INODES; + goto cleanup; + } + + /* + * See if the last group is big enough to support the + * necessary data structures. If not, we need to get rid of + * it. We need to recalculate the overhead for the last block + * group, since it might or might not have a superblock + * backup. + */ + overhead = (int) (2 + fs->inode_blocks_per_group); + if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) + overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; + rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % + super->s_blocks_per_group); + if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } + if (rem && (rem < overhead+50)) { + ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - + rem); + + goto retry; + } + + /* + * At this point we know how big the filesystem will be. So + * we can do any and all allocations that depend on the block + * count. + */ + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + goto cleanup; + + strcpy(buf, "block bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + + strcpy(buf, "inode bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + + ext2fs_free_mem(&buf); + + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + + memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); + + /* + * Reserve the superblock and group descriptors for each + * group, and fill in the correct group statistics for group. + * Note that although the block bitmap, inode bitmap, and + * inode table have not been allocated (and in fact won't be + * by this routine), they are accounted for nevertheless. + * + * If FLEX_BG meta-data grouping is used, only account for the + * superblock and group descriptors (the inode tables and + * bitmaps will be accounted for when allocated). + */ + ext2fs_free_blocks_count_set(super, 0); + csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + for (i = 0; i < fs->group_desc_count; i++) { + /* + * Don't set the BLOCK_UNINIT group for the last group + * because the block bitmap needs to be padded. + */ + if (csum_flag) { + if (i != fs->group_desc_count - 1) + ext2fs_bg_flags_set(fs, i, + EXT2_BG_BLOCK_UNINIT); + ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); + numblocks = super->s_inodes_per_group; + if (i == 0) + numblocks -= super->s_first_ino; + ext2fs_bg_itable_unused_set(fs, i, numblocks); + } + numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); + if (fs->super->s_log_groups_per_flex) + numblocks += 2 + fs->inode_blocks_per_group; + + ext2fs_free_blocks_count_set(super, + ext2fs_free_blocks_count(super) + + numblocks); + ext2fs_bg_free_blocks_count_set(fs, i, numblocks); + ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); + ext2fs_bg_used_dirs_count_set(fs, i, 0); + ext2fs_group_desc_csum_set(fs, i); + } + + c = (char) 255; + if (((int) c) == -1) { + super->s_flags |= EXT2_FLAGS_SIGNED_HASH; + } else { + super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; + } + + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); + ext2fs_mark_ib_dirty(fs); + + io_channel_set_blksize(fs->io, fs->blocksize); + + *ret_fs = fs; + return 0; +cleanup: + free(buf); + ext2fs_free(fs); + return retval; +} diff --git a/lib/libext2fs/orig/inline.c b/lib/libext2fs/orig/inline.c new file mode 100644 index 0000000..f9be368 --- /dev/null +++ b/lib/libext2fs/orig/inline.c @@ -0,0 +1,32 @@ +/* + * inline.c --- Includes the inlined functions defined in the header + * files as standalone functions, in case the application program + * is compiled with inlining turned off. + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#define INCLUDE_INLINE_FUNCS +#include "ext2fs.h" + diff --git a/lib/libext2fs/orig/inode.c b/lib/libext2fs/orig/inode.c new file mode 100644 index 0000000..a762dbc --- /dev/null +++ b/lib/libext2fs/orig/inode.c @@ -0,0 +1,834 @@ +/* + * inode.c --- utility routines to read and write inodes + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +struct ext2_struct_inode_scan { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t current_inode; + blk64_t current_block; + dgrp_t current_group; + ext2_ino_t inodes_left; + blk_t blocks_left; + dgrp_t groups_left; + blk_t inode_buffer_blocks; + char * inode_buffer; + int inode_size; + char * ptr; + int bytes_left; + char *temp_buffer; + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data); + void * done_group_data; + int bad_block_ptr; + int scan_flags; + int reserved[6]; +}; + +/* + * This routine flushes the icache, if it exists. + */ +errcode_t ext2fs_flush_icache(ext2_filsys fs) +{ + int i; + + if (!fs->icache) + return 0; + + for (i=0; i < fs->icache->cache_size; i++) + fs->icache->cache[i].ino = 0; + + fs->icache->buffer_blk = 0; + return 0; +} + +static errcode_t create_icache(ext2_filsys fs) +{ + errcode_t retval; + + if (fs->icache) + return 0; + retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); + if (retval) + return retval; + + memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); + retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); + if (retval) { + ext2fs_free_mem(&fs->icache); + return retval; + } + fs->icache->buffer_blk = 0; + fs->icache->cache_last = -1; + fs->icache->cache_size = 4; + fs->icache->refcount = 1; + retval = ext2fs_get_array(fs->icache->cache_size, + sizeof(struct ext2_inode_cache_ent), + &fs->icache->cache); + if (retval) { + ext2fs_free_mem(&fs->icache->buffer); + ext2fs_free_mem(&fs->icache); + return retval; + } + ext2fs_flush_icache(fs); + return 0; +} + +errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan) +{ + ext2_inode_scan scan; + errcode_t retval; + errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * If fs->badblocks isn't set, then set it --- since the inode + * scanning functions require it. + */ + if (fs->badblocks == 0) { + /* + * Temporarly save fs->get_blocks and set it to zero, + * for compatibility with old e2fsck's. + */ + save_get_blocks = fs->get_blocks; + fs->get_blocks = 0; + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval && fs->badblocks) { + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + } + fs->get_blocks = save_get_blocks; + } + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); + if (retval) + return retval; + memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); + + scan->magic = EXT2_ET_MAGIC_INODE_SCAN; + scan->fs = fs; + scan->inode_size = EXT2_INODE_SIZE(fs->super); + scan->bytes_left = 0; + scan->current_group = 0; + scan->groups_left = fs->group_desc_count - 1; + scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); + scan->blocks_left = scan->fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + scan->inodes_left -= + ext2fs_bg_itable_unused(fs, scan->current_group); + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + retval = ext2fs_get_memalign(scan->inode_buffer_blocks * fs->blocksize, + fs->blocksize, &scan->inode_buffer); + scan->done_group = 0; + scan->done_group_data = 0; + scan->bad_block_ptr = 0; + if (retval) { + ext2fs_free_mem(&scan); + return retval; + } + retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); + if (retval) { + ext2fs_free_mem(&scan->inode_buffer); + ext2fs_free_mem(&scan); + return retval; + } + if (scan->fs->badblocks && scan->fs->badblocks->num) + scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + scan->scan_flags |= EXT2_SF_DO_LAZY; + *ret_scan = scan; + return 0; +} + +void ext2fs_close_inode_scan(ext2_inode_scan scan) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + ext2fs_free_mem(&scan->inode_buffer); + scan->inode_buffer = NULL; + ext2fs_free_mem(&scan->temp_buffer); + scan->temp_buffer = NULL; + ext2fs_free_mem(&scan); + return; +} + +void ext2fs_set_inode_callback(ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data), + void *done_group_data) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + scan->done_group = done_group; + scan->done_group_data = done_group_data; +} + +int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags) +{ + int old_flags; + + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return 0; + + old_flags = scan->scan_flags; + scan->scan_flags &= ~clear_flags; + scan->scan_flags |= set_flags; + return old_flags; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * get ready to read in a new blockgroup. + */ +static errcode_t get_next_blockgroup(ext2_inode_scan scan) +{ + ext2_filsys fs = scan->fs; + + scan->current_group++; + scan->groups_left--; + + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->current_inode = scan->current_group * + EXT2_INODES_PER_GROUP(fs->super); + + scan->bytes_left = 0; + scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); + scan->blocks_left = fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + scan->inodes_left -= + ext2fs_bg_itable_unused(fs, scan->current_group); + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + + return 0; +} + +errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group) +{ + scan->current_group = group - 1; + scan->groups_left = scan->fs->group_desc_count - group; + return get_next_blockgroup(scan); +} + +/* + * This function is called by get_next_blocks() to check for bad + * blocks in the inode table. + * + * This function assumes that badblocks_list->list is sorted in + * increasing order. + */ +static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, + blk_t *num_blocks) +{ + blk_t blk = scan->current_block; + badblocks_list bb = scan->fs->badblocks; + + /* + * If the inode table is missing, then obviously there are no + * bad blocks. :-) + */ + if (blk == 0) + return 0; + + /* + * If the current block is greater than the bad block listed + * in the bad block list, then advance the pointer until this + * is no longer the case. If we run out of bad blocks, then + * we don't need to do any more checking! + */ + while (blk > bb->list[scan->bad_block_ptr]) { + if (++scan->bad_block_ptr >= bb->num) { + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + } + + /* + * If the current block is equal to the bad block listed in + * the bad block list, then handle that one block specially. + * (We could try to handle runs of bad blocks, but that + * only increases CPU efficiency by a small amount, at the + * expense of a huge expense of code complexity, and for an + * uncommon case at that.) + */ + if (blk == bb->list[scan->bad_block_ptr]) { + scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; + *num_blocks = 1; + if (++scan->bad_block_ptr >= bb->num) + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + + /* + * If there is a bad block in the range that we're about to + * read in, adjust the number of blocks to read so that we we + * don't read in the bad block. (Then the next block to read + * will be the bad block, which is handled in the above case.) + */ + if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) + *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); + + return 0; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * read in more blocks from the current blockgroup's inode table. + */ +static errcode_t get_next_blocks(ext2_inode_scan scan) +{ + blk_t num_blocks; + errcode_t retval; + + /* + * Figure out how many blocks to read; we read at most + * inode_buffer_blocks, and perhaps less if there aren't that + * many blocks left to read. + */ + num_blocks = scan->inode_buffer_blocks; + if (num_blocks > scan->blocks_left) + num_blocks = scan->blocks_left; + + /* + * If the past block "read" was a bad block, then mark the + * left-over extra bytes as also being bad. + */ + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { + if (scan->bytes_left) + scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; + scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; + } + + /* + * Do inode bad block processing, if necessary. + */ + if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { + retval = check_for_inode_bad_blocks(scan, &num_blocks); + if (retval) + return retval; + } + + if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || + (scan->current_block == 0)) { + memset(scan->inode_buffer, 0, + (size_t) num_blocks * scan->fs->blocksize); + } else { + retval = io_channel_read_blk64(scan->fs->io, + scan->current_block, + (int) num_blocks, + scan->inode_buffer); + if (retval) + return EXT2_ET_NEXT_INODE_READ; + } + scan->ptr = scan->inode_buffer; + scan->bytes_left = num_blocks * scan->fs->blocksize; + + scan->blocks_left -= num_blocks; + if (scan->current_block) + scan->current_block += num_blocks; + return 0; +} + +#if 0 +/* + * Returns 1 if the entire inode_buffer has a non-zero size and + * contains all zeros. (Not just deleted inodes, since that means + * that part of the inode table was used at one point; we want all + * zeros, which means that the inode table is pristine.) + */ +static inline int is_empty_scan(ext2_inode_scan scan) +{ + int i; + + if (scan->bytes_left == 0) + return 0; + + for (i=0; i < scan->bytes_left; i++) + if (scan->ptr[i]) + return 0; + return 1; +} +#endif + +errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode, int bufsize) +{ + errcode_t retval; + int extra_bytes = 0; + + EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); + + /* + * Do we need to start reading a new block group? + */ + if (scan->inodes_left <= 0) { + force_new_group: + if (scan->done_group) { + retval = (scan->done_group) + (scan->fs, scan, scan->current_group, + scan->done_group_data); + if (retval) + return retval; + } + if (scan->groups_left <= 0) { + *ino = 0; + return 0; + } + retval = get_next_blockgroup(scan); + if (retval) + return retval; + } + /* + * These checks are done outside the above if statement so + * they can be done for block group #0. + */ + if ((scan->scan_flags & EXT2_SF_DO_LAZY) && + (ext2fs_bg_flags_test(scan->fs, scan->current_group, EXT2_BG_INODE_UNINIT) + )) + goto force_new_group; + if (scan->inodes_left == 0) + goto force_new_group; + if (scan->current_block == 0) { + if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { + goto force_new_group; + } else + return EXT2_ET_MISSING_INODE_TABLE; + } + + + /* + * Have we run out of space in the inode buffer? If so, we + * need to read in more blocks. + */ + if (scan->bytes_left < scan->inode_size) { + memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); + extra_bytes = scan->bytes_left; + + retval = get_next_blocks(scan); + if (retval) + return retval; +#if 0 + /* + * XXX test Need check for used inode somehow. + * (Note: this is hard.) + */ + if (is_empty_scan(scan)) + goto force_new_group; +#endif + } + + retval = 0; + if (extra_bytes) { + memcpy(scan->temp_buffer+extra_bytes, scan->ptr, + scan->inode_size - extra_bytes); + scan->ptr += scan->inode_size - extra_bytes; + scan->bytes_left -= scan->inode_size - extra_bytes; + +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->temp_buffer, + 0, bufsize); +#else + *inode = *((struct ext2_inode *) scan->temp_buffer); +#endif + if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; + } else { +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->ptr, + 0, bufsize); +#else + memcpy(inode, scan->ptr, bufsize); +#endif + scan->ptr += scan->inode_size; + scan->bytes_left -= scan->inode_size; + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + } + + scan->inodes_left--; + scan->current_inode++; + *ino = scan->current_inode; + return retval; +} + +errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode) +{ + return ext2fs_get_next_inode_full(scan, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * Functions to read and write a single inode. + */ +errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + char *ptr; + errcode_t retval; + int clen, i, inodes_per_block, length; + io_channel io; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user has an override function */ + if (fs->read_inode) { + retval = (fs->read_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + /* Create inode cache if not present */ + if (!fs->icache) { + retval = create_icache(fs); + if (retval) + return retval; + } + /* Check to see if it's in the inode cache */ + if (bufsize == sizeof(struct ext2_inode)) { + /* only old good inode can be retrieved from the cache */ + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + *inode = fs->icache->cache[i].inode; + return 0; + } + } + } + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); + block_nr = fs->image_header->offset_inode / fs->blocksize; + block_nr += (ino - 1) / inodes_per_block; + offset = ((ino - 1) % inodes_per_block) * + EXT2_INODE_SIZE(fs->super); + io = fs->image_io; + } else { + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + if (group > fs->group_desc_count) + return EXT2_ET_BAD_INODE_NUM; + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) + return EXT2_ET_MISSING_INODE_TABLE; + block_nr = ext2fs_inode_table_loc(fs, group) + + block; + io = fs->io; + } + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (bufsize < length) + length = bufsize; + + ptr = (char *) inode; + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (block_nr != fs->icache->buffer_blk) { + retval = io_channel_read_blk64(io, block_nr, 1, + fs->icache->buffer); + if (retval) + return retval; + fs->icache->buffer_blk = block_nr; + } + + memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, + clen); + + offset = 0; + length -= clen; + ptr += clen; + block_nr++; + } + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) inode, + 0, bufsize); +#endif + + /* Update the inode cache */ + fs->icache->cache_last = (fs->icache->cache_last + 1) % + fs->icache->cache_size; + fs->icache->cache[fs->icache->cache_last].ino = ino; + fs->icache->cache[fs->icache->cache_last].inode = *inode; + + return 0; +} + +errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode) +{ + return ext2fs_read_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + errcode_t retval = 0; + struct ext2_inode_large temp_inode, *w_inode; + char *ptr; + int clen, i, length; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user provided an override function */ + if (fs->write_inode) { + retval = (fs->write_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + + /* Check to see if the inode cache needs to be updated */ + if (fs->icache) { + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + fs->icache->cache[i].inode = *inode; + break; + } + } + } else { + retval = create_icache(fs); + if (retval) + return retval; + } + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + + length = bufsize; + if (length < EXT2_INODE_SIZE(fs->super)) + length = EXT2_INODE_SIZE(fs->super); + + if (length > (int) sizeof(struct ext2_inode_large)) { + w_inode = malloc(length); + if (!w_inode) + return ENOMEM; + } else + w_inode = &temp_inode; + memset(w_inode, 0, length); + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, w_inode, + (struct ext2_inode_large *) inode, + 1, bufsize); +#else + memcpy(w_inode, inode, bufsize); +#endif + + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block; + + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (length > bufsize) + length = bufsize; + + ptr = (char *) w_inode; + + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (fs->icache->buffer_blk != block_nr) { + retval = io_channel_read_blk64(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + fs->icache->buffer_blk = block_nr; + } + + + memcpy((char *) fs->icache->buffer + (unsigned) offset, + ptr, clen); + + retval = io_channel_write_blk64(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + + offset = 0; + ptr += clen; + length -= clen; + block_nr++; + } + + fs->flags |= EXT2_FLAG_CHANGED; +errout: + if (w_inode && w_inode != &temp_inode) + free(w_inode); + return retval; +} + +errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * This function should be called when writing a new inode. It makes + * sure that extra part of large inodes is initialized properly. + */ +errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + struct ext2_inode *buf; + int size = EXT2_INODE_SIZE(fs->super); + struct ext2_inode_large *large_inode; + errcode_t retval; + __u32 t = fs->now ? fs->now : time(NULL); + + if (!inode->i_ctime) + inode->i_ctime = t; + if (!inode->i_mtime) + inode->i_mtime = t; + if (!inode->i_atime) + inode->i_atime = t; + + if (size == sizeof(struct ext2_inode)) + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); + + buf = malloc(size); + if (!buf) + return ENOMEM; + + memset(buf, 0, size); + *buf = *inode; + + large_inode = (struct ext2_inode_large *) buf; + large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + if (!large_inode->i_crtime) + large_inode->i_crtime = t; + + retval = ext2fs_write_inode_full(fs, ino, buf, size); + free(buf); + return retval; +} + + +errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) +{ + struct ext2_inode inode; + int i; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->get_blocks) { + if (!(*fs->get_blocks)(fs, ino, blocks)) + return 0; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + for (i=0; i < EXT2_N_BLOCKS; i++) + blocks[i] = inode.i_block[i]; + return 0; +} + +errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode inode; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->check_directory) { + retval = (fs->check_directory)(fs, ino); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + if (!LINUX_S_ISDIR(inode.i_mode)) + return EXT2_ET_NO_DIRECTORY; + return 0; +} + diff --git a/lib/libext2fs/orig/inode_io.c b/lib/libext2fs/orig/inode_io.c new file mode 100644 index 0000000..b3e7ce5 --- /dev/null +++ b/lib/libext2fs/orig/inode_io.c @@ -0,0 +1,291 @@ +/* + * inode_io.c --- This is allows an inode in an ext2 filesystem image + * to be accessed via the I/O manager interface. + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + +struct inode_private_data { + int magic; + char name[32]; + ext2_file_t file; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + struct inode_private_data *next; +}; + +#define CHANNEL_HAS_INODE 0x8000 + +static struct inode_private_data *top_intern; +static int ino_unique = 0; + +static errcode_t inode_open(const char *name, int flags, io_channel *channel); +static errcode_t inode_close(io_channel channel); +static errcode_t inode_set_blksize(io_channel channel, int blksize); +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *data); +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *data); +static errcode_t inode_flush(io_channel channel); +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *data); +static errcode_t inode_read_blk64(io_channel channel, + unsigned long long block, int count, void *data); +static errcode_t inode_write_blk64(io_channel channel, + unsigned long long block, int count, const void *data); + +static struct struct_io_manager struct_inode_manager = { + EXT2_ET_MAGIC_IO_MANAGER, + "Inode I/O Manager", + inode_open, + inode_close, + inode_set_blksize, + inode_read_blk, + inode_write_blk, + inode_flush, + inode_write_byte, + NULL, + NULL, + inode_read_blk64, + inode_write_blk64 +}; + +io_manager inode_io_manager = &struct_inode_manager; + +errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name) +{ + struct inode_private_data *data; + errcode_t retval; + + if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), + &data))) + return retval; + data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; + sprintf(data->name, "%u:%d", ino, ino_unique++); + data->file = 0; + data->fs = fs; + data->ino = ino; + data->flags = 0; + if (inode) { + memcpy(&data->inode, inode, sizeof(struct ext2_inode)); + data->flags |= CHANNEL_HAS_INODE; + } + data->next = top_intern; + top_intern = data; + *name = data->name; + return 0; +} + +errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name) +{ + return ext2fs_inode_io_intern2(fs, ino, NULL, name); +} + + +static errcode_t inode_open(const char *name, int flags, io_channel *channel) +{ + io_channel io = NULL; + struct inode_private_data *prev, *data = NULL; + errcode_t retval; + int open_flags; + + if (name == 0) + return EXT2_ET_BAD_DEVICE_NAME; + + for (data = top_intern, prev = NULL; data; + prev = data, data = data->next) + if (strcmp(name, data->name) == 0) + break; + if (!data) + return ENOENT; + if (prev) + prev->next = data->next; + else + top_intern = data->next; + + retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); + if (retval) + goto cleanup; + memset(io, 0, sizeof(struct struct_io_channel)); + + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io->manager = inode_io_manager; + retval = ext2fs_get_mem(strlen(name)+1, &io->name); + if (retval) + goto cleanup; + + strcpy(io->name, name); + io->private_data = data; + io->block_size = 1024; + io->read_error = 0; + io->write_error = 0; + io->refcount = 1; + + open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; + retval = ext2fs_file_open2(data->fs, data->ino, + (data->flags & CHANNEL_HAS_INODE) ? + &data->inode : 0, open_flags, + &data->file); + if (retval) + goto cleanup; + + *channel = io; + return 0; + +cleanup: + if (io->name) + ext2fs_free_mem(&io->name); + if (data) + ext2fs_free_mem(&data); + if (io) + ext2fs_free_mem(&io); + return retval; +} + +static errcode_t inode_close(io_channel channel) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if (--channel->refcount > 0) + return 0; + + retval = ext2fs_file_close(data->file); + + ext2fs_free_mem(&channel->private_data); + if (channel->name) + ext2fs_free_mem(&channel->name); + ext2fs_free_mem(&channel); + return retval; +} + +static errcode_t inode_set_blksize(io_channel channel, int blksize) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + channel->block_size = blksize; + return 0; +} + + +static errcode_t inode_read_blk64(io_channel channel, + unsigned long long block, int count, void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_read(data->file, buf, count, 0); +} + +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + return inode_read_blk64(channel, block, count, buf); +} + +static errcode_t inode_write_blk64(io_channel channel, + unsigned long long block, int count, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_write(data->file, buf, count, 0); +} + +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + return inode_write_blk64(channel, block, count, buf); +} + +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, offset, + EXT2_SEEK_SET, 0))) + return retval; + + return ext2fs_file_write(data->file, buf, size, 0); +} + +/* + * Flush data buffers to disk. + */ +static errcode_t inode_flush(io_channel channel) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + return ext2fs_file_flush(data->file); +} + diff --git a/lib/libext2fs/orig/io_manager.c b/lib/libext2fs/orig/io_manager.c new file mode 100644 index 0000000..80f9dfc --- /dev/null +++ b/lib/libext2fs/orig/io_manager.c @@ -0,0 +1,112 @@ +/* + * io_manager.c --- the I/O manager abstraction + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t io_channel_set_options(io_channel channel, const char *opts) +{ + errcode_t retval = 0; + char *next, *ptr, *options, *arg; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!opts) + return 0; + + if (!channel->manager->set_option) + return EXT2_ET_INVALID_ARGUMENT; + + options = malloc(strlen(opts)+1); + if (!options) + return EXT2_ET_NO_MEMORY; + strcpy(options, opts); + ptr = options; + + while (ptr && *ptr) { + next = strchr(ptr, '&'); + if (next) + *next++ = 0; + + arg = strchr(ptr, '='); + if (arg) + *arg++ = 0; + + retval = (channel->manager->set_option)(channel, ptr, arg); + if (retval) + break; + ptr = next; + } + free(options); + return retval; +} + +errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_byte) + return channel->manager->write_byte(channel, offset, + count, data); + + return EXT2_ET_UNIMPLEMENTED; +} + +errcode_t io_channel_read_blk64(io_channel channel, unsigned long long block, + int count, void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->read_blk64) + return (channel->manager->read_blk64)(channel, block, + count, data); + + if ((block >> 32) != 0) + return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; + + return (channel->manager->read_blk)(channel, (unsigned long) block, + count, data); +} + +errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_blk64) + return (channel->manager->write_blk64)(channel, block, + count, data); + + if ((block >> 32) != 0) + return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; + + return (channel->manager->write_blk)(channel, (unsigned long) block, + count, data); +} + +errcode_t io_channel_discard(io_channel channel, unsigned long long block, + unsigned long long count) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->discard) + return (channel->manager->discard)(channel, block, count); + + return EXT2_ET_UNIMPLEMENTED; +} diff --git a/lib/libext2fs/orig/irel.h b/lib/libext2fs/orig/irel.h new file mode 100644 index 0000000..9a4958b --- /dev/null +++ b/lib/libext2fs/orig/irel.h @@ -0,0 +1,114 @@ +/* + * irel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +struct ext2_inode_reference { + blk_t block; + __u16 offset; +}; + +struct ext2_inode_relocate_entry { + ext2_ino_t new; + ext2_ino_t orig; + __u16 flags; + __u16 max_refs; +}; + +typedef struct ext2_inode_relocation_table *ext2_irel; + +struct ext2_inode_relocation_table { + __u32 magic; + char *name; + ext2_ino_t current; + void *priv_data; + + /* + * Add an inode relocation entry. + */ + errcode_t (*put)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + /* + * Get an inode relocation entry. + */ + errcode_t (*get)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + + /* + * Get an inode relocation entry by its original inode number + */ + errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Initialize for iterating over the inode relocation entries. + */ + errcode_t (*start_iter)(ext2_irel irel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Add an inode reference (i.e., note the fact that a + * particular block/offset contains a reference to an inode) + */ + errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); + + /* + * Initialize for iterating over the inode references for a + * particular inode. + */ + errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); + + /* + * The iterator function for the inode references for an + * inode. The references for only one inode can be interator + * over at a time, as the iterator state is stored in ext2_irel. + */ + errcode_t (*next_ref)(ext2_irel irel, + struct ext2_inode_reference *ref); + + /* + * Move the inode relocation table from one inode number to + * another. Note that the inode references also must move. + */ + errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); + + /* + * Remove an inode relocation entry, along with all of the + * inode references. + */ + errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); + + /* + * Free the inode relocation table. + */ + errcode_t (*free)(ext2_irel irel); +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *irel); + +#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) +#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) +#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ + ((irel)->get_by_orig((irel), orig, old, ent)) +#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) +#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) +#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) +#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) +#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) +#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) +#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) +#define ext2fs_irel_free(irel) ((irel)->free((irel))) diff --git a/lib/libext2fs/orig/irel_ma.c b/lib/libext2fs/orig/irel_ma.c new file mode 100644 index 0000000..b7eaf6b --- /dev/null +++ b/lib/libext2fs/orig/irel_ma.c @@ -0,0 +1,372 @@ +/* + * irel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "irel.h" + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_start_iter(ext2_irel irel); +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); +static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); +static errcode_t ima_free(ext2_irel irel); + +/* + * This data structure stores the array of inode references; there is + * a structure for each inode. + */ +struct inode_reference_entry { + __u16 num; + struct ext2_inode_reference *refs; +}; + +struct irel_ma { + __u32 magic; + ext2_ino_t max_inode; + ext2_ino_t ref_current; + int ref_iter; + ext2_ino_t *orig_map; + struct ext2_inode_relocate_entry *entries; + struct inode_reference_entry *ref_entries; +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *new_irel) +{ + ext2_irel irel = 0; + errcode_t retval; + struct irel_ma *ma = 0; + size_t size; + + *new_irel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), + &irel); + if (retval) + goto errout; + memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &irel->name); + if (retval) + goto errout; + strcpy(irel->name, name); + + retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct irel_ma)); + irel->priv_data = ma; + + size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); + retval = ext2fs_get_array(max_inode+1, sizeof(ext2_ino_t), + &ma->orig_map); + if (retval) + goto errout; + memset(ma->orig_map, 0, size); + + size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * (max_inode+1)); + retval = ext2fs_get_array((max_inode+1), sizeof(struct ext2_inode_relocate_entry), &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + + size = (size_t) (sizeof(struct inode_reference_entry) * (max_inode+1)); + retval = ext2fs_get_mem(size, &ma->ref_entries); + if (retval) + goto errout; + memset(ma->ref_entries, 0, size); + ma->max_inode = max_inode; + + /* + * Fill in the irel data structure + */ + irel->put = ima_put; + irel->get = ima_get; + irel->get_by_orig = ima_get_by_orig; + irel->start_iter = ima_start_iter; + irel->next = ima_next; + irel->add_ref = ima_add_ref; + irel->start_iter_ref = ima_start_iter_ref; + irel->next_ref = ima_next_ref; + irel->move = ima_move; + irel->delete = ima_delete; + irel->free = ima_free; + + *new_irel = irel; + return 0; + +errout: + ima_free(irel); + return retval; +} + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct inode_reference_entry *ref_ent; + struct irel_ma *ma; + errcode_t retval; + size_t size, old_size; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + /* + * Force the orig field to the correct value; the application + * program shouldn't be messing with this field. + */ + if (ma->entries[(unsigned) old].new == 0) + ent->orig = old; + else + ent->orig = ma->entries[(unsigned) old].orig; + + /* + * If max_refs has changed, reallocate the refs array + */ + ref_ent = ma->ref_entries + (unsigned) old; + if (ref_ent->refs && ent->max_refs != + ma->entries[(unsigned) old].max_refs) { + size = (sizeof(struct ext2_inode_reference) * ent->max_refs); + old_size = (sizeof(struct ext2_inode_reference) * + ma->entries[(unsigned) old].max_refs); + retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); + if (retval) + return retval; + } + + ma->entries[(unsigned) old] = *ent; + ma->orig_map[(unsigned) ent->orig] = old; + return 0; +} + +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + *ent = ma->entries[(unsigned) old]; + return 0; +} + +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + ma = irel->priv_data; + if (orig > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + ino = ma->orig_map[(unsigned) orig]; + if (ino == 0) + return ENOENT; + *old = ino; + *ent = ma->entries[(unsigned) ino]; + return 0; +} + +static errcode_t ima_start_iter(ext2_irel irel) +{ + irel->current = 0; + return 0; +} + +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + while (++irel->current < ma->max_inode) { + if (ma->entries[(unsigned) irel->current].new == 0) + continue; + *old = irel->current; + *ent = ma->entries[(unsigned) irel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + size_t size; + struct inode_reference_entry *ref_ent; + struct ext2_inode_relocate_entry *ent; + errcode_t retval; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + ref_ent = ma->ref_entries + (unsigned) ino; + ent = ma->entries + (unsigned) ino; + + /* + * If the inode reference array doesn't exist, create it. + */ + if (ref_ent->refs == 0) { + size = (size_t) ((sizeof(struct ext2_inode_reference) * + ent->max_refs)); + retval = ext2fs_get_array(ent->max_refs, + sizeof(struct ext2_inode_reference), &ref_ent->refs); + if (retval) + return retval; + memset(ref_ent->refs, 0, size); + ref_ent->num = 0; + } + + if (ref_ent->num >= ent->max_refs) + return EXT2_ET_TOO_MANY_REFS; + + ref_ent->refs[(unsigned) ref_ent->num++] = *ref; + return 0; +} + +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) ino].new == 0) + return ENOENT; + ma->ref_current = ino; + ma->ref_iter = 0; + return 0; +} + +static errcode_t ima_next_ref(ext2_irel irel, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + struct inode_reference_entry *ref_ent; + + ma = irel->priv_data; + + ref_ent = ma->ref_entries + ma->ref_current; + + if ((ref_ent->refs == NULL) || + (ma->ref_iter >= ref_ent->num)) { + ref->block = 0; + ref->offset = 0; + return 0; + } + *ref = ref_ent->refs[ma->ref_iter++]; + return 0; +} + + +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if ((old > ma->max_inode) || (new > ma->max_inode)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; + if (ma->ref_entries[(unsigned) new].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); + ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; + + ma->entries[(unsigned) old].new = 0; + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + + ma->orig_map[ma->entries[new].orig] = new; + return 0; +} + +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[old].new = 0; + if (ma->ref_entries[(unsigned) old].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); + ma->orig_map[ma->entries[(unsigned) old].orig] = 0; + + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + return 0; +} + +static errcode_t ima_free(ext2_irel irel) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + if (!irel) + return 0; + + ma = irel->priv_data; + + if (ma) { + if (ma->orig_map) + ext2fs_free_mem(&ma->orig_map); + if (ma->entries) + ext2fs_free_mem(&ma->entries); + if (ma->ref_entries) { + for (ino = 0; ino <= ma->max_inode; ino++) { + if (ma->ref_entries[(unsigned) ino].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); + } + ext2fs_free_mem(&ma->ref_entries); + } + ext2fs_free_mem(&ma); + } + if (irel->name) + ext2fs_free_mem(&irel->name); + ext2fs_free_mem(&irel); + return 0; +} diff --git a/lib/libext2fs/orig/ismounted.c b/lib/libext2fs/orig/ismounted.c new file mode 100644 index 0000000..bc1134f --- /dev/null +++ b/lib/libext2fs/orig/ismounted.c @@ -0,0 +1,383 @@ +/* + * ismounted.c --- Check to see if the filesystem was mounted + * + * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_MNTENT_H +#include +#endif +#ifdef HAVE_GETMNTINFO +#include +#include +#include +#endif /* HAVE_GETMNTINFO */ +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifdef HAVE_MNTENT_H +/* + * Helper function which checks a file in /etc/mtab format to see if a + * filesystem is mounted. Returns an error if the file doesn't exist + * or can't be opened. + */ +static errcode_t check_mntent_file(const char *mtab_file, const char *file, + int *mount_flags, char *mtpt, int mtlen) +{ + struct mntent *mnt; + struct stat st_buf; + errcode_t retval = 0; + dev_t file_dev=0, file_rdev=0; + ino_t file_ino=0; + FILE *f; + int fd; + + *mount_flags = 0; + if ((f = setmntent (mtab_file, "r")) == NULL) + return (errno == ENOENT ? EXT2_NO_MTAB_FILE : errno); + if (stat(file, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + file_rdev = st_buf.st_rdev; +#endif /* __GNU__ */ + } else { + file_dev = st_buf.st_dev; + file_ino = st_buf.st_ino; + } + } + while ((mnt = getmntent (f)) != NULL) { + if (mnt->mnt_fsname[0] != '/') + continue; + if (strcmp(file, mnt->mnt_fsname) == 0) + break; + if (stat(mnt->mnt_fsname, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ + if (file_rdev && (file_rdev == st_buf.st_rdev)) + break; +#endif /* __GNU__ */ + } else { + if (file_dev && ((file_dev == st_buf.st_dev) && + (file_ino == st_buf.st_ino))) + break; + } + } + } + + if (mnt == 0) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + /* + * Do an extra check to see if this is the root device. We + * can't trust /etc/mtab, and /proc/mounts will only list + * /dev/root for the root filesystem. Argh. Instead we + * check if the given device has the same major/minor number + * as the device that the root directory is on. + */ + if (file_rdev && stat("/", &st_buf) == 0) { + if (st_buf.st_dev == file_rdev) { + *mount_flags = EXT2_MF_MOUNTED; + if (mtpt) + strncpy(mtpt, "/", mtlen); + goto is_root; + } + } +#endif /* __GNU__ */ + goto errout; + } +#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ + /* Validate the entry in case /etc/mtab is out of date */ + /* + * We need to be paranoid, because some broken distributions + * (read: Slackware) don't initialize /etc/mtab before checking + * all of the non-root filesystems on the disk. + */ + if (stat(mnt->mnt_dir, &st_buf) < 0) { + retval = errno; + if (retval == ENOENT) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s does not exist)\n", + mtab_file, mnt->mnt_dir); +#endif /* DEBUG */ + retval = 0; + } + goto errout; + } + if (file_rdev && (st_buf.st_dev != file_rdev)) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s not mounted on %s)\n", + mtab_file, file, mnt->mnt_dir); +#endif /* DEBUG */ + goto errout; + } +#endif /* __GNU__ */ + *mount_flags = EXT2_MF_MOUNTED; + +#ifdef MNTOPT_RO + /* Check to see if the ro option is set */ + if (hasmntopt(mnt, MNTOPT_RO)) + *mount_flags |= EXT2_MF_READONLY; +#endif + + if (mtpt) + strncpy(mtpt, mnt->mnt_dir, mtlen); + /* + * Check to see if we're referring to the root filesystem. + * If so, do a manual check to see if we can open /etc/mtab + * read/write, since if the root is mounted read/only, the + * contents of /etc/mtab may not be accurate. + */ + if (!strcmp(mnt->mnt_dir, "/")) { +is_root: +#define TEST_FILE "/.ismount-test-file" + *mount_flags |= EXT2_MF_ISROOT; + fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); + if (fd < 0) { + if (errno == EROFS) + *mount_flags |= EXT2_MF_READONLY; + } else + close(fd); + (void) unlink(TEST_FILE); + } + retval = 0; +errout: + endmntent (f); + return retval; +} + +static errcode_t check_mntent(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + errcode_t retval; + +#ifdef DEBUG + retval = check_mntent_file("/tmp/mtab", file, mount_flags, + mtpt, mtlen); + if (retval == 0) + return 0; +#endif /* DEBUG */ +#ifdef __linux__ + retval = check_mntent_file("/proc/mounts", file, mount_flags, + mtpt, mtlen); + if (retval == 0 && (*mount_flags != 0)) + return 0; +#endif /* __linux__ */ +#if defined(MOUNTED) || defined(_PATH_MOUNTED) +#ifndef MOUNTED +#define MOUNTED _PATH_MOUNTED +#endif /* MOUNTED */ + retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); + return retval; +#else + *mount_flags = 0; + return 0; +#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ +} + +#else +#if defined(HAVE_GETMNTINFO) + +static errcode_t check_getmntinfo(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + struct statfs *mp; + int len, n; + const char *s1; + char *s2; + + n = getmntinfo(&mp, MNT_NOWAIT); + if (n == 0) + return errno; + + len = sizeof(_PATH_DEV) - 1; + s1 = file; + if (strncmp(_PATH_DEV, s1, len) == 0) + s1 += len; + + *mount_flags = 0; + while (--n >= 0) { + s2 = mp->f_mntfromname; + if (strncmp(_PATH_DEV, s2, len) == 0) { + s2 += len - 1; + *s2 = 'r'; + } + if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { + *mount_flags = EXT2_MF_MOUNTED; + break; + } + ++mp; + } + if (mtpt) + strncpy(mtpt, mp->f_mntonname, mtlen); + return 0; +} +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ + +/* + * Check to see if we're dealing with the swap device. + */ +static int is_swap_device(const char *file) +{ + FILE *f; + char buf[1024], *cp; + dev_t file_dev; + struct stat st_buf; + int ret = 0; + + file_dev = 0; +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + if ((stat(file, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode)) + file_dev = st_buf.st_rdev; +#endif /* __GNU__ */ + + if (!(f = fopen("/proc/swaps", "r"))) + return 0; + /* Skip the first line */ + if (!fgets(buf, sizeof(buf), f)) + goto leave; + if (*buf && strncmp(buf, "Filename\t", 9)) + /* Linux <=2.6.19 contained a bug in the /proc/swaps + * code where the header would not be displayed + */ + goto valid_first_line; + + while (fgets(buf, sizeof(buf), f)) { +valid_first_line: + if ((cp = strchr(buf, ' ')) != NULL) + *cp = 0; + if ((cp = strchr(buf, '\t')) != NULL) + *cp = 0; + if (strcmp(buf, file) == 0) { + ret++; + break; + } +#ifndef __GNU__ + if (file_dev && (stat(buf, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode) && + file_dev == st_buf.st_rdev) { + ret++; + break; + } +#endif /* __GNU__ */ + } + +leave: + fclose(f); + return ret; +} + + +/* + * ext2fs_check_mount_point() fills determines if the device is + * mounted or otherwise busy, and fills in mount_flags with one or + * more of the following flags: EXT2_MF_MOUNTED, EXT2_MF_ISROOT, + * EXT2_MF_READONLY, EXT2_MF_SWAP, and EXT2_MF_BUSY. If mtpt is + * non-NULL, the directory where the device is mounted is copied to + * where mtpt is pointing, up to mtlen characters. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen) +{ + errcode_t retval = 0; + + if (is_swap_device(device)) { + *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; + strncpy(mtpt, "", mtlen); + } else { +#ifdef HAVE_MNTENT_H + retval = check_mntent(device, mount_flags, mtpt, mtlen); +#else +#ifdef HAVE_GETMNTINFO + retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); +#else + *mount_flags = 0; +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ + } + if (retval) + return retval; + +#ifdef __linux__ /* This only works on Linux 2.6+ systems */ + if ((stat(device, &st_buf) != 0) || + !S_ISBLK(st_buf.st_mode)) + return 0; + fd = open(device, O_RDONLY | O_EXCL); + if (fd < 0) { + if (errno == EBUSY) + *mount_flags |= EXT2_MF_BUSY; + } else + close(fd); +#endif + + return 0; +} + +/* + * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, + * EXT2_MF_READONLY, and EXT2_MF_ROOT + * + */ +errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) +{ + return ext2fs_check_mount_point(file, mount_flags, NULL, 0); +} + +#ifdef DEBUG +int main(int argc, char **argv) +{ + int retval, mount_flags; + char mntpt[80]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + add_error_table(&et_ext2_error_table); + mntpt[0] = 0; + retval = ext2fs_check_mount_point(argv[1], &mount_flags, + mntpt, sizeof(mntpt)); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_check_if_mounted"); + exit(1); + } + printf("Device %s reports flags %02x\n", argv[1], mount_flags); + if (mount_flags & EXT2_MF_BUSY) + printf("\t%s is apparently in use.\n", argv[1]); + if (mount_flags & EXT2_MF_MOUNTED) + printf("\t%s is mounted.\n", argv[1]); + if (mount_flags & EXT2_MF_SWAP) + printf("\t%s is a swap device.\n", argv[1]); + if (mount_flags & EXT2_MF_READONLY) + printf("\t%s is read-only.\n", argv[1]); + if (mount_flags & EXT2_MF_ISROOT) + printf("\t%s is the root filesystem.\n", argv[1]); + if (mntpt[0]) + printf("\t%s is mounted on %s.\n", argv[1], mntpt); + exit(0); +} +#endif /* DEBUG */ diff --git a/lib/libext2fs/orig/jfs_compat.h b/lib/libext2fs/orig/jfs_compat.h new file mode 100644 index 0000000..7b8aafd --- /dev/null +++ b/lib/libext2fs/orig/jfs_compat.h @@ -0,0 +1,68 @@ + +#ifndef _JFS_COMPAT_H +#define _JFS_COMPAT_H + +#include "kernel-list.h" +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#define printk printf +#define KERN_ERR "" +#define KERN_DEBUG "" + +#define READ 0 +#define WRITE 1 + +#define cpu_to_be32(n) htonl(n) +#define be32_to_cpu(n) ntohl(n) + +typedef unsigned int tid_t; +typedef struct journal_s journal_t; + +struct buffer_head; +struct inode; + +struct journal_s +{ + unsigned long j_flags; + int j_errno; + struct buffer_head * j_sb_buffer; + struct journal_superblock_s *j_superblock; + int j_format_version; + unsigned long j_head; + unsigned long j_tail; + unsigned long j_free; + unsigned long j_first, j_last; + kdev_t j_dev; + kdev_t j_fs_dev; + int j_blocksize; + unsigned int j_blk_offset; + unsigned int j_maxlen; + struct inode * j_inode; + tid_t j_tail_sequence; + tid_t j_transaction_sequence; + __u8 j_uuid[16]; + struct jbd_revoke_table_s *j_revoke; + tid_t j_failed_commit; +}; + +#define J_ASSERT(assert) \ + do { if (!(assert)) { \ + printf ("Assertion failure in %s() at %s line %d: " \ + "\"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + fatal_error(e2fsck_global_ctx, 0); \ + } } while (0) + +#define is_journal_abort(x) 0 + +#define BUFFER_TRACE(bh, info) do {} while (0) + +/* Need this so we can compile with configure --enable-gcc-wall */ +#ifdef NO_INLINE_FUNCS +#define inline +#endif + +#endif /* _JFS_COMPAT_H */ diff --git a/lib/libext2fs/orig/jfs_dat.h b/lib/libext2fs/orig/jfs_dat.h new file mode 100644 index 0000000..62778c6 --- /dev/null +++ b/lib/libext2fs/orig/jfs_dat.h @@ -0,0 +1,64 @@ +/* + * jfs_dat.h --- stripped down header file which only contains the JFS + * on-disk data structures + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK 3 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ +} journal_block_tag_t; + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock + */ +typedef struct journal_superblock_s +{ + journal_header_t s_header; + + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +} journal_superblock_t; + diff --git a/lib/libext2fs/orig/jfs_user.h b/lib/libext2fs/orig/jfs_user.h new file mode 100644 index 0000000..3a52123 --- /dev/null +++ b/lib/libext2fs/orig/jfs_user.h @@ -0,0 +1,8 @@ +#ifndef _JFS_USER_H +#define _JFS_USER_H + +typedef unsigned short kdev_t; + +#include "kernel-jbd.h" + +#endif /* _JFS_USER_H */ diff --git a/lib/libext2fs/orig/kernel-jbd.h b/lib/libext2fs/orig/kernel-jbd.h new file mode 100644 index 0000000..066c031 --- /dev/null +++ b/lib/libext2fs/orig/kernel-jbd.h @@ -0,0 +1,952 @@ +/* + * linux/include/linux/jbd.h + * + * Written by Stephen C. Tweedie + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * Definitions for transaction data structures for the buffer cache + * filesystem journaling support. + */ + +#ifndef _LINUX_JBD_H +#define _LINUX_JBD_H + +#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__) + +/* Allow this file to be included directly into e2fsprogs */ +#ifndef __KERNEL__ +#include "jfs_compat.h" +#define JFS_DEBUG +#define jfs_debug jbd_debug +#else + +#include +#include +#include +#endif + +#ifndef __GNUC__ +#define __FUNCTION__ "" +#endif + +#define journal_oom_retry 1 + +#ifdef __STDC__ +#ifdef CONFIG_JBD_DEBUG +/* + * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal + * consistency checks. By default we don't do this unless + * CONFIG_JBD_DEBUG is on. + */ +#define JBD_EXPENSIVE_CHECKING +extern int journal_enable_debug; + +#define jbd_debug(n, f, a...) \ + do { \ + if ((n) <= journal_enable_debug) { \ + printk (KERN_DEBUG "(%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } \ + } while (0) +#else +#ifdef __GNUC__ +#define jbd_debug(f, a...) /**/ +#else +#define jbd_debug(f, ...) /**/ +#endif +#endif +#else +#define jbd_debug(x) /* AIX doesn't do STDC */ +#endif + +extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); +#define jbd_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) +#define jbd_rep_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) + +#define JFS_MIN_JOURNAL_BLOCKS 1024 + +#ifdef __KERNEL__ +typedef struct handle_s handle_t; /* Atomic operation type */ +typedef struct journal_s journal_t; /* Journal control structure */ +#endif + +/* + * Internal structures used by the logging mechanism: + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK_V1 3 +#define JFS_SUPERBLOCK_V2 4 +#define JFS_REVOKE_BLOCK 5 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + +/* + * Checksum types. + */ +#define JBD2_CRC32_CHKSUM 1 +#define JBD2_MD5_CHKSUM 2 +#define JBD2_SHA1_CHKSUM 3 + +#define JBD2_CRC32_CHKSUM_SIZE 4 + +#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) +/* + * Commit block header for storing transactional checksums: + */ +struct commit_header { + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; + unsigned char h_chksum_type; + unsigned char h_chksum_size; + unsigned char h_padding[2]; + __u32 h_chksum[JBD2_CHECKSUM_BYTES]; + __u64 h_commit_sec; + __u32 h_commit_nsec; +}; + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ + __u32 t_blocknr_high; /* most-significant high 32bits. */ +} journal_block_tag_t; + +#define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t)) +#define JBD_TAG_SIZE32 (8) + +/* + * The revoke descriptor: used on disk to describe a series of blocks to + * be revoked from the log + */ +typedef struct journal_revoke_header_s +{ + journal_header_t r_header; + int r_count; /* Count of bytes used in the block */ +} journal_revoke_header_t; + + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock. All fields are in big-endian byte order. + */ +typedef struct journal_superblock_s +{ +/* 0x0000 */ + journal_header_t s_header; + +/* 0x000C */ + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + +/* 0x0018 */ + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +/* 0x0020 */ + /* Error value, as set by journal_abort(). */ + __s32 s_errno; + +/* 0x0024 */ + /* Remaining fields are only valid in a version-2 superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ +/* 0x0030 */ + __u8 s_uuid[16]; /* 128-bit uuid for journal */ + +/* 0x0040 */ + __u32 s_nr_users; /* Nr of filesystems sharing log */ + + __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ + +/* 0x0048 */ + __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ + __u32 s_max_trans_data; /* Limit of data blocks per trans. */ + +/* 0x0050 */ + __u32 s_padding[44]; + +/* 0x0100 */ + __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ +/* 0x0400 */ +} journal_superblock_t; + +#define JFS_HAS_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) +#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) +#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) + +#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 +#define JFS_FEATURE_INCOMPAT_64BIT 0x00000002 +#define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 + +/* Features known to this kernel version: */ +#define JFS_KNOWN_COMPAT_FEATURES 0 +#define JFS_KNOWN_ROCOMPAT_FEATURES 0 +#define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\ + JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\ + JFS_FEATURE_INCOMPAT_64BIT) + +#ifdef __KERNEL__ + +#include +#include + +#define JBD_ASSERTIONS +#ifdef JBD_ASSERTIONS +#define J_ASSERT(assert) \ +do { \ + if (!(assert)) { \ + printk (KERN_EMERG \ + "Assertion failure in %s() at %s:%d: \"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + BUG(); \ + } \ +} while (0) + +#if defined(CONFIG_BUFFER_DEBUG) +void buffer_assertion_failure(struct buffer_head *bh); +#define J_ASSERT_BH(bh, expr) \ + do { \ + if (!(expr)) \ + buffer_assertion_failure(bh); \ + J_ASSERT(expr); \ + } while (0) +#define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr) +#else +#define J_ASSERT_BH(bh, expr) J_ASSERT(expr) +#define J_ASSERT_JH(jh, expr) J_ASSERT(expr) +#endif + +#else +#define J_ASSERT(assert) +#endif /* JBD_ASSERTIONS */ + +enum jbd_state_bits { + BH_JWrite + = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */ + BH_Freed, /* 1 if buffer has been freed (truncated) */ + BH_Revoked, /* 1 if buffer has been revoked from the log */ + BH_RevokeValid, /* 1 if buffer revoked flag is valid */ + BH_JBDDirty, /* 1 if buffer is dirty but journaled */ +}; + +/* Return true if the buffer is one which JBD is managing */ +static inline int buffer_jbd(struct buffer_head *bh) +{ + return __buffer_state(bh, JBD); +} + +static inline struct buffer_head *jh2bh(struct journal_head *jh) +{ + return jh->b_bh; +} + +static inline struct journal_head *bh2jh(struct buffer_head *bh) +{ + return bh->b_private; +} + +struct jbd_revoke_table_s; + +/* The handle_t type represents a single atomic update being performed + * by some process. All filesystem modifications made by the process go + * through this handle. Recursive operations (such as quota operations) + * are gathered into a single update. + * + * The buffer credits field is used to account for journaled buffers + * being modified by the running process. To ensure that there is + * enough log space for all outstanding operations, we need to limit the + * number of outstanding buffers possible at any time. When the + * operation completes, any buffer credits not used are credited back to + * the transaction, so that at all times we know how many buffers the + * outstanding updates on a transaction might possibly touch. */ + +struct handle_s +{ + /* Which compound transaction is this update a part of? */ + transaction_t * h_transaction; + + /* Number of remaining buffers we are allowed to dirty: */ + int h_buffer_credits; + + /* Reference count on this handle */ + int h_ref; + + /* Field for caller's use to track errors through large fs + operations */ + int h_err; + + /* Flags */ + unsigned int h_sync: 1; /* sync-on-close */ + unsigned int h_jdata: 1; /* force data journaling */ + unsigned int h_aborted: 1; /* fatal error on handle */ +}; + + +/* The transaction_t type is the guts of the journaling mechanism. It + * tracks a compound transaction through its various states: + * + * RUNNING: accepting new updates + * LOCKED: Updates still running but we don't accept new ones + * RUNDOWN: Updates are tidying up but have finished requesting + * new buffers to modify (state not used for now) + * FLUSH: All updates complete, but we are still writing to disk + * COMMIT: All data on disk, writing commit record + * FINISHED: We still have to keep the transaction for checkpointing. + * + * The transaction keeps track of all of the buffers modified by a + * running transaction, and all of the buffers committed but not yet + * flushed to home for finished transactions. + */ + +struct transaction_s +{ + /* Pointer to the journal for this transaction. */ + journal_t * t_journal; + + /* Sequence number for this transaction */ + tid_t t_tid; + + /* Transaction's current state */ + enum { + T_RUNNING, + T_LOCKED, + T_RUNDOWN, + T_FLUSH, + T_COMMIT, + T_FINISHED + } t_state; + + /* Where in the log does this transaction's commit start? */ + unsigned long t_log_start; + + /* Doubly-linked circular list of all inodes owned by this + transaction */ /* AKPM: unused */ + struct inode * t_ilist; + + /* Number of buffers on the t_buffers list */ + int t_nr_buffers; + + /* Doubly-linked circular list of all buffers reserved but not + yet modified by this transaction */ + struct journal_head * t_reserved_list; + + /* Doubly-linked circular list of all metadata buffers owned by this + transaction */ + struct journal_head * t_buffers; + + /* + * Doubly-linked circular list of all data buffers still to be + * flushed before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_sync_datalist; + + /* + * Doubly-linked circular list of all writepage data buffers + * still to be written before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_async_datalist; + + /* Doubly-linked circular list of all forget buffers (superceded + buffers which we can un-checkpoint once this transaction + commits) */ + struct journal_head * t_forget; + + /* + * Doubly-linked circular list of all buffers still to be + * flushed before this transaction can be checkpointed. + */ + /* Protected by journal_datalist_lock */ + struct journal_head * t_checkpoint_list; + + /* Doubly-linked circular list of temporary buffers currently + undergoing IO in the log */ + struct journal_head * t_iobuf_list; + + /* Doubly-linked circular list of metadata buffers being + shadowed by log IO. The IO buffers on the iobuf list and the + shadow buffers on this list match each other one for one at + all times. */ + struct journal_head * t_shadow_list; + + /* Doubly-linked circular list of control buffers being written + to the log. */ + struct journal_head * t_log_list; + + /* Number of outstanding updates running on this transaction */ + int t_updates; + + /* Number of buffers reserved for use by all handles in this + * transaction handle but not yet modified. */ + int t_outstanding_credits; + + /* + * Forward and backward links for the circular list of all + * transactions awaiting checkpoint. + */ + /* Protected by journal_datalist_lock */ + transaction_t *t_cpnext, *t_cpprev; + + /* When will the transaction expire (become due for commit), in + * jiffies ? */ + unsigned long t_expires; + + /* How many handles used this transaction? */ + int t_handle_count; +}; + + +/* The journal_t maintains all of the journaling state information for a + * single filesystem. It is linked to from the fs superblock structure. + * + * We use the journal_t to keep track of all outstanding transaction + * activity on the filesystem, and to manage the state of the log + * writing process. */ + +struct journal_s +{ + /* General journaling state flags */ + unsigned long j_flags; + + /* Is there an outstanding uncleared error on the journal (from + * a prior abort)? */ + int j_errno; + + /* The superblock buffer */ + struct buffer_head * j_sb_buffer; + journal_superblock_t * j_superblock; + + /* Version of the superblock format */ + int j_format_version; + + /* Number of processes waiting to create a barrier lock */ + int j_barrier_count; + + /* The barrier lock itself */ + struct semaphore j_barrier; + + /* Transactions: The current running transaction... */ + transaction_t * j_running_transaction; + + /* ... the transaction we are pushing to disk ... */ + transaction_t * j_committing_transaction; + + /* ... and a linked circular list of all transactions waiting + * for checkpointing. */ + /* Protected by journal_datalist_lock */ + transaction_t * j_checkpoint_transactions; + + /* Wait queue for waiting for a locked transaction to start + committing, or for a barrier lock to be released */ + wait_queue_head_t j_wait_transaction_locked; + + /* Wait queue for waiting for checkpointing to complete */ + wait_queue_head_t j_wait_logspace; + + /* Wait queue for waiting for commit to complete */ + wait_queue_head_t j_wait_done_commit; + + /* Wait queue to trigger checkpointing */ + wait_queue_head_t j_wait_checkpoint; + + /* Wait queue to trigger commit */ + wait_queue_head_t j_wait_commit; + + /* Wait queue to wait for updates to complete */ + wait_queue_head_t j_wait_updates; + + /* Semaphore for locking against concurrent checkpoints */ + struct semaphore j_checkpoint_sem; + + /* The main journal lock, used by lock_journal() */ + struct semaphore j_sem; + + /* Journal head: identifies the first unused block in the journal. */ + unsigned long j_head; + + /* Journal tail: identifies the oldest still-used block in the + * journal. */ + unsigned long j_tail; + + /* Journal free: how many free blocks are there in the journal? */ + unsigned long j_free; + + /* Journal start and end: the block numbers of the first usable + * block and one beyond the last usable block in the journal. */ + unsigned long j_first, j_last; + + /* Device, blocksize and starting block offset for the location + * where we store the journal. */ + kdev_t j_dev; + int j_blocksize; + unsigned int j_blk_offset; + + /* Device which holds the client fs. For internal journal this + * will be equal to j_dev. */ + kdev_t j_fs_dev; + + /* Total maximum capacity of the journal region on disk. */ + unsigned int j_maxlen; + + /* Optional inode where we store the journal. If present, all + * journal block numbers are mapped into this inode via + * bmap(). */ + struct inode * j_inode; + + /* Sequence number of the oldest transaction in the log */ + tid_t j_tail_sequence; + /* Sequence number of the next transaction to grant */ + tid_t j_transaction_sequence; + /* Sequence number of the most recently committed transaction */ + tid_t j_commit_sequence; + /* Sequence number of the most recent transaction wanting commit */ + tid_t j_commit_request; + + /* Journal uuid: identifies the object (filesystem, LVM volume + * etc) backed by this journal. This will eventually be + * replaced by an array of uuids, allowing us to index multiple + * devices within a single journal and to perform atomic updates + * across them. */ + + __u8 j_uuid[16]; + + /* Pointer to the current commit thread for this journal */ + struct task_struct * j_task; + + /* Maximum number of metadata buffers to allow in a single + * compound commit transaction */ + int j_max_transaction_buffers; + + /* What is the maximum transaction lifetime before we begin a + * commit? */ + unsigned long j_commit_interval; + + /* The timer used to wakeup the commit thread: */ + struct timer_list * j_commit_timer; + int j_commit_timer_active; + + /* Link all journals together - system-wide */ + struct list_head j_all_journals; + + /* The revoke table: maintains the list of revoked blocks in the + current transaction. */ + struct jbd_revoke_table_s *j_revoke; + + /* Failed journal commit ID */ + unsigned int j_failed_commit; +}; + +/* + * Journal flag definitions + */ +#define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */ +#define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */ +#define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */ +#define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ +#define JFS_LOADED 0x010 /* The journal superblock has been loaded */ + +/* + * Function declarations for the journaling transaction and buffer + * management + */ + +/* Filing buffers */ +extern void __journal_unfile_buffer(struct journal_head *); +extern void journal_unfile_buffer(struct journal_head *); +extern void __journal_refile_buffer(struct journal_head *); +extern void journal_refile_buffer(struct journal_head *); +extern void __journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_free_buffer(struct journal_head *bh); +extern void journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_clean_data_list(transaction_t *transaction); + +/* Log buffer allocation */ +extern struct journal_head * journal_get_descriptor_buffer(journal_t *); +extern unsigned long journal_next_log_block(journal_t *); + +/* Commit management */ +extern void journal_commit_transaction(journal_t *); + +/* Checkpoint list management */ +int __journal_clean_checkpoint_list(journal_t *journal); +extern void journal_remove_checkpoint(struct journal_head *); +extern void __journal_remove_checkpoint(struct journal_head *); +extern void journal_insert_checkpoint(struct journal_head *, transaction_t *); +extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *); + +/* Buffer IO */ +extern int +journal_write_metadata_buffer(transaction_t *transaction, + struct journal_head *jh_in, + struct journal_head **jh_out, + int blocknr); + +/* Transaction locking */ +extern void __wait_on_journal (journal_t *); + +/* + * Journal locking. + * + * We need to lock the journal during transaction state changes so that + * nobody ever tries to take a handle on the running transaction while + * we are in the middle of moving it to the commit phase. + * + * Note that the locking is completely interrupt unsafe. We never touch + * journal structures from interrupts. + * + * In 2.2, the BKL was required for lock_journal. This is no longer + * the case. + */ + +static inline void lock_journal(journal_t *journal) +{ + down(&journal->j_sem); +} + +/* This returns zero if we acquired the semaphore */ +static inline int try_lock_journal(journal_t * journal) +{ + return down_trylock(&journal->j_sem); +} + +static inline void unlock_journal(journal_t * journal) +{ + up(&journal->j_sem); +} + + +static inline handle_t *journal_current_handle(void) +{ + return current->journal_info; +} + +/* The journaling code user interface: + * + * Create and destroy handles + * Register buffer modifications against the current transaction. + */ + +extern handle_t *journal_start(journal_t *, int nblocks); +extern handle_t *journal_try_start(journal_t *, int nblocks); +extern int journal_restart (handle_t *, int nblocks); +extern int journal_extend (handle_t *, int nblocks); +extern int journal_get_write_access (handle_t *, struct buffer_head *); +extern int journal_get_create_access (handle_t *, struct buffer_head *); +extern int journal_get_undo_access (handle_t *, struct buffer_head *); +extern int journal_dirty_data (handle_t *, + struct buffer_head *, int async); +extern int journal_dirty_metadata (handle_t *, struct buffer_head *); +extern void journal_release_buffer (handle_t *, struct buffer_head *); +extern void journal_forget (handle_t *, struct buffer_head *); +extern void journal_sync_buffer (struct buffer_head *); +extern int journal_flushpage(journal_t *, struct page *, unsigned long); +extern int journal_try_to_free_buffers(journal_t *, struct page *, int); +extern int journal_stop(handle_t *); +extern int journal_flush (journal_t *); + +extern void journal_lock_updates (journal_t *); +extern void journal_unlock_updates (journal_t *); + +extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev, + int start, int len, int bsize); +extern journal_t * journal_init_inode (struct inode *); +extern int journal_update_format (journal_t *); +extern int journal_check_used_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_check_available_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_set_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_create (journal_t *); +extern int journal_load (journal_t *journal); +extern void journal_destroy (journal_t *); +extern int journal_recover (journal_t *journal); +extern int journal_wipe (journal_t *, int); +extern int journal_skip_recovery (journal_t *); +extern void journal_update_superblock (journal_t *, int); +extern void __journal_abort (journal_t *); +extern void journal_abort (journal_t *, int); +extern int journal_errno (journal_t *); +extern void journal_ack_err (journal_t *); +extern int journal_clear_err (journal_t *); +extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr); +extern int journal_force_commit(journal_t *journal); + +/* + * journal_head management + */ +extern struct journal_head + *journal_add_journal_head(struct buffer_head *bh); +extern void journal_remove_journal_head(struct buffer_head *bh); +extern void __journal_remove_journal_head(struct buffer_head *bh); +extern void journal_unlock_journal_head(struct journal_head *jh); + +/* Primary revoke support */ +#define JOURNAL_REVOKE_DEFAULT_HASH 256 +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +extern void journal_destroy_revoke(journal_t *); +extern int journal_revoke (handle_t *, + unsigned long, struct buffer_head *); +extern int journal_cancel_revoke(handle_t *, struct journal_head *); +extern void journal_write_revoke_records(journal_t *, transaction_t *); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +/* The log thread user interface: + * + * Request space in the current transaction, and force transaction commit + * transitions on demand. + */ + +extern int log_space_left (journal_t *); /* Called with journal locked */ +extern tid_t log_start_commit (journal_t *, transaction_t *); +extern void log_wait_commit (journal_t *, tid_t); +extern int log_do_checkpoint (journal_t *, int); + +extern void log_wait_for_space(journal_t *, int nblocks); +extern void __journal_drop_transaction(journal_t *, transaction_t *); +extern int cleanup_journal_tail(journal_t *); + +/* Reduce journal memory usage by flushing */ +extern void shrink_journal_memory(void); + +/* Debugging code only: */ + +#define jbd_ENOSYS() \ +do { \ + printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \ + current->state = TASK_UNINTERRUPTIBLE; \ + schedule(); \ +} while (1) + +/* + * is_journal_abort + * + * Simple test wrapper function to test the JFS_ABORT state flag. This + * bit, when set, indicates that we have had a fatal error somewhere, + * either inside the journaling layer or indicated to us by the client + * (eg. ext3), and that we and should not commit any further + * transactions. + */ + +static inline int is_journal_aborted(journal_t *journal) +{ + return journal->j_flags & JFS_ABORT; +} + +static inline int is_handle_aborted(handle_t *handle) +{ + if (handle->h_aborted) + return 1; + return is_journal_aborted(handle->h_transaction->t_journal); +} + +static inline void journal_abort_handle(handle_t *handle) +{ + handle->h_aborted = 1; +} + +/* Not all architectures define BUG() */ +#ifndef BUG +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + * ((char *) 0) = 0; \ + } while (0) +#endif /* BUG */ + +#else + +extern int journal_recover (journal_t *journal); +extern int journal_skip_recovery (journal_t *); + +/* Primary revoke support */ +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +extern void journal_destroy_revoke(journal_t *); +#endif /* __KERNEL__ */ + +static inline int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused)); +static inline int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused)); + +/* Comparison functions for transaction IDs: perform comparisons using + * modulo arithmetic so that they work over sequence number wraps. */ + +static inline int tid_gt(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference > 0); +} + +static inline int tid_geq(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference >= 0); +} + +extern int journal_blocks_per_page(struct inode *inode); + +/* + * Definitions which augment the buffer_head layer + */ + +/* journaling buffer types */ +#define BJ_None 0 /* Not journaled */ +#define BJ_SyncData 1 /* Normal data: flush before commit */ +#define BJ_AsyncData 2 /* writepage data: wait on it before commit */ +#define BJ_Metadata 3 /* Normal journaled metadata */ +#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_IO 5 /* Buffer is for temporary IO use */ +#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 7 /* Buffer contains log descriptors */ +#define BJ_Reserved 8 /* Buffer is reserved for access by journal */ +#define BJ_Types 9 + +extern int jbd_blocks_per_page(struct inode *inode); + +#ifdef __KERNEL__ + +extern spinlock_t jh_splice_lock; +/* + * Once `expr1' has been found true, take jh_splice_lock + * and then reevaluate everything. + */ +#define SPLICE_LOCK(expr1, expr2) \ + ({ \ + int ret = (expr1); \ + if (ret) { \ + spin_lock(&jh_splice_lock); \ + ret = (expr1) && (expr2); \ + spin_unlock(&jh_splice_lock); \ + } \ + ret; \ + }) + +/* + * A number of buffer state predicates. They test for + * buffer_jbd() because they are used in core kernel code. + * + * These will be racy on SMP unless we're *sure* that the + * buffer won't be detached from the journalling system + * in parallel. + */ + +/* Return true if the buffer is on journal list `list' */ +static inline int buffer_jlist_eq(struct buffer_head *bh, int list) +{ + return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list); +} + +/* Return true if this bufer is dirty wrt the journal */ +static inline int buffer_jdirty(struct buffer_head *bh) +{ + return buffer_jbd(bh) && __buffer_state(bh, JBDDirty); +} + +/* Return true if it's a data buffer which journalling is managing */ +static inline int buffer_jbd_data(struct buffer_head *bh) +{ + return SPLICE_LOCK(buffer_jbd(bh), + bh2jh(bh)->b_jlist == BJ_SyncData || + bh2jh(bh)->b_jlist == BJ_AsyncData); +} + +#ifdef CONFIG_SMP +#define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock)) +#else +#define assert_spin_locked(lock) do {} while(0) +#endif + +#define buffer_trace_init(bh) do {} while (0) +#define print_buffer_fields(bh) do {} while (0) +#define print_buffer_trace(bh) do {} while (0) +#define BUFFER_TRACE(bh, info) do {} while (0) +#define BUFFER_TRACE2(bh, bh2, info) do {} while (0) +#define JBUFFER_TRACE(jh, info) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */ + +/* + * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD + * go here. + */ + +#if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE)) + +#define J_ASSERT(expr) do {} while (0) +#define J_ASSERT_BH(bh, expr) do {} while (0) +#define buffer_jbd(bh) 0 +#define buffer_jlist_eq(bh, val) 0 +#define journal_buffer_journal_lru(bh) 0 + +#endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */ +#endif /* _LINUX_JBD_H */ diff --git a/lib/libext2fs/orig/kernel-list.h b/lib/libext2fs/orig/kernel-list.h new file mode 100644 index 0000000..e07d06b --- /dev/null +++ b/lib/libext2fs/orig/kernel-list.h @@ -0,0 +1,112 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = { &name, &name } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define __inline__ +#endif + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Insert a new entry after the specified head.. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Insert a new entry at the tail + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif diff --git a/lib/libext2fs/orig/link.c b/lib/libext2fs/orig/link.c new file mode 100644 index 0000000..4cc8426 --- /dev/null +++ b/lib/libext2fs/orig/link.c @@ -0,0 +1,153 @@ +/* + * link.c --- create links in a ext2fs directory + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + ext2_filsys fs; + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + int done; + unsigned int blocksize; + errcode_t err; + struct ext2_super_block *sb; +}; + +static int link_proc(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *next; + unsigned int rec_len, min_rec_len, curr_rec_len; + int ret = 0; + + rec_len = EXT2_DIR_REC_LEN(ls->namelen); + + ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); + if (ls->err) + return DIRENT_ABORT; + + /* + * See if the following directory entry (if any) is unused; + * if so, absorb it into this one. + */ + next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); + if ((offset + curr_rec_len < blocksize - 8) && + (next->inode == 0) && + (offset + curr_rec_len + next->rec_len <= blocksize)) { + curr_rec_len += next->rec_len; + ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + ret = DIRENT_CHANGED; + } + + /* + * If the directory entry is used, see if we can split the + * directory entry to make room for the new name. If so, + * truncate it and return. + */ + if (dirent->inode) { + min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); + if (curr_rec_len < (min_rec_len + rec_len)) + return ret; + rec_len = curr_rec_len - min_rec_len; + ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + next = (struct ext2_dir_entry *) (buf + offset + + dirent->rec_len); + next->inode = 0; + next->name_len = 0; + ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); + if (ls->err) + return DIRENT_ABORT; + return DIRENT_CHANGED; + } + + /* + * If we get this far, then the directory entry is not used. + * See if we can fit the request entry in. If so, do it. + */ + if (curr_rec_len < rec_len) + return ret; + dirent->inode = ls->inode; + dirent->name_len = ls->namelen; + strncpy(dirent->name, ls->name, ls->namelen); + if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) + dirent->name_len |= (ls->flags & 0x7) << 8; + + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +/* + * Note: the low 3 bits of the flags field are used as the directory + * entry filetype. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags) +{ + errcode_t retval; + struct link_struct ls; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.fs = fs; + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = flags; + ls.done = 0; + ls.sb = fs->super; + ls.blocksize = fs->blocksize; + ls.err = 0; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, link_proc, &ls); + if (retval) + return retval; + if (ls.err) + return ls.err; + + if (!ls.done) + return EXT2_ET_DIR_NO_SPACE; + + if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) + return retval; + + if (inode.i_flags & EXT2_INDEX_FL) { + inode.i_flags &= ~EXT2_INDEX_FL; + if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) + return retval; + } + + return 0; +} diff --git a/lib/libext2fs/orig/llseek.c b/lib/libext2fs/orig/llseek.c new file mode 100644 index 0000000..3b919be --- /dev/null +++ b/lib/libext2fs/orig/llseek.c @@ -0,0 +1,139 @@ +/* + * llseek.c -- stub calling the llseek system call + * + * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#ifdef __MSDOS__ +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifdef __linux__ + +#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) + +#define my_llseek lseek64 + +#else +#if defined(HAVE_LLSEEK) +#include + +#ifndef HAVE_LLSEEK_PROTOTYPE +extern long long llseek (int fd, long long offset, int origin); +#endif + +#define my_llseek llseek + +#else /* ! HAVE_LLSEEK */ + +#if SIZEOF_LONG == SIZEOF_LONG_LONG + +#define llseek lseek + +#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */ + +#include + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +#ifndef __i386__ +static int _llseek (unsigned int, unsigned long, + unsigned long, ext2_loff_t *, unsigned int); + +static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, + unsigned long, offset_low,ext2_loff_t *,result, + unsigned int, origin) +#endif + +static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin) +{ + ext2_loff_t result; + int retval; + +#ifndef __i386__ + retval = _llseek(fd, ((unsigned long long) offset) >> 32, +#else + retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32), +#endif + ((unsigned long long) offset) & 0xffffffff, + &result, origin); + return (retval == -1 ? (ext2_loff_t) retval : result); +} + +#endif /* __alpha__ || __ia64__ */ + +#endif /* HAVE_LLSEEK */ +#endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */ + +ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) +{ + ext2_loff_t result; + static int do_compat = 0; + + if ((sizeof(off_t) >= sizeof(ext2_loff_t)) || + (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) + return lseek(fd, (off_t) offset, origin); + + if (do_compat) { + errno = EINVAL; + return -1; + } + + result = my_llseek (fd, offset, origin); + if (result == -1 && errno == ENOSYS) { + /* + * Just in case this code runs on top of an old kernel + * which does not support the llseek system call + */ + do_compat++; + errno = EINVAL; + } + return result; +} + +#else /* !linux */ + +#ifndef EINVAL +#define EINVAL EXT2_ET_INVALID_ARGUMENT +#endif + +ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) +{ +#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) + return lseek64 (fd, offset, origin); +#else + if ((sizeof(off_t) < sizeof(ext2_loff_t)) && + (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { + errno = EINVAL; + return -1; + } + return lseek (fd, (off_t) offset, origin); +#endif +} + +#endif /* linux */ + + diff --git a/lib/libext2fs/orig/lookup.c b/lib/libext2fs/orig/lookup.c new file mode 100644 index 0000000..97aa088 --- /dev/null +++ b/lib/libext2fs/orig/lookup.c @@ -0,0 +1,69 @@ +/* + * lookup.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct lookup_struct { + const char *name; + int len; + ext2_ino_t *inode; + int found; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int lookup_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct lookup_struct *ls = (struct lookup_struct *) priv_data; + + if (ls->len != (dirent->name_len & 0xFF)) + return 0; + if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) + return 0; + *ls->inode = dirent->inode; + ls->found++; + return DIRENT_ABORT; +} + + +errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode) +{ + errcode_t retval; + struct lookup_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ls.name = name; + ls.len = namelen; + ls.inode = inode; + ls.found = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); + if (retval) + return retval; + + return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; +} + + diff --git a/lib/libext2fs/orig/mem_allocate.h b/lib/libext2fs/orig/mem_allocate.h new file mode 100644 index 0000000..1c5bd67 --- /dev/null +++ b/lib/libext2fs/orig/mem_allocate.h @@ -0,0 +1,26 @@ +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +extern __inline__ void* mem_alloc (size_t size) { + return malloc(size); +} + +extern __inline__ void* mem_realloc (void *p, size_t size) { + return realloc(p, size); +} + +extern __inline__ void* mem_align (size_t a, size_t size) { + #ifdef __wii__ + return memalign(32, size); + #else + return malloc(size); + #endif +} + +extern __inline__ void mem_free (void* mem) { + free(mem); +} + +#endif /* _MEM_ALLOCATE_H */ diff --git a/lib/libext2fs/orig/mkdir.c b/lib/libext2fs/orig/mkdir.c new file mode 100644 index 0000000..86c65da --- /dev/null +++ b/lib/libext2fs/orig/mkdir.c @@ -0,0 +1,142 @@ +/* + * mkdir.c --- make a directory in the filesystem + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name) +{ + errcode_t retval; + struct ext2_inode parent_inode, inode; + ext2_ino_t ino = inum; + ext2_ino_t scratch_ino; + blk64_t blk; + char *block = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * Allocate an inode, if necessary + */ + if (!ino) { + retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, + 0, &ino); + if (retval) + goto cleanup; + } + + /* + * Allocate a data block for the directory + */ + retval = ext2fs_new_block2(fs, 0, 0, &blk); + if (retval) + goto cleanup; + + /* + * Create a scratch template for the directory + */ + retval = ext2fs_new_dir_block(fs, ino, parent, &block); + if (retval) + goto cleanup; + + /* + * Get the parent's inode, if necessary + */ + if (parent != ino) { + retval = ext2fs_read_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } else + memset(&parent_inode, 0, sizeof(parent_inode)); + + /* + * Create the inode structure.... + */ + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); + inode.i_uid = inode.i_gid = 0; + ext2fs_iblk_set(fs, &inode, 1); + /* FIXME-64 */ + inode.i_block[0] = blk; + inode.i_links_count = 2; + inode.i_size = fs->blocksize; + + /* + * Write out the inode and inode data block + */ + retval = ext2fs_write_dir_block(fs, blk, block); + if (retval) + goto cleanup; + retval = ext2fs_write_new_inode(fs, ino, &inode); + if (retval) + goto cleanup; + + /* + * Link the directory into the filesystem hierarchy + */ + if (name) { + retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, + &scratch_ino); + if (!retval) { + retval = EXT2_ET_DIR_EXISTS; + name = 0; + goto cleanup; + } + if (retval != EXT2_ET_FILE_NOT_FOUND) + goto cleanup; + retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); + if (retval) + goto cleanup; + } + + /* + * Update parent inode's counts + */ + if (parent != ino) { + parent_inode.i_links_count++; + retval = ext2fs_write_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } + + /* + * Update accounting.... + */ + ext2fs_block_alloc_stats2(fs, blk, +1); + ext2fs_inode_alloc_stats2(fs, ino, +1, 1); + +cleanup: + if (block) + ext2fs_free_mem(&block); + return retval; + +} + + diff --git a/lib/libext2fs/orig/mkjournal.c b/lib/libext2fs/orig/mkjournal.c new file mode 100644 index 0000000..47fb92c --- /dev/null +++ b/lib/libext2fs/orig/mkjournal.c @@ -0,0 +1,601 @@ +/* + * mkjournal.c --- make a journal for a filesystem + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#ifdef GEKKO +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "jfs_user.h" + +/* + * This function automatically sets up the journal superblock and + * returns it as an allocated block. + */ +errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb) +{ + errcode_t retval; + journal_superblock_t *jsb; + + if (size < 1024) + return EXT2_ET_JOURNAL_TOO_SMALL; + + if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) + return retval; + + memset (jsb, 0, fs->blocksize); + + jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); + if (flags & EXT2_MKJOURNAL_V1_SUPER) + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); + else + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); + jsb->s_blocksize = htonl(fs->blocksize); + jsb->s_maxlen = htonl(size); + jsb->s_nr_users = htonl(1); + jsb->s_first = htonl(1); + jsb->s_sequence = htonl(1); + memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); + /* + * If we're creating an external journal device, we need to + * adjust these fields. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + jsb->s_nr_users = 0; + if (fs->blocksize == 1024) + jsb->s_first = htonl(3); + else + jsb->s_first = htonl(2); + } + + *ret_jsb = (char *) jsb; + return 0; +} + +/* + * This function writes a journal using POSIX routines. It is used + * for creating external journals and creating journals on live + * filesystems. + */ +static errcode_t write_journal_file(ext2_filsys fs, char *filename, + blk_t size, int flags) +{ + errcode_t retval; + char *buf = 0; + int fd, ret_size; + blk_t i; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + /* Open the device or journal file */ + if ((fd = open(filename, O_WRONLY)) < 0) { + retval = errno; + goto errout; + } + + /* Write the superblock out */ + retval = EXT2_ET_SHORT_WRITE; + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + memset(buf, 0, fs->blocksize); + + for (i = 1; i < size; i++) { + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + } + close(fd); + + retval = 0; +errout: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Convenience function which zeros out _num_ blocks starting at + * _blk_. In case of an error, the details of the error is returned + * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. + * Returns 0 on success, and an error code on an error. + * + * As a special case, if the first argument is NULL, then it will + * attempt to free the static zeroizing buffer. (This is to keep + * programs that check for memory leaks happy.) + */ +#define STRIDE_LENGTH 8 +errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, + blk64_t *ret_blk, int *ret_count) +{ + int j, count; + static char *buf; + errcode_t retval; + + /* If fs is null, clean up the static buffer and return */ + if (!fs) { + if (buf) { + free(buf); + buf = 0; + } + return 0; + } + /* Allocate the zeroizing buffer if necessary */ + if (!buf) { + buf = malloc(fs->blocksize * STRIDE_LENGTH); + if (!buf) + return ENOMEM; + memset(buf, 0, fs->blocksize * STRIDE_LENGTH); + } + /* OK, do the write loop */ + j=0; + while (j < num) { + if (blk % STRIDE_LENGTH) { + count = STRIDE_LENGTH - (blk % STRIDE_LENGTH); + if (count > (num - j)) + count = num - j; + } else { + count = num - j; + if (count > STRIDE_LENGTH) + count = STRIDE_LENGTH; + } + retval = io_channel_write_blk64(fs->io, blk, count, buf); + if (retval) { + if (ret_count) + *ret_count = count; + if (ret_blk) + *ret_blk = blk; + return retval; + } + j += count; blk += count; + } + return 0; +} + +errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, + blk_t *ret_blk, int *ret_count) +{ + blk64_t ret_blk2; + errcode_t retval; + + retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count); + if (retval) + *ret_blk = (blk_t) ret_blk2; + return retval; +} + +/* + * Helper function for creating the journal using direct I/O routines + */ +struct mkjournal_struct { + int num_blocks; + int newblocks; + blk64_t goal; + blk64_t blk_to_zero; + int zero_count; + char *buf; + errcode_t err; +}; + +static int mkjournal_proc(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; + blk64_t new_blk; + errcode_t retval; + + if (*blocknr) { + es->goal = *blocknr; + return 0; + } + retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt >= 0) + es->num_blocks--; + + es->newblocks++; + retval = 0; + if (blockcnt <= 0) + retval = io_channel_write_blk64(fs->io, new_blk, 1, es->buf); + else { + if (es->zero_count) { + if ((es->blk_to_zero + es->zero_count == new_blk) && + (es->zero_count < 1024)) + es->zero_count++; + else { + retval = ext2fs_zero_blocks2(fs, + es->blk_to_zero, + es->zero_count, + 0, 0); + es->zero_count = 0; + } + } + if (es->zero_count == 0) { + es->blk_to_zero = new_blk; + es->zero_count = 1; + } + } + + if (blockcnt == 0) + memset(es->buf, 0, fs->blocksize); + + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + *blocknr = es->goal = new_blk; + ext2fs_block_alloc_stats2(fs, new_blk, +1); + + if (es->num_blocks == 0) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; + +} + +/* + * This function creates a journal using direct I/O routines. + */ +static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, + blk64_t size, int flags) +{ + char *buf; + dgrp_t group, start, end, i, log_flex; + errcode_t retval; + struct ext2_inode inode; + struct mkjournal_struct es; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + if ((retval = ext2fs_read_bitmaps(fs))) + return retval; + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + return retval; + + if (inode.i_blocks > 0) + return EEXIST; + + es.num_blocks = size; + es.newblocks = 0; + es.buf = buf; + es.err = 0; + es.zero_count = 0; + + if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { + inode.i_flags |= EXT4_EXTENTS_FL; + if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) + return retval; + } + + /* + * Set the initial goal block to be roughly at the middle of + * the filesystem. Pick a group that has the largest number + * of free blocks. + */ + group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block) / 2); + log_flex = 1 << fs->super->s_log_groups_per_flex; + if (fs->super->s_log_groups_per_flex && (group > log_flex)) { + group = group & ~(log_flex - 1); + while ((group < fs->group_desc_count) && + ext2fs_bg_free_blocks_count(fs, group) == 0) + group++; + if (group == fs->group_desc_count) + group = 0; + start = group; + } else + start = (group > 0) ? group-1 : group; + end = ((group+1) < fs->group_desc_count) ? group+1 : group; + group = start; + for (i=start+1; i <= end; i++) + if (ext2fs_bg_free_blocks_count(fs, i) > + ext2fs_bg_free_blocks_count(fs, group)) + group = i; + + es.goal = (fs->super->s_blocks_per_group * group) + + fs->super->s_first_data_block; + + retval = ext2fs_block_iterate3(fs, journal_ino, BLOCK_FLAG_APPEND, + 0, mkjournal_proc, &es); + if (es.err) { + retval = es.err; + goto errout; + } + if (es.zero_count) { + retval = ext2fs_zero_blocks2(fs, es.blk_to_zero, + es.zero_count, 0, 0); + if (retval) + goto errout; + } + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + goto errout; + + inode.i_size += fs->blocksize * size; + ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); + inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); + inode.i_links_count = 1; + inode.i_mode = LINUX_S_IFREG | 0600; + + if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) + goto errout; + retval = 0; + + memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); + fs->super->s_jnl_blocks[16] = inode.i_size; + fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; + ext2fs_mark_super_dirty(fs); + +errout: + ext2fs_zero_blocks2(0, 0, 0, 0, 0); + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Find a reasonable journal file size (in blocks) given the number of blocks + * in the filesystem. For very small filesystems, it is not reasonable to + * have a journal that fills more than half of the filesystem. + */ +int ext2fs_default_journal_size(__u64 blocks) +{ + if (blocks < 2048) + return -1; + if (blocks < 32768) + return (1024); + if (blocks < 256*1024) + return (4096); + if (blocks < 512*1024) + return (8192); + if (blocks < 1024*1024) + return (16384); + return 32768; +} + +/* + * This function adds a journal device to a filesystem + */ +errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) +{ + struct stat st; + errcode_t retval; + char buf[1024]; + journal_superblock_t *jsb; + int start; + __u32 i, nr_users; + + /* Make sure the device exists and is a block device */ + if (stat(journal_dev->device_name, &st) < 0) + return errno; + + if (!S_ISBLK(st.st_mode)) + return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ + + /* Get the journal superblock */ + start = 1; + if (journal_dev->blocksize == 1024) + start++; + if ((retval = io_channel_read_blk64(journal_dev->io, start, -1024, + buf))) + return retval; + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) + return EXT2_ET_NO_JOURNAL_SB; + + if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + /* Check and see if this filesystem has already been added */ + nr_users = ntohl(jsb->s_nr_users); + for (i=0; i < nr_users; i++) { + if (memcmp(fs->super->s_uuid, + &jsb->s_users[i*16], 16) == 0) + break; + } + if (i >= nr_users) { + memcpy(&jsb->s_users[nr_users*16], + fs->super->s_uuid, 16); + jsb->s_nr_users = htonl(nr_users+1); + } + + /* Writeback the journal superblock */ + if ((retval = io_channel_write_blk64(journal_dev->io, start, -1024, buf))) + return retval; + + fs->super->s_journal_inum = 0; + fs->super->s_journal_dev = st.st_rdev; + memcpy(fs->super->s_journal_uuid, jsb->s_uuid, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + ext2fs_mark_super_dirty(fs); + return 0; +} + +/* + * This function adds a journal inode to a filesystem, using either + * POSIX routines if the filesystem is mounted, or using direct I/O + * functions if it is not. + */ +errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) +{ + errcode_t retval; + ext2_ino_t journal_ino; + struct stat st; + char jfile[1024]; + int mount_flags; + int fd = -1; + + if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, + jfile, sizeof(jfile)-10))) + return retval; + + if (mount_flags & EXT2_MF_MOUNTED) { + strcat(jfile, "/.journal"); + + /* + * If .../.journal already exists, make sure any + * immutable or append-only flags are cleared. + */ +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + (void) chflags (jfile, 0); +#else +#if HAVE_EXT2_IOCTLS + fd = open(jfile, O_RDONLY); + if (fd >= 0) { + f = 0; + ioctl(fd, EXT2_IOC_SETFLAGS, &f); + close(fd); + } +#endif +#endif + + /* Create the journal file */ + if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) + return errno; + + if ((retval = write_journal_file(fs, jfile, size, flags))) + goto errout; + + /* Get inode number of the journal file */ + if (fstat(fd, &st) < 0) { + retval = errno; + goto errout; + } + +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); +#else +#if HAVE_EXT2_IOCTLS + if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { + retval = errno; + goto errout; + } + f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; + retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); +#endif +#endif + if (retval) { + retval = errno; + goto errout; + } + + if (close(fd) < 0) { + retval = errno; + fd = -1; + goto errout; + } + journal_ino = st.st_ino; + } else { + if ((mount_flags & EXT2_MF_BUSY) && + !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { + retval = EBUSY; + goto errout; + } + journal_ino = EXT2_JOURNAL_INO; + if ((retval = write_journal_inode(fs, journal_ino, + size, flags))) + return retval; + } + + fs->super->s_journal_inum = journal_ino; + fs->super->s_journal_dev = 0; + memset(fs->super->s_journal_uuid, 0, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + + ext2fs_mark_super_dirty(fs); + return 0; +errout: + if (fd > 0) + close(fd); + return retval; +} + +#ifdef DEBUG +main(int argc, char **argv) +{ + errcode_t retval; + char *device_name; + ext2_filsys fs; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filesystem\n", argv[0]); + exit(1); + } + device_name = argv[1]; + + retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, + unix_io_manager, &fs); + if (retval) { + com_err(argv[0], retval, "while opening %s", device_name); + exit(1); + } + + retval = ext2fs_add_journal_inode(fs, 1024); + if (retval) { + com_err(argv[0], retval, "while adding journal to %s", + device_name); + exit(1); + } + retval = ext2fs_flush(fs); + if (retval) { + printf("Warning, had trouble writing out superblocks.\n"); + } + ext2fs_close(fs); + exit(0); + +} +#endif diff --git a/lib/libext2fs/orig/namei.c b/lib/libext2fs/orig/namei.c new file mode 100644 index 0000000..bc0ae61 --- /dev/null +++ b/lib/libext2fs/orig/namei.c @@ -0,0 +1,206 @@ +/* + * namei.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +/* #define NAMEI_DEBUG */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode); + +static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + ext2_ino_t inode, int link_count, + char *buf, ext2_ino_t *res_inode) +{ + char *pathname; + char *buffer = 0; + errcode_t retval; + struct ext2_inode ei; + +#ifdef NAMEI_DEBUG + printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", + root, dir, inode, link_count); + +#endif + retval = ext2fs_read_inode (fs, inode, &ei); + if (retval) return retval; + if (!LINUX_S_ISLNK (ei.i_mode)) { + *res_inode = inode; + return 0; + } + if (link_count++ > 5) { + return EXT2_ET_SYMLINK_LOOP; + } + /* FIXME-64: Actually, this is FIXME EXTENTS */ + if (ext2fs_inode_data_blocks(fs,&ei)) { + retval = ext2fs_get_mem(fs->blocksize, &buffer); + if (retval) + return retval; + retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); + if (retval) { + ext2fs_free_mem(&buffer); + return retval; + } + pathname = buffer; + } else + pathname = (char *)&(ei.i_block[0]); + retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, + link_count, buf, res_inode); + if (buffer) + ext2fs_free_mem(&buffer); + return retval; +} + +/* + * This routine interprets a pathname in the context of the current + * directory and the root directory, and returns the inode of the + * containing directory, and a pointer to the filename of the file + * (pointing into the pathname) and the length of the filename. + */ +static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + const char *pathname, int pathlen, + int link_count, char *buf, + const char **name, int *namelen, + ext2_ino_t *res_inode) +{ + char c; + const char *thisname; + int len; + ext2_ino_t inode; + errcode_t retval; + + if ((c = *pathname) == '/') { + dir = root; + pathname++; + pathlen--; + } + while (1) { + thisname = pathname; + for (len=0; --pathlen >= 0;len++) { + c = *(pathname++); + if (c == '/') + break; + } + if (pathlen < 0) + break; + retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); + if (retval) return retval; + retval = follow_link (fs, root, dir, inode, + link_count, buf, &dir); + if (retval) return retval; + } + *name = thisname; + *namelen = len; + *res_inode = dir; + return 0; +} + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode) +{ + const char *base_name; + int namelen; + ext2_ino_t dir, inode; + errcode_t retval; + +#ifdef NAMEI_DEBUG + printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", + root, base, pathlen, pathname, link_count); +#endif + retval = dir_namei(fs, root, base, pathname, pathlen, + link_count, buf, &base_name, &namelen, &dir); + if (retval) return retval; + if (!namelen) { /* special case: '/usr/' etc */ + *res_inode=dir; + return 0; + } + retval = ext2fs_lookup (fs, dir, base_name, namelen, buf, &inode); + if (retval) + return retval; + if (follow) { + retval = follow_link(fs, root, dir, inode, link_count, + buf, &inode); + if (retval) + return retval; + } +#ifdef NAMEI_DEBUG + printf("open_namei: (link_count=%d) returns %lu\n", + link_count, inode); +#endif + *res_inode = inode; + return 0; +} + +errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); + + ext2fs_free_mem(&buf); + return retval; +} + diff --git a/lib/libext2fs/orig/native.c b/lib/libext2fs/orig/native.c new file mode 100644 index 0000000..c71a95e --- /dev/null +++ b/lib/libext2fs/orig/native.c @@ -0,0 +1,27 @@ +/* + * native.c --- returns the ext2_flag for a native byte order + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +int ext2fs_native_flag(void) +{ +#ifdef WORDS_BIGENDIAN + return EXT2_FLAG_SWAP_BYTES; +#else + return 0; +#endif +} + + + diff --git a/lib/libext2fs/orig/newdir.c b/lib/libext2fs/orig/newdir.c new file mode 100644 index 0000000..6bc5719 --- /dev/null +++ b/lib/libext2fs/orig/newdir.c @@ -0,0 +1,77 @@ +/* + * newdir.c --- create a new directory block + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +/* + * Create new directory block + */ +errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block) +{ + struct ext2_dir_entry *dir = NULL; + errcode_t retval; + char *buf; + int rec_len; + int filetype = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memset(buf, 0, fs->blocksize); + dir = (struct ext2_dir_entry *) buf; + + retval = ext2fs_set_rec_len(fs, fs->blocksize, dir); + if (retval) + return retval; + + if (dir_ino) { + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE) + filetype = EXT2_FT_DIR << 8; + /* + * Set up entry for '.' + */ + dir->inode = dir_ino; + dir->name_len = 1 | filetype; + dir->name[0] = '.'; + rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1); + dir->rec_len = EXT2_DIR_REC_LEN(1); + + /* + * Set up entry for '..' + */ + dir = (struct ext2_dir_entry *) (buf + dir->rec_len); + retval = ext2fs_set_rec_len(fs, rec_len, dir); + if (retval) + return retval; + dir->inode = parent_ino; + dir->name_len = 2 | filetype; + dir->name[0] = '.'; + dir->name[1] = '.'; + + } + *block = buf; + return 0; +} diff --git a/lib/libext2fs/orig/openfs.c b/lib/libext2fs/orig/openfs.c new file mode 100644 index 0000000..eb04d02 --- /dev/null +++ b/lib/libext2fs/orig/openfs.c @@ -0,0 +1,411 @@ +/* + * openfs.c --- open an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" + + +#include "ext2fs.h" +#include "e2image.h" + +blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block, + dgrp_t i) +{ + int bg; + int has_super = 0; + blk64_t ret_blk; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (i < fs->super->s_first_meta_bg)) + return (group_block + i + 1); + + bg = EXT2_DESC_PER_BLOCK(fs->super) * i; + if (ext2fs_bg_has_super(fs, bg)) + has_super = 1; + ret_blk = ext2fs_group_first_block2(fs, bg) + has_super; + /* + * If group_block is not the normal value, we're trying to use + * the backup group descriptors and superblock --- so use the + * alternate location of the second block group in the + * metablock group. Ideally we should be testing each bg + * descriptor block individually for correctness, but we don't + * have the infrastructure in place to do that. + */ + if (group_block != fs->super->s_first_data_block && + ((ret_blk + fs->super->s_blocks_per_group) < + ext2fs_blocks_count(fs->super))) + ret_blk += fs->super->s_blocks_per_group; + return ret_blk; +} + +blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) +{ + return ext2fs_descriptor_block_loc2(fs, group_block, i); +} + +errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_channel * io, + ext2_filsys *ret_fs) +{ + return ext2fs_open2(name, 0, flags, superblock, block_size, + io, ret_fs); +} + +/* + * Note: if superblock is non-zero, block-size must also be non-zero. + * Superblock and block_size can be zero to use the default size. + * + * Valid flags for ext2fs_open() + * + * EXT2_FLAG_RW - Open the filesystem for read/write. + * EXT2_FLAG_FORCE - Open the filesystem even if some of the + * features aren't supported. + * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device + */ +errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_channel * io, + ext2_filsys *ret_fs) +{ + ext2_filsys fs; + io_manager manager = (*io)->manager; + errcode_t retval; + unsigned long i, first_meta_bg; + __u32 features; + int groups_per_block, blocks_per_group, io_flags; + blk64_t group_block, blk; + char *dest, *cp; +#ifdef WORDS_BIGENDIAN + struct ext2_group_desc *gdp; + int j; +#endif + + EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->io = *io; + fs->flags = flags; + /* don't overwrite sb backups unless flag is explicitly cleared */ + fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; + fs->umask = 022; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + strcpy(fs->device_name, name); + cp = strchr(fs->device_name, '?'); + if (!io_options && cp) { + *cp++ = 0; + io_options = cp; + } + + io_flags = 0; + if (flags & EXT2_FLAG_RW) + io_flags |= IO_FLAG_RW; + if (flags & EXT2_FLAG_EXCLUSIVE) + io_flags |= IO_FLAG_EXCLUSIVE; + if (flags & EXT2_FLAG_DIRECT_IO) + io_flags |= IO_FLAG_DIRECT_IO; + retval = manager->open(fs->device_name, io_flags, &fs->io); + if (retval) + goto cleanup; + if (io_options && + (retval = io_channel_set_options(fs->io, io_options))) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super); + if (retval) + goto cleanup; + if (flags & EXT2_FLAG_IMAGE_FILE) { + retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), + &fs->image_header); + if (retval) + goto cleanup; + retval = io_channel_read_blk(fs->io, 0, + -(int)sizeof(struct ext2_image_hdr), + fs->image_header); + if (retval) + goto cleanup; + if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) + return EXT2_ET_MAGIC_E2IMAGE; + superblock = 1; + block_size = fs->image_header->fs_blocksize; + } + + /* + * If the user specifies a specific block # for the + * superblock, then he/she must also specify the block size! + * Otherwise, read the master superblock located at offset + * SUPERBLOCK_OFFSET from the start of the partition. + * + * Note: we only save a backup copy of the superblock if we + * are reading the superblock from the primary superblock location. + */ + if (superblock) { + if (!block_size) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + io_channel_set_blksize(fs->io, block_size); + group_block = superblock; + fs->orig_super = 0; + } else { + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + superblock = 1; + group_block = 0; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto cleanup; + } + retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, + fs->super); + if (retval) + goto cleanup; + if (fs->orig_super) + memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); + +#ifdef WORDS_BIGENDIAN + fs->flags |= EXT2_FLAG_SWAP_BYTES; + ext2fs_swap_super(fs->super); +#else + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + retval = EXT2_ET_UNIMPLEMENTED; + goto cleanup; + } +#endif + + if (fs->super->s_magic != EXT2_SUPER_MAGIC) { + retval = EXT2_ET_BAD_MAGIC; + goto cleanup; + } + if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { + retval = EXT2_ET_REV_TOO_HIGH; + goto cleanup; + } + + /* + * Check for feature set incompatibility + */ + if (!(flags & EXT2_FLAG_FORCE)) { + features = fs->super->s_feature_incompat; +#ifdef EXT2_LIB_SOFTSUPP_INCOMPAT + if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) + features &= !EXT2_LIB_SOFTSUPP_INCOMPAT; +#endif + if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + + features = fs->super->s_feature_ro_compat; +#ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT + if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) + features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT; +#endif + if ((flags & EXT2_FLAG_RW) && + (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + + if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && + (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + } + + if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > + EXT2_MAX_BLOCK_LOG_SIZE) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->blocksize = EXT2_BLOCK_SIZE(fs->super); + if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->clustersize = EXT2_CLUSTER_SIZE(fs->super); + fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * + EXT2_INODE_SIZE(fs->super) + + EXT2_BLOCK_SIZE(fs->super) - 1) / + EXT2_BLOCK_SIZE(fs->super)); + if (block_size) { + if (block_size != fs->blocksize) { + retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; + goto cleanup; + } + } + /* + * Set the blocksize to the filesystem's blocksize. + */ + io_channel_set_blksize(fs->io, fs->blocksize); + + /* + * If this is an external journal device, don't try to read + * the group descriptors, because they're not there. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + *ret_fs = fs; + return 0; + } + + if (EXT2_INODES_PER_GROUP(fs->super) == 0) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + + /* + * Read group descriptors + */ + blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); + if (blocks_per_group == 0 || + blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || + fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || + EXT2_DESC_PER_BLOCK(fs->super) == 0 || + fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block, + blocks_per_group); + if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != + fs->super->s_inodes_count) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, + EXT2_DESC_PER_BLOCK(fs->super)); + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + if (!group_block) + group_block = fs->super->s_first_data_block; + dest = (char *) fs->group_desc; + groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + first_meta_bg = fs->super->s_first_meta_bg; + else + first_meta_bg = fs->desc_blocks; + if (first_meta_bg) { + retval = io_channel_read_blk(fs->io, group_block+1, + first_meta_bg, dest); + if (retval) + goto cleanup; +#ifdef WORDS_BIGENDIAN + gdp = (struct ext2_group_desc *) dest; + for (j=0; j < groups_per_block*first_meta_bg; j++) { + gdp = ext2fs_group_desc(fs, fs->group_desc, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#endif + dest += fs->blocksize*first_meta_bg; + } + for (i=first_meta_bg ; i < fs->desc_blocks; i++) { + blk = ext2fs_descriptor_block_loc2(fs, group_block, i); + retval = io_channel_read_blk64(fs->io, blk, 1, dest); + if (retval) + goto cleanup; +#ifdef WORDS_BIGENDIAN + for (j=0; j < groups_per_block; j++) { + /* The below happens to work... be careful. */ + gdp = ext2fs_group_desc(fs, fs->group_desc, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#endif + dest += fs->blocksize; + } + + fs->stride = fs->super->s_raid_stride; + + /* + * If recovery is from backup superblock, Clear _UNININT flags & + * reset bg_itable_unused to zero + */ + if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + dgrp_t group; + + for (group = 0; group < fs->group_desc_count; group++) { + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, group, 0); + } + ext2fs_mark_super_dirty(fs); + } + + fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; + *ret_fs = fs; + return 0; +cleanup: + if (flags & EXT2_FLAG_NOFREE_ON_ERROR) + *ret_fs = fs; + else + ext2fs_free(fs); + return retval; +} + +/* + * Set/get the filesystem data I/O channel. + * + * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. + */ +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + if (old_io) { + *old_io = (fs->image_io == fs->io) ? 0 : fs->io; + } + return 0; +} + +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = new_io ? new_io : fs->image_io; + return 0; +} + +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = fs->image_io = new_io; + fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | + EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; + fs->flags &= ~EXT2_FLAG_IMAGE_FILE; + return 0; +} diff --git a/lib/libext2fs/orig/partitions.h b/lib/libext2fs/orig/partitions.h new file mode 100644 index 0000000..9bb1049 --- /dev/null +++ b/lib/libext2fs/orig/partitions.h @@ -0,0 +1,49 @@ +#ifndef PARTITIONS_H_ +#define PARTITIONS_H_ + +#include +#include "bitops.h" + +#define MBR_SIGNATURE ext2fs_cpu_to_le16(0xAA55) +#define EBR_SIGNATURE ext2fs_cpu_to_le16(0xAA55) + +#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */ + +#define PARTITION_TYPE_EMPTY 0x00 /* Empty */ +#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */ +#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */ +#define PARTITION_TYPE_LINUX 0x83 /* EXT2/3/4 */ + +/** + * PRIMARY_PARTITION - Block device partition record + */ +typedef struct _PARTITION_RECORD { + u8 status; /* Partition status; see above */ + u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ + u8 type; /* Partition type; see above */ + u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ + u32 lba_start; /* Local block address to first sector of partition */ + u32 block_count; /* Number of blocks in partition */ +} __attribute__((__packed__)) PARTITION_RECORD; + +/** + * MASTER_BOOT_RECORD - Block device master boot record + */ +typedef struct _MASTER_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partitions[4]; /* 4 primary partitions */ + u16 signature; /* MBR signature; 0xAA55 */ +} __attribute__((__packed__)) MASTER_BOOT_RECORD; + +/** + * EXTENDED_PARTITION - Block device extended boot record + */ +typedef struct _EXTENDED_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partition; /* Primary partition */ + PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */ + u8 reserved[32]; /* Normally empty */ + u16 signature; /* EBR signature; 0xAA55 */ +} __attribute__((__packed__)) EXTENDED_BOOT_RECORD; + +#endif diff --git a/lib/libext2fs/orig/progress.c b/lib/libext2fs/orig/progress.c new file mode 100644 index 0000000..335b98b --- /dev/null +++ b/lib/libext2fs/orig/progress.c @@ -0,0 +1,86 @@ +/* + * progress.c - Numeric progress meter + * + * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + * 2003, 2004, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" +#include "ext2fsP.h" + +static char spaces[80], backspaces[80]; + +static int int_log10(unsigned int arg) +{ + int l; + + for (l=0; arg ; l++) + arg = arg / 10; + return l; +} + +void ext2fs_numeric_progress_init(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *label, __u64 max) +{ + /* + * The PRINT_PROGRESS flag turns on or off ALL + * progress-related messages, whereas the SKIP_PROGRESS + * environment variable prints the start and end messages but + * not the numeric countdown in the middle. + */ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + + memset(spaces, ' ', sizeof(spaces)-1); + spaces[sizeof(spaces)-1] = 0; + memset(backspaces, '\b', sizeof(backspaces)-1); + backspaces[sizeof(backspaces)-1] = 0; + progress->skip_progress = 0; + if (getenv("E2FSPROGS_SKIP_PROGRESS")) + progress->skip_progress++; + + memset(progress, 0, sizeof(*progress)); + + /* + * Figure out how many digits we need + */ + progress->max = max; + progress->log_max = int_log10(max); + + if (label) { + fputs(label, stdout); + fflush(stdout); + } +} + +void ext2fs_numeric_progress_update(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + __u64 val) +{ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + if (progress->skip_progress) + return; + + fprintf(stdout, "%*llu/%*llu", progress->log_max, val, + progress->log_max, progress->max); + fprintf(stdout, "%.*s", (2*progress->log_max)+1, backspaces); +} + +void ext2fs_numeric_progress_close(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *message) +{ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + fprintf(stdout, "%.*s", (2*progress->log_max)+1, spaces); + fprintf(stdout, "%.*s", (2*progress->log_max)+1, backspaces); + if (message) + fputs(message, stdout); +} diff --git a/lib/libext2fs/orig/punch.c b/lib/libext2fs/orig/punch.c new file mode 100644 index 0000000..8c6ec54 --- /dev/null +++ b/lib/libext2fs/orig/punch.c @@ -0,0 +1,324 @@ +/* + * punch.c --- deallocate blocks allocated to an inode + * + * Copyright (C) 2010 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#undef PUNCH_DEBUG + +/* + * This function returns 1 if the specified block is all zeros + */ +static int check_zero_block(char *buf, int blocksize) +{ + char *cp = buf; + int left = blocksize; + + while (left > 0) { + if (*cp++) + return 0; + left--; + } + return 1; +} + +/* + * This clever recursive function handles i_blocks[] as well as + * indirect, double indirect, and triple indirect blocks. It iterates + * over the entries in the i_blocks array or indirect blocks, and for + * each one, will recursively handle any indirect blocks and then + * frees and deallocates the blocks. + */ +static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode, + char *block_buf, blk_t *p, int level, + blk_t start, blk_t count, int max) +{ + errcode_t retval; + blk_t b, offset; + int i, incr; + int freed = 0; + +#ifdef PUNCH_DEBUG + printf("Entering ind_punch, level %d, start %u, count %u, " + "max %d\n", level, start, count, max); +#endif + incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level); + for (i=0, offset=0; i < max; i++, p++, offset += incr) { + if (offset > count) + break; + if (*p == 0 || (offset+incr) <= start) + continue; + b = *p; + if (level > 0) { + blk_t start2; +#ifdef PUNCH_DEBUG + printf("Reading indirect block %u\n", b); +#endif + retval = ext2fs_read_ind_block(fs, b, block_buf); + if (retval) + return retval; + start2 = (start > offset) ? start - offset : 0; + retval = ind_punch(fs, inode, block_buf + fs->blocksize, + (blk_t *) block_buf, level - 1, + start2, count - offset, + fs->blocksize >> 2); + if (retval) + return retval; + retval = ext2fs_write_ind_block(fs, b, block_buf); + if (retval) + return retval; + if (!check_zero_block(block_buf, fs->blocksize)) + continue; + } +#ifdef PUNCH_DEBUG + printf("Freeing block %u (offset %d)\n", b, offset); +#endif + ext2fs_block_alloc_stats(fs, b, -1); + *p = 0; + freed++; + } +#ifdef PUNCH_DEBUG + printf("Freed %d blocks\n", freed); +#endif + return ext2fs_iblk_sub_blocks(fs, inode, freed); +} + +static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode, + char *block_buf, blk_t start, blk_t count) +{ + errcode_t retval; + char *buf = 0; + int level; + int num = EXT2_NDIR_BLOCKS; + blk_t *bp = inode->i_block; + blk_t addr_per_block; + blk_t max = EXT2_NDIR_BLOCKS; + + if (!block_buf) { + retval = ext2fs_get_array(3, fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + addr_per_block = (blk_t) fs->blocksize >> 2; + + for (level=0; level < 4; level++, max *= addr_per_block) { +#ifdef PUNCH_DEBUG + printf("Main loop level %d, start %u count %u " + "max %d num %d\n", level, start, count, max, num); +#endif + if (start < max) { + retval = ind_punch(fs, inode, block_buf, bp, level, + start, count, num); + if (retval) + goto errout; + if (count > max) + count -= max - start; + else + break; + start = 0; + } else + start -= max; + bp += num; + if (level == 0) { + num = 1; + max = 1; + } + } + retval = 0; +errout: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +#ifdef PUNCH_DEBUG + +#define dbg_printf(f, a...) printf(f, ## a) + +static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) +{ + if (desc) + printf("%s: ", desc); + printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", + extent->e_lblk, extent->e_lblk + extent->e_len - 1, + extent->e_len, extent->e_pblk); + if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) + fputs("LEAF ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fputs("UNINIT ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + fputs("2ND_VISIT ", stdout); + if (!extent->e_flags) + fputs("(none)", stdout); + fputc('\n', stdout); + +} +#else +#define dbg_print_extent(desc, ex) do { } while (0) +#define dbg_printf(f, a...) do { } while (0) +#endif + +static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + blk64_t start, blk64_t end) +{ + ext2_extent_handle_t handle = 0; + struct ext2fs_extent extent; + errcode_t retval; + blk64_t free_start, next; + __u32 free_count, newlen; + int freed = 0; + + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + return retval; + ext2fs_extent_goto(handle, start); + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto errout; + while (1) { + dbg_print_extent("main loop", &extent); + next = extent.e_lblk + extent.e_len; + dbg_printf("start %llu, end %llu, next %llu\n", + (unsigned long long) start, + (unsigned long long) end, + (unsigned long long) next); + if (start <= extent.e_lblk) { + if (end < extent.e_lblk) + goto next_extent; + dbg_printf("Case #1\n"); + /* Start of deleted region before extent; + adjust beginning of extent */ + free_start = extent.e_pblk; + if (next > end) + free_count = end - extent.e_lblk + 1; + else + free_count = extent.e_len; + extent.e_len -= free_count; + extent.e_lblk += free_count; + extent.e_pblk += free_count; + } else if (end >= next-1) { + if (start >= next) + break; + /* End of deleted region after extent; + adjust end of extent */ + dbg_printf("Case #2\n"); + newlen = start - extent.e_lblk; + free_start = extent.e_pblk + newlen; + free_count = extent.e_len - newlen; + extent.e_len = newlen; + } else { + struct ext2fs_extent newex; + + dbg_printf("Case #3\n"); + /* The hard case; we need to split the extent */ + newex.e_pblk = extent.e_pblk + + (end + 1 - extent.e_lblk); + newex.e_lblk = end + 1; + newex.e_len = next - end - 1; + newex.e_flags = extent.e_flags; + + extent.e_len = start - extent.e_lblk; + free_start = extent.e_pblk + extent.e_len; + free_count = end - start + 1; + + dbg_print_extent("inserting", &newex); + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newex); + if (retval) + goto errout; + /* Now pointing at inserted extent; so go back */ + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, + &newex); + if (retval) + goto errout; + } + if (extent.e_len) { + dbg_print_extent("replacing", &extent); + retval = ext2fs_extent_replace(handle, 0, &extent); + } else { + dbg_printf("deleting current extent\n"); + retval = ext2fs_extent_delete(handle, 0); + } + if (retval) + goto errout; + dbg_printf("Free start %llu, free count = %u\n", + free_start, free_count); + while (free_count-- > 0) { + ext2fs_block_alloc_stats(fs, free_start++, -1); + freed++; + } + next_extent: + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + break; + if (retval) + goto errout; + } + dbg_printf("Freed %d blocks\n", freed); + retval = ext2fs_iblk_sub_blocks(fs, inode, freed); +errout: + ext2fs_extent_free(handle); + return retval; +} + +/* + * Deallocate all logical blocks starting at start to end, inclusive. + * If end is ~0, then this is effectively truncate. + */ +extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, blk64_t start, + blk64_t end) +{ + errcode_t retval; + struct ext2_inode inode_buf; + + if (start > end) + return EINVAL; + + if (start == end) + return 0; + + /* Read inode structure if necessary */ + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + if (inode->i_flags & EXT4_EXTENTS_FL) + retval = ext2fs_punch_extent(fs, ino, inode, start, end); + else { + blk_t count; + + if (start > ~0U) + return 0; + count = ((end - start) < ~0U) ? (end - start) : ~0U; + retval = ext2fs_punch_ind(fs, inode, block_buf, + (blk_t) start, count); + } + if (retval) + return retval; + + return ext2fs_write_inode(fs, ino, inode); +} diff --git a/lib/libext2fs/orig/read_bb.c b/lib/libext2fs/orig/read_bb.c new file mode 100644 index 0000000..e5d6322 --- /dev/null +++ b/lib/libext2fs/orig/read_bb.c @@ -0,0 +1,102 @@ +/* + * read_bb --- read the bad blocks inode + * + * Copyright (C) 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct read_bb_record { + ext2_badblocks_list bb_list; + errcode_t err; +}; + +/* + * Helper function for ext2fs_read_bb_inode() + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct read_bb_record *rb = (struct read_bb_record *) priv_data; + + if (blockcnt < 0) + return 0; + + if ((*block_nr < fs->super->s_first_data_block) || + (*block_nr >= ext2fs_blocks_count(fs->super))) + return 0; /* Ignore illegal blocks */ + + rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); + if (rb->err) + return BLOCK_ABORT; + return 0; +} + +/* + * Reads the current bad blocks from the bad blocks inode. + */ +errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) +{ + errcode_t retval; + struct read_bb_record rb; + struct ext2_inode inode; + blk_t numblocks; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + return retval; + numblocks = inode.i_blocks; + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + (inode.i_flags & EXT4_HUGE_FILE_FL))) + numblocks = numblocks / (fs->blocksize / 512); + numblocks += 20; + if (numblocks < 50) + numblocks = 50; + if (numblocks > 50000) + numblocks = 500; + retval = ext2fs_badblocks_list_create(bb_list, numblocks); + if (retval) + return retval; + } + + rb.bb_list = *bb_list; + rb.err = 0; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_READ_ONLY, + 0, mark_bad_block, &rb); + if (retval) + return retval; + + return rb.err; +} + + diff --git a/lib/libext2fs/orig/read_bb_file.c b/lib/libext2fs/orig/read_bb_file.c new file mode 100644 index 0000000..25454a8 --- /dev/null +++ b/lib/libext2fs/orig/read_bb_file.c @@ -0,0 +1,105 @@ +/* + * read_bb_file.c --- read a list of bad blocks from a FILE * + * + * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)) +{ + errcode_t retval; + blk_t blockno; + int count; + char buf[128]; + + if (fs) + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_badblocks_list_create(bb_list, 10); + if (retval) + return retval; + } + + while (!feof (f)) { + if (fgets(buf, sizeof(buf), f) == NULL) + break; + count = sscanf(buf, "%u", &blockno); + if (count <= 0) + continue; + if (fs && + ((blockno < fs->super->s_first_data_block) || + (blockno >= ext2fs_blocks_count(fs->super)))) { + if (invalid) + (invalid)(fs, blockno, buf, priv_data); + continue; + } + retval = ext2fs_badblocks_list_add(*bb_list, blockno); + if (retval) + return retval; + } + return 0; +} + +struct compat_struct { + void (*invalid)(ext2_filsys, blk_t); +}; + +static void call_compat_invalid(ext2_filsys fs, blk_t blk, + char *badstr EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct compat_struct *st; + + st = (struct compat_struct *) priv_data; + if (st->invalid) + (st->invalid)(fs, blk); +} + + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, blk_t blk)) +{ + struct compat_struct st; + + st.invalid = invalid; + + return ext2fs_read_bb_FILE2(fs, f, bb_list, &st, + call_compat_invalid); +} + + diff --git a/lib/libext2fs/orig/res_gdt.c b/lib/libext2fs/orig/res_gdt.c new file mode 100644 index 0000000..0d98842 --- /dev/null +++ b/lib/libext2fs/orig/res_gdt.c @@ -0,0 +1,220 @@ +/* + * res_gdt.c --- reserve blocks for growing the group descriptor table + * during online resizing. + * + * Copyright (C) 2002 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +static unsigned int list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} + +/* + * This code assumes that the reserved blocks have already been marked in-use + * during ext2fs_initialize(), so that they are not allocated for other + * uses before we can add them to the resize inode (which has to come + * after the creation of the inode table). + */ +errcode_t ext2fs_create_resize_inode(ext2_filsys fs) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf = 0, *gdt_buf = 0; + unsigned long long apb, inode_size; + /* FIXME-64 - can't deal with extents */ + blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; + int dindir_dirty = 0, inode_dirty = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + + retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf); + if (retval) + goto out_free; + gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); + + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) + goto out_free; + + /* Maximum possible file size (we donly use the dindirect blocks) */ + apb = EXT2_ADDR_PER_BLOCK(sb); + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { +#ifdef RES_GDT_DEBUG + printf("reading GDT dindir %u\n", dindir_blk); +#endif + retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); + if (retval) + goto out_inode; + } else { + blk_t goal = sb->s_first_data_block + fs->desc_blocks + + sb->s_reserved_gdt_blocks + 2 + + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + ext2fs_iblk_set(fs, &inode, 1); + memset(dindir_buf, 0, fs->blocksize); +#ifdef RES_GDT_DEBUG + printf("allocated GDT dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; + if(inode.i_size_high) { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + } + inode.i_ctime = fs->now ? fs->now : time(0); + } + + for (rsv_off = 0, gdt_off = fs->desc_blocks, + gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; + rsv_off < sb->s_reserved_gdt_blocks; + rsv_off++, gdt_off++, gdt_blk++) { + unsigned int three = 1, five = 5, seven = 7; + unsigned int grp, last = 0; + int gdt_dirty = 0; + + gdt_off %= apb; + if (!dindir_buf[gdt_off]) { + /* FIXME XXX XXX + blk_t new_blk; + + retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); + if (retval) + goto out_free; + if (new_blk != gdt_blk) { + // XXX free block + retval = -1; // XXX + } + */ + gdt_dirty = dindir_dirty = inode_dirty = 1; + memset(gdt_buf, 0, fs->blocksize); + dindir_buf[gdt_off] = gdt_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef RES_GDT_DEBUG + printf("added primary GDT block %u at %u[%u]\n", + gdt_blk, dindir_blk, gdt_off); +#endif + } else if (dindir_buf[gdt_off] == gdt_blk) { +#ifdef RES_GDT_DEBUG + printf("reading primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } else { +#ifdef RES_GDT_DEBUG + printf("bad primary GDT %u != %u at %u[%u]\n", + dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + + while ((grp = list_backups(fs, &three, &five, &seven)) < + fs->group_desc_count) { + blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; + + if (!gdt_buf[last]) { +#ifdef RES_GDT_DEBUG + printf("added backup GDT %u grp %u@%u[%u]\n", + expect, grp, gdt_blk, last); +#endif + gdt_buf[last] = expect; + ext2fs_iblk_add_blocks(fs, &inode, 1); + gdt_dirty = inode_dirty = 1; + } else if (gdt_buf[last] != expect) { +#ifdef RES_GDT_DEBUG + printf("bad backup GDT %u != %u at %u[%u]\n", + gdt_buf[last], expect, gdt_blk, last); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + last++; + } + if (gdt_dirty) { +#ifdef RES_GDT_DEBUG + printf("writing primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } + } + +out_dindir: + if (dindir_dirty) { + retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: +#ifdef RES_GDT_DEBUG + printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, + inode.i_size); +#endif + if (inode_dirty) { + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode); + if (!retval) + retval = retval2; + } +out_free: + ext2fs_free_mem(&dindir_buf); + return retval; +} + diff --git a/lib/libext2fs/orig/rw_bitmaps.c b/lib/libext2fs/orig/rw_bitmaps.c new file mode 100644 index 0000000..7b74b42 --- /dev/null +++ b/lib/libext2fs/orig/rw_bitmaps.c @@ -0,0 +1,351 @@ +/* + * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. + * + * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "e2image.h" + +static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) +{ + dgrp_t i; + unsigned int j; + int block_nbytes, inode_nbytes; + unsigned int nbits; + errcode_t retval; + char *block_buf = 0, *inode_buf = 0; + int csum_flag = 0; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + ext2_ino_t ino_itr = 1; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + csum_flag = 1; + + inode_nbytes = block_nbytes = 0; + if (do_block) { + block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, + &block_buf); + if (retval) + return retval; + memset(block_buf, 0xff, fs->blocksize); + } + if (do_inode) { + inode_nbytes = (size_t) + ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); + retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, + &inode_buf); + if (retval) + return retval; + memset(inode_buf, 0xff, fs->blocksize); + } + + for (i = 0; i < fs->group_desc_count; i++) { + if (!do_block) + goto skip_block_bitmap; + + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) + goto skip_this_block_bitmap; + + retval = ext2fs_get_block_bitmap_range2(fs->block_map, + blk_itr, block_nbytes << 3, block_buf); + if (retval) + return retval; + + if (i == fs->group_desc_count - 1) { + /* Force bitmap padding for the last group */ + nbits = ((ext2fs_blocks_count(fs->super) + - (__u64) fs->super->s_first_data_block) + % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super)); + if (nbits) + for (j = nbits; j < fs->blocksize * 8; j++) + ext2fs_set_bit(j, block_buf); + } + blk = ext2fs_block_bitmap_loc(fs, i); + if (blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + block_buf); + if (retval) + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + skip_this_block_bitmap: + blk_itr += block_nbytes << 3; + skip_block_bitmap: + + if (!do_inode) + continue; + + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) + ) + goto skip_this_inode_bitmap; + + retval = ext2fs_get_inode_bitmap_range2(fs->inode_map, + ino_itr, inode_nbytes << 3, inode_buf); + if (retval) + return retval; + + blk = ext2fs_inode_bitmap_loc(fs, i); + if (blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + inode_buf); + if (retval) + return EXT2_ET_INODE_BITMAP_WRITE; + } + skip_this_inode_bitmap: + ino_itr += inode_nbytes << 3; + + } + if (do_block) { + fs->flags &= ~EXT2_FLAG_BB_DIRTY; + ext2fs_free_mem(&block_buf); + } + if (do_inode) { + fs->flags &= ~EXT2_FLAG_IB_DIRTY; + ext2fs_free_mem(&inode_buf); + } + return 0; +} + +static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) +{ + dgrp_t i; + char *block_bitmap = 0, *inode_bitmap = 0; + char *buf; + errcode_t retval; + int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; + int csum_flag = 0; + int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE; + unsigned int cnt; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + blk64_t blk_cnt; + ext2_ino_t ino_itr = 1; + ext2_ino_t ino_cnt; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + csum_flag = 1; + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + return retval; + if (do_block) { + if (fs->block_map) + ext2fs_free_block_bitmap(fs->block_map); + strcpy(buf, "block bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + if (do_image) + retval = ext2fs_get_mem(fs->blocksize, &block_bitmap); + else + retval = ext2fs_get_memalign((unsigned) block_nbytes, + fs->blocksize, + &block_bitmap); + + if (retval) + goto cleanup; + } else + block_nbytes = 0; + if (do_inode) { + if (fs->inode_map) + ext2fs_free_inode_bitmap(fs->inode_map); + strcpy(buf, "inode bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + retval = ext2fs_get_mem(do_image ? fs->blocksize : + (unsigned) inode_nbytes, &inode_bitmap); + if (retval) + goto cleanup; + } else + inode_nbytes = 0; + ext2fs_free_mem(&buf); + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + blk = (fs->image_header->offset_inodemap / fs->blocksize); + ino_cnt = fs->super->s_inodes_count; + while (inode_nbytes > 0) { + retval = io_channel_read_blk64(fs->image_io, blk++, + 1, inode_bitmap); + if (retval) + goto cleanup; + cnt = fs->blocksize << 3; + if (cnt > ino_cnt) + cnt = ino_cnt; + retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, + ino_itr, cnt, inode_bitmap); + if (retval) + goto cleanup; + ino_itr += fs->blocksize << 3; + ino_cnt -= fs->blocksize << 3; + inode_nbytes -= fs->blocksize; + } + blk = (fs->image_header->offset_blockmap / + fs->blocksize); + blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) * + fs->group_desc_count; + while (block_nbytes > 0) { + retval = io_channel_read_blk64(fs->image_io, blk++, + 1, block_bitmap); + if (retval) + goto cleanup; + cnt = fs->blocksize << 3; + if (cnt > blk_cnt) + cnt = blk_cnt; + retval = ext2fs_set_block_bitmap_range2(fs->block_map, + blk_itr, cnt, block_bitmap); + if (retval) + goto cleanup; + blk_itr += fs->blocksize << 3; + blk_cnt -= fs->blocksize << 3; + block_nbytes -= fs->blocksize; + } + goto success_cleanup; + } + + for (i = 0; i < fs->group_desc_count; i++) { + if (block_bitmap) { + blk = ext2fs_block_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -block_nbytes, block_bitmap); + if (retval) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + } else + memset(block_bitmap, 0, block_nbytes); + cnt = block_nbytes << 3; + retval = ext2fs_set_block_bitmap_range2(fs->block_map, + blk_itr, cnt, block_bitmap); + if (retval) + goto cleanup; + blk_itr += block_nbytes << 3; + } + if (inode_bitmap) { + blk = ext2fs_inode_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -inode_nbytes, inode_bitmap); + if (retval) { + retval = EXT2_ET_INODE_BITMAP_READ; + goto cleanup; + } + } else + memset(inode_bitmap, 0, inode_nbytes); + cnt = inode_nbytes << 3; + retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, + ino_itr, cnt, inode_bitmap); + if (retval) + goto cleanup; + ino_itr += inode_nbytes << 3; + } + } +success_cleanup: + if (inode_bitmap) + ext2fs_free_mem(&inode_bitmap); + if (block_bitmap) + ext2fs_free_mem(&block_bitmap); + return 0; + +cleanup: + if (do_block) { + ext2fs_free_mem(&fs->block_map); + fs->block_map = 0; + } + if (do_inode) { + ext2fs_free_mem(&fs->inode_map); + fs->inode_map = 0; + } + if (inode_bitmap) + ext2fs_free_mem(&inode_bitmap); + if (block_bitmap) + ext2fs_free_mem(&block_bitmap); + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) +{ + return read_bitmaps(fs, 1, 0); +} + +errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) +{ + return read_bitmaps(fs, 0, 1); +} + +errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) +{ + return write_bitmaps(fs, 1, 0); +} + +errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) +{ + return write_bitmaps(fs, 0, 1); +} + +errcode_t ext2fs_read_bitmaps(ext2_filsys fs) +{ + if (fs->inode_map && fs->block_map) + return 0; + + return read_bitmaps(fs, !fs->inode_map, !fs->block_map); +} + +errcode_t ext2fs_write_bitmaps(ext2_filsys fs) +{ + int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); + int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); + + if (!do_inode && !do_block) + return 0; + + return write_bitmaps(fs, do_inode, do_block); +} diff --git a/lib/libext2fs/orig/sparse.c b/lib/libext2fs/orig/sparse.c new file mode 100644 index 0000000..15ded0a --- /dev/null +++ b/lib/libext2fs/orig/sparse.c @@ -0,0 +1,52 @@ +/* + * sparse.c --- find the groups in an ext2 filesystem with metadata backups + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * Copyright (C) 2002 Andreas Dilger. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} diff --git a/lib/libext2fs/orig/swapfs.c b/lib/libext2fs/orig/swapfs.c new file mode 100644 index 0000000..b856a09 --- /dev/null +++ b/lib/libext2fs/orig/swapfs.c @@ -0,0 +1,315 @@ +/* + * swapfs.c --- swap ext2 filesystem data structures + * + * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_ext_attr.h" + +#ifdef WORDS_BIGENDIAN +void ext2fs_swap_super(struct ext2_super_block * sb) +{ + int i; + sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); + sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); + sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); + sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); + sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); + sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); + sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); + sb->s_log_cluster_size = ext2fs_swab32(sb->s_log_cluster_size); + sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); + sb->s_clusters_per_group = ext2fs_swab32(sb->s_clusters_per_group); + sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); + sb->s_mtime = ext2fs_swab32(sb->s_mtime); + sb->s_wtime = ext2fs_swab32(sb->s_wtime); + sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); + sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); + sb->s_magic = ext2fs_swab16(sb->s_magic); + sb->s_state = ext2fs_swab16(sb->s_state); + sb->s_errors = ext2fs_swab16(sb->s_errors); + sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); + sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); + sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); + sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); + sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); + sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); + sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); + sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); + sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); + sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); + sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); + sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); + sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); + sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); + sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); + sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); + sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); + sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); + sb->s_desc_size = ext2fs_swab16(sb->s_desc_size); + sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); + sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); + sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); + sb->s_blocks_count_hi = ext2fs_swab32(sb->s_blocks_count_hi); + sb->s_r_blocks_count_hi = ext2fs_swab32(sb->s_r_blocks_count_hi); + sb->s_free_blocks_hi = ext2fs_swab32(sb->s_free_blocks_hi); + sb->s_min_extra_isize = ext2fs_swab16(sb->s_min_extra_isize); + sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize); + sb->s_flags = ext2fs_swab32(sb->s_flags); + sb->s_kbytes_written = ext2fs_swab64(sb->s_kbytes_written); + sb->s_snapshot_inum = ext2fs_swab32(sb->s_snapshot_inum); + sb->s_snapshot_id = ext2fs_swab32(sb->s_snapshot_id); + sb->s_snapshot_r_blocks_count = + ext2fs_swab64(sb->s_snapshot_r_blocks_count); + sb->s_snapshot_list = ext2fs_swab32(sb->s_snapshot_list); + sb->s_usr_quota_inum = ext2fs_swab32(sb->s_usr_quota_inum); + sb->s_grp_quota_inum = ext2fs_swab32(sb->s_grp_quota_inum); + + for (i=0; i < 4; i++) + sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); + + /* if journal backup is for a valid extent-based journal... */ + if (!ext2fs_extent_header_verify(sb->s_jnl_blocks, + sizeof(sb->s_jnl_blocks))) { + /* ... swap only the journal i_size */ + sb->s_jnl_blocks[16] = ext2fs_swab32(sb->s_jnl_blocks[16]); + /* and the extent data is not swapped on read */ + return; + } + + /* direct/indirect journal: swap it all */ + for (i=0; i < 17; i++) + sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); +} + +void ext2fs_swap_group_desc2(ext2_filsys fs, struct ext2_group_desc *gdp) +{ + /* Do the 32-bit parts first */ + gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); + gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); + gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); + gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); + gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); + gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); + gdp->bg_flags = ext2fs_swab16(gdp->bg_flags); + gdp->bg_itable_unused = ext2fs_swab16(gdp->bg_itable_unused); + gdp->bg_checksum = ext2fs_swab16(gdp->bg_checksum); + /* If we're 32-bit, we're done */ + if (fs && (!fs->super->s_desc_size || + (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT))) + return; + + /* Swap the 64-bit parts */ + struct ext4_group_desc *gdp4 = (struct ext4_group_desc *) gdp; + gdp4->bg_block_bitmap_hi = ext2fs_swab32(gdp4->bg_block_bitmap_hi); + gdp4->bg_inode_bitmap_hi = ext2fs_swab32(gdp4->bg_inode_bitmap_hi); + gdp4->bg_inode_table_hi = ext2fs_swab32(gdp4->bg_inode_table_hi); + gdp4->bg_free_blocks_count_hi = + ext2fs_swab16(gdp4->bg_free_blocks_count_hi); + gdp4->bg_free_inodes_count_hi = + ext2fs_swab16(gdp4->bg_free_inodes_count_hi); + gdp4->bg_used_dirs_count_hi = + ext2fs_swab16(gdp4->bg_used_dirs_count_hi); + gdp4->bg_itable_unused_hi = ext2fs_swab16(gdp4->bg_itable_unused_hi); +} + +void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) +{ + return ext2fs_swap_group_desc2(0, gdp); +} + + +void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, + struct ext2_ext_attr_header *from_header) +{ + int n; + + to_header->h_magic = ext2fs_swab32(from_header->h_magic); + to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); + to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); + to_header->h_hash = ext2fs_swab32(from_header->h_hash); + for (n = 0; n < 4; n++) + to_header->h_reserved[n] = + ext2fs_swab32(from_header->h_reserved[n]); +} + +void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, + struct ext2_ext_attr_entry *from_entry) +{ + to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs); + to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block); + to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size); + to_entry->e_hash = ext2fs_swab32(from_entry->e_hash); +} + +void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) +{ + struct ext2_ext_attr_header *from_header = + (struct ext2_ext_attr_header *)from; + struct ext2_ext_attr_header *to_header = + (struct ext2_ext_attr_header *)to; + struct ext2_ext_attr_entry *from_entry, *to_entry; + char *from_end = (char *)from_header + bufsize; + + if (to_header != from_header) + memcpy(to_header, from_header, bufsize); + + if (has_header) { + ext2fs_swap_ext_attr_header(to_header, from_header); + + from_entry = (struct ext2_ext_attr_entry *)(from_header+1); + to_entry = (struct ext2_ext_attr_entry *)(to_header+1); + } else { + from_entry = (struct ext2_ext_attr_entry *)from_header; + to_entry = (struct ext2_ext_attr_entry *)to_header; + } + + while ((char *)from_entry < from_end && *(__u32 *)from_entry) { + ext2fs_swap_ext_attr_entry(to_entry, from_entry); + from_entry = EXT2_EXT_ATTR_NEXT(from_entry); + to_entry = EXT2_EXT_ATTR_NEXT(to_entry); + } +} + +void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize) +{ + unsigned i, has_data_blocks = 0, extra_isize = 0, attr_magic = 0; + int has_extents = 0; + int islnk = 0; + __u32 *eaf, *eat; + + if (hostorder && LINUX_S_ISLNK(f->i_mode)) + islnk = 1; + t->i_mode = ext2fs_swab16(f->i_mode); + if (!hostorder && LINUX_S_ISLNK(t->i_mode)) + islnk = 1; + t->i_uid = ext2fs_swab16(f->i_uid); + t->i_size = ext2fs_swab32(f->i_size); + t->i_atime = ext2fs_swab32(f->i_atime); + t->i_ctime = ext2fs_swab32(f->i_ctime); + t->i_mtime = ext2fs_swab32(f->i_mtime); + t->i_dtime = ext2fs_swab32(f->i_dtime); + t->i_gid = ext2fs_swab16(f->i_gid); + t->i_links_count = ext2fs_swab16(f->i_links_count); + t->i_file_acl = ext2fs_swab32(f->i_file_acl); + if (hostorder) + has_data_blocks = ext2fs_inode_data_blocks(fs, + (struct ext2_inode *) f); + t->i_blocks = ext2fs_swab32(f->i_blocks); + if (!hostorder) + has_data_blocks = ext2fs_inode_data_blocks(fs, + (struct ext2_inode *) t); + if (hostorder && (f->i_flags & EXT4_EXTENTS_FL)) + has_extents = 1; + t->i_flags = ext2fs_swab32(f->i_flags); + if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL)) + has_extents = 1; + t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); + /* extent data are swapped on access, not here */ + if (!has_extents && (!islnk || has_data_blocks)) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = ext2fs_swab32(f->i_block[i]); + } else if (t != f) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = f->i_block[i]; + } + t->i_generation = ext2fs_swab32(f->i_generation); + t->i_faddr = ext2fs_swab32(f->i_faddr); + + switch (fs->super->s_creator_os) { + case EXT2_OS_LINUX: + t->osd1.linux1.l_i_version = + ext2fs_swab32(f->osd1.linux1.l_i_version); + t->osd2.linux2.l_i_blocks_hi = + ext2fs_swab16(f->osd2.linux2.l_i_blocks_hi); + t->osd2.linux2.l_i_file_acl_high = + ext2fs_swab16(f->osd2.linux2.l_i_file_acl_high); + t->osd2.linux2.l_i_uid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); + t->osd2.linux2.l_i_gid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); + t->osd2.linux2.l_i_reserved2 = + ext2fs_swab32(f->osd2.linux2.l_i_reserved2); + break; + case EXT2_OS_HURD: + t->osd1.hurd1.h_i_translator = + ext2fs_swab32 (f->osd1.hurd1.h_i_translator); + t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; + t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; + t->osd2.hurd2.h_i_mode_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); + t->osd2.hurd2.h_i_uid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); + t->osd2.hurd2.h_i_gid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); + t->osd2.hurd2.h_i_author = + ext2fs_swab32 (f->osd2.hurd2.h_i_author); + break; + default: + break; + } + + if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) + return; /* no i_extra_isize field */ + + if (hostorder) + extra_isize = f->i_extra_isize; + t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); + if (!hostorder) + extra_isize = t->i_extra_isize; + if (extra_isize > EXT2_INODE_SIZE(fs->super) - + sizeof(struct ext2_inode)) { + /* this is error case: i_extra_size is too large */ + return; + } + + i = sizeof(struct ext2_inode) + extra_isize + sizeof(__u32); + if (bufsize < (int) i) + return; /* no space for EA magic */ + + eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + + extra_isize); + + attr_magic = *eaf; + if (!hostorder) + attr_magic = ext2fs_swab32(attr_magic); + + if (attr_magic != EXT2_EXT_ATTR_MAGIC) + return; /* it seems no magic here */ + + eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + + extra_isize); + *eat = ext2fs_swab32(*eaf); + + /* convert EA(s) */ + ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), + bufsize - sizeof(struct ext2_inode) - + extra_isize - sizeof(__u32), 0); + +} + +void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, + struct ext2_inode *f, int hostorder) +{ + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, + (struct ext2_inode_large *) f, hostorder, + sizeof(struct ext2_inode)); +} + +#endif diff --git a/lib/libext2fs/orig/tdb.c b/lib/libext2fs/orig/tdb.c new file mode 100644 index 0000000..0550f46 --- /dev/null +++ b/lib/libext2fs/orig/tdb.c @@ -0,0 +1,4145 @@ +/* +URL: svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb/common +Rev: 23590 +Last Changed Date: 2007-06-22 13:36:10 -0400 (Fri, 22 Jun 2007) +*/ + /* + trivial database library - standalone version + + Copyright (C) Andrew Tridgell 1999-2005 + Copyright (C) Jeremy Allison 2000-2006 + Copyright (C) Paul `Rusty' Russell 2000 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef CONFIG_STAND_ALONE +#define HAVE_MMAP +#define HAVE_STRDUP +#define HAVE_SYS_MMAN_H +#define HAVE_UTIME_H +#define HAVE_UTIME +#endif +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +#ifndef HAVE_STRDUP +#define strdup rep_strdup +static char *rep_strdup(const char *s) +{ + char *ret; + int length; + if (!s) + return NULL; + + if (!length) + length = strlen(s); + + ret = malloc(length + 1); + if (ret) { + strncpy(ret, s, length); + ret[length] = '\0'; + } + return ret; +} +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 ) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +typedef int bool; + +#include "tdb.h" + +#ifndef u32 +#define u32 unsigned +#endif + +#ifndef HAVE_GETPAGESIZE +#define getpagesize() 0x2000 +#endif + +typedef u32 tdb_len_t; +typedef u32 tdb_off_t; + +#ifndef offsetof +#define offsetof(t,f) ((unsigned int)&((t *)0)->f) +#endif + +#define TDB_MAGIC_FOOD "TDB file\n" +#define TDB_VERSION (0x26011967 + 6) +#define TDB_MAGIC (0x26011999U) +#define TDB_FREE_MAGIC (~TDB_MAGIC) +#define TDB_DEAD_MAGIC (0xFEE1DEAD) +#define TDB_RECOVERY_MAGIC (0xf53bc0e7U) +#define TDB_ALIGNMENT 4 +#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) +#define DEFAULT_HASH_SIZE 131 +#define FREELIST_TOP (sizeof(struct tdb_header)) +#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) +#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) +#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) +#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) +#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t)) +#define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t)) +#define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1) +#define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start) +#define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number) +#define TDB_PAD_BYTE 0x42 +#define TDB_PAD_U32 0x42424242 + +/* NB assumes there is a local variable called "tdb" that is the + * current context, also takes doubly-parenthesized print-style + * argument. */ +#define TDB_LOG(x) tdb->log.log_fn x + +/* lock offsets */ +#define GLOBAL_LOCK 0 +#define ACTIVE_LOCK 4 +#define TRANSACTION_LOCK 8 + +/* free memory if the pointer is valid and zero the pointer */ +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) +#endif + +#define BUCKET(hash) ((hash) % tdb->header.hash_size) + +#define DOCONV() (tdb->flags & TDB_CONVERT) +#define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x) + + +/* the body of the database is made of one list_struct for the free space + plus a separate data list for each hash value */ +struct list_struct { + tdb_off_t next; /* offset of the next record in the list */ + tdb_len_t rec_len; /* total byte length of record */ + tdb_len_t key_len; /* byte length of key */ + tdb_len_t data_len; /* byte length of data */ + u32 full_hash; /* the full 32 bit hash of the key */ + u32 magic; /* try to catch errors */ + /* the following union is implied: + union { + char record[rec_len]; + struct { + char key[key_len]; + char data[data_len]; + } + u32 totalsize; (tailer) + } + */ +}; + + +/* this is stored at the front of every database */ +struct tdb_header { + char magic_food[32]; /* for /etc/magic */ + u32 version; /* version of the code */ + u32 hash_size; /* number of hash entries */ + tdb_off_t rwlocks; /* obsolete - kept to detect old formats */ + tdb_off_t recovery_start; /* offset of transaction recovery region */ + tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */ + tdb_off_t reserved[29]; +}; + +struct tdb_lock_type { + int list; + u32 count; + u32 ltype; +}; + +struct tdb_traverse_lock { + struct tdb_traverse_lock *next; + u32 off; + u32 hash; + int lock_rw; +}; + + +struct tdb_methods { + int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int ); + int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t); + void (*next_hash_chain)(struct tdb_context *, u32 *); + int (*tdb_oob)(struct tdb_context *, tdb_off_t , int ); + int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t ); + int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int, size_t); +}; + +struct tdb_context { + char *name; /* the name of the database */ + void *map_ptr; /* where it is currently mapped */ + int fd; /* open file descriptor for the database */ + tdb_len_t map_size; /* how much space has been mapped */ + int read_only; /* opened read-only */ + int traverse_read; /* read-only traversal */ + struct tdb_lock_type global_lock; + int num_lockrecs; + struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */ + enum TDB_ERROR ecode; /* error code for last tdb error */ + struct tdb_header header; /* a cached copy of the header */ + u32 flags; /* the flags passed to tdb_open */ + struct tdb_traverse_lock travlocks; /* current traversal locks */ + struct tdb_context *next; /* all tdbs to avoid multiple opens */ + dev_t device; /* uniquely identifies this tdb */ + ino_t inode; /* uniquely identifies this tdb */ + struct tdb_logging_context log; + unsigned int (*hash_fn)(TDB_DATA *key); + int open_flags; /* flags used in the open - needed by reopen */ + unsigned int num_locks; /* number of chain locks held */ + const struct tdb_methods *methods; + struct tdb_transaction *transaction; + int page_size; + int max_dead_records; + bool have_transaction_lock; +}; + + +/* + internal prototypes +*/ +static int tdb_munmap(struct tdb_context *tdb); +static void tdb_mmap(struct tdb_context *tdb); +static int tdb_lock(struct tdb_context *tdb, int list, int ltype); +static int tdb_unlock(struct tdb_context *tdb, int list, int ltype); +static int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); +static int tdb_transaction_lock(struct tdb_context *tdb, int ltype); +static int tdb_transaction_unlock(struct tdb_context *tdb); +static int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); +static int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static void *tdb_convert(void *buf, u32 size); +static int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); +static unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); +static int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, + tdb_off_t offset, tdb_len_t len, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data); +static tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, + struct list_struct *rec); +static void tdb_io_init(struct tdb_context *tdb); +static int tdb_expand(struct tdb_context *tdb, tdb_off_t size); +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, + struct list_struct *rec); + + +/* file: error.c */ + +enum TDB_ERROR tdb_error(struct tdb_context *tdb) +{ + return tdb->ecode; +} + +static struct tdb_errname { + enum TDB_ERROR ecode; const char *estring; +} emap[] = { {TDB_SUCCESS, "Success"}, + {TDB_ERR_CORRUPT, "Corrupt database"}, + {TDB_ERR_IO, "IO Error"}, + {TDB_ERR_LOCK, "Locking error"}, + {TDB_ERR_OOM, "Out of memory"}, + {TDB_ERR_EXISTS, "Record exists"}, + {TDB_ERR_NOLOCK, "Lock exists on other keys"}, + {TDB_ERR_EINVAL, "Invalid parameter"}, + {TDB_ERR_NOEXIST, "Record does not exist"}, + {TDB_ERR_RDONLY, "write not permitted"} }; + +/* Error string for the last tdb error */ +const char *tdb_errorstr(struct tdb_context *tdb) +{ + u32 i; + for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) + if (tdb->ecode == emap[i].ecode) + return emap[i].estring; + return "Invalid error code"; +} + +/* file: lock.c */ + +#define TDB_MARK_LOCK 0x80000000 + +/* a byte range locking function - return 0 on success + this functions locks/unlocks 1 byte at the specified offset. + + On error, errno is also set so that errors are passed back properly + through tdb_open(). + + note that a len of zero means lock to end of file +*/ +int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, + int rw_type, int lck_type, int probe, size_t len) +{ + struct flock fl; + int ret; + + if (tdb->flags & TDB_NOLOCK) { + return 0; + } + + if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + fl.l_type = rw_type; + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = len; + fl.l_pid = 0; + + do { + ret = fcntl(tdb->fd,lck_type,&fl); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* Generic lock error. errno set by fcntl. + * EAGAIN is an expected return from non-blocking + * locks. */ + if (!probe && lck_type != F_SETLK) { + /* Ensure error code is set for log fun to examine. */ + tdb->ecode = TDB_ERR_LOCK; + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", + tdb->fd, offset, rw_type, lck_type, (int)len)); + } + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + return 0; +} + + +/* + upgrade a read lock to a write lock. This needs to be handled in a + special way as some OSes (such as solaris) have too conservative + deadlock detection and claim a deadlock when progress can be + made. For those OSes we may loop for a while. +*/ +int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len) +{ + int count = 1000; + while (count--) { + struct timeval tv; + if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { + return 0; + } + if (errno != EDEADLK) { + break; + } + /* sleep for as short a time as we can - more portable than usleep() */ + tv.tv_sec = 0; + tv.tv_usec = 1; +#ifdef HAVE_SYS_SELECT_H + select(0, NULL, NULL, NULL, &tv); +#endif + } + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset)); + return -1; +} + + +/* lock a list in the database. list -1 is the alloc list */ +static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op) +{ + struct tdb_lock_type *new_lck; + int i; + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", + list, ltype)); + return -1; + } + if (tdb->flags & TDB_NOLOCK) + return 0; + + for (i=0; inum_lockrecs; i++) { + if (tdb->lockrecs[i].list == list) { + if (tdb->lockrecs[i].count == 0) { + /* + * Can't happen, see tdb_unlock(). It should + * be an assert. + */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: " + "lck->count == 0 for list %d", list)); + } + /* + * Just increment the in-memory struct, posix locks + * don't stack. + */ + tdb->lockrecs[i].count++; + return 0; + } + } + + new_lck = (struct tdb_lock_type *)realloc( + tdb->lockrecs, + sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1)); + if (new_lck == NULL) { + errno = ENOMEM; + return -1; + } + tdb->lockrecs = new_lck; + + /* Since fcntl locks don't nest, we do a lock for the first one, + and simply bump the count for future ones */ + if (!mark_lock && + tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op, + 0, 1)) { + return -1; + } + + tdb->num_locks++; + + tdb->lockrecs[tdb->num_lockrecs].list = list; + tdb->lockrecs[tdb->num_lockrecs].count = 1; + tdb->lockrecs[tdb->num_lockrecs].ltype = ltype; + tdb->num_lockrecs += 1; + + return 0; +} + +/* lock a list in the database. list -1 is the alloc list */ +int tdb_lock(struct tdb_context *tdb, int list, int ltype) +{ + int ret; + ret = _tdb_lock(tdb, list, ltype, F_SETLKW); + if (ret) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d " + "ltype=%d (%s)\n", list, ltype, strerror(errno))); + } + return ret; +} + +/* lock a list in the database. list -1 is the alloc list. non-blocking lock */ +int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype) +{ + return _tdb_lock(tdb, list, ltype, F_SETLK); +} + + +/* unlock the database: returns void because it's too late for errors. */ + /* changed to return int it may be interesting to know there + has been an error --simo */ +int tdb_unlock(struct tdb_context *tdb, int list, int ltype) +{ + int ret = -1; + int i; + struct tdb_lock_type *lck = NULL; + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->flags & TDB_NOLOCK) + return 0; + + /* Sanity checks */ + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); + return ret; + } + + for (i=0; inum_lockrecs; i++) { + if (tdb->lockrecs[i].list == list) { + lck = &tdb->lockrecs[i]; + break; + } + } + + if ((lck == NULL) || (lck->count == 0)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); + return -1; + } + + if (lck->count > 1) { + lck->count--; + return 0; + } + + /* + * This lock has count==1 left, so we need to unlock it in the + * kernel. We don't bother with decrementing the in-memory array + * element, we're about to overwrite it with the last array element + * anyway. + */ + + if (mark_lock) { + ret = 0; + } else { + ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, + F_SETLKW, 0, 1); + } + tdb->num_locks--; + + /* + * Shrink the array by overwriting the element just unlocked with the + * last array element. + */ + + if (tdb->num_lockrecs > 1) { + *lck = tdb->lockrecs[tdb->num_lockrecs-1]; + } + tdb->num_lockrecs -= 1; + + /* + * We don't bother with realloc when the array shrinks, but if we have + * a completely idle tdb we should get rid of the locked array. + */ + + if (tdb->num_lockrecs == 0) { + SAFE_FREE(tdb->lockrecs); + } + + if (ret) + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); + return ret; +} + +/* + get the transaction lock + */ +int tdb_transaction_lock(struct tdb_context *tdb, int ltype) +{ + if (tdb->have_transaction_lock || tdb->global_lock.count) { + return 0; + } + if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, + F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + tdb->have_transaction_lock = 1; + return 0; +} + +/* + release the transaction lock + */ +int tdb_transaction_unlock(struct tdb_context *tdb) +{ + int ret; + if (!tdb->have_transaction_lock) { + return 0; + } + ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); + if (ret == 0) { + tdb->have_transaction_lock = 0; + } + return ret; +} + + + + +/* lock/unlock entire database */ +static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) +{ + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* There are no locks on read-only dbs */ + if (tdb->read_only || tdb->traverse_read) + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + + if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) { + tdb->global_lock.count++; + return 0; + } + + if (tdb->global_lock.count) { + /* a global lock of a different type exists */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->num_locks != 0) { + /* can't combine global and chain locks */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (!mark_lock && + tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op, + 0, 4*tdb->header.hash_size)) { + if (op == F_SETLKW) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno))); + } + return -1; + } + + tdb->global_lock.count = 1; + tdb->global_lock.ltype = ltype; + + return 0; +} + + + +/* unlock entire db */ +static int _tdb_unlockall(struct tdb_context *tdb, int ltype) +{ + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* There are no locks on read-only dbs */ + if (tdb->read_only || tdb->traverse_read) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->global_lock.count > 1) { + tdb->global_lock.count--; + return 0; + } + + if (!mark_lock && + tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, + 0, 4*tdb->header.hash_size)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno))); + return -1; + } + + tdb->global_lock.count = 0; + tdb->global_lock.ltype = 0; + + return 0; +} + +/* lock entire database with write lock */ +int tdb_lockall(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK, F_SETLKW); +} + +/* lock entire database with write lock - mark only */ +int tdb_lockall_mark(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW); +} + +/* unlock entire database with write lock - unmark only */ +int tdb_lockall_unmark(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK); +} + +/* lock entire database with write lock - nonblocking varient */ +int tdb_lockall_nonblock(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK, F_SETLK); +} + +/* unlock entire database with write lock */ +int tdb_unlockall(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_WRLCK); +} + +/* lock entire database with read lock */ +int tdb_lockall_read(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_RDLCK, F_SETLKW); +} + +/* lock entire database with read lock - nonblock varient */ +int tdb_lockall_read_nonblock(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_RDLCK, F_SETLK); +} + +/* unlock entire database with read lock */ +int tdb_unlockall_read(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_RDLCK); +} + +/* lock/unlock one hash chain. This is meant to be used to reduce + contention - it cannot guarantee how many records will be locked */ +int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +/* lock/unlock one hash chain, non-blocking. This is meant to be used + to reduce contention - it cannot guarantee how many records will be + locked */ +int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +/* mark a chain as locked without actually locking it. Warning! use with great caution! */ +int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); +} + +/* unmark a chain as locked without actually locking it. Warning! use with great caution! */ +int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); +} + +int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); +} + +int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); +} + + + +/* record lock stops delete underneath */ +int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) +{ + return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0; +} + +/* + Write locks override our own fcntl readlocks, so check it here. + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ +int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off) +{ + struct tdb_traverse_lock *i; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + return -1; + return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1); +} + +/* + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ +int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off) +{ + return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1); +} + +/* fcntl locks don't stack: avoid unlocking someone else's */ +int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off) +{ + struct tdb_traverse_lock *i; + u32 count = 0; + + if (off == 0) + return 0; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + count++; + return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0); +} + +/* file: io.c */ + +/* check for an out of bounds access - if it is out of bounds then + see if the database has been expanded by someone else and expand + if necessary + note that "len" is the minimum length needed for the db +*/ +static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) +{ + struct stat st; + if (len <= tdb->map_size) + return 0; + if (tdb->flags & TDB_INTERNAL) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n", + (int)len, (int)tdb->map_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + if (fstat(tdb->fd, &st) == -1) { + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + if (st.st_size < (size_t)len) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n", + (int)len, (int)st.st_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + /* Unmap, update size, remap */ + if (tdb_munmap(tdb) == -1) + return TDB_ERRCODE(TDB_ERR_IO, -1); + tdb->map_size = st.st_size; + tdb_mmap(tdb); + return 0; +} + +/* write a lump of data at a specified offset */ +static int tdb_write(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) +{ + if (len == 0) { + return 0; + } + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) + return -1; + + if (tdb->map_ptr) { + memcpy(off + (char *)tdb->map_ptr, buf, len); + } else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n", + off, len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + return 0; +} + +/* Endian conversion: we only ever deal with 4 byte quantities */ +void *tdb_convert(void *buf, u32 size) +{ + u32 i, *p = (u32 *)buf; + for (i = 0; i < size / 4; i++) + p[i] = TDB_BYTEREV(p[i]); + return buf; +} + + +/* read a lump of data at a specified offset, maybe convert */ +static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, + tdb_len_t len, int cv) +{ + if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) { + return -1; + } + + if (tdb->map_ptr) { + memcpy(buf, off + (char *)tdb->map_ptr, len); + } else { + ssize_t ret = pread(tdb->fd, buf, len, off); + if (ret != (ssize_t)len) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d " + "len=%d ret=%d (%s) map_size=%d\n", + (int)off, (int)len, (int)ret, strerror(errno), + (int)tdb->map_size)); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + } + if (cv) { + tdb_convert(buf, len); + } + return 0; +} + + + +/* + do an unlocked scan of the hash table heads to find the next non-zero head. The value + will then be confirmed with the lock held +*/ +static void tdb_next_hash_chain(struct tdb_context *tdb, u32 *chain) +{ + u32 h = *chain; + if (tdb->map_ptr) { + for (;h < tdb->header.hash_size;h++) { + if (0 != *(u32 *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) { + break; + } + } + } else { + u32 off=0; + for (;h < tdb->header.hash_size;h++) { + if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) { + break; + } + } + } + (*chain) = h; +} + + +int tdb_munmap(struct tdb_context *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return 0; + +#ifdef HAVE_MMAP + if (tdb->map_ptr) { + int ret = munmap(tdb->map_ptr, tdb->map_size); + if (ret != 0) + return ret; + } +#endif + tdb->map_ptr = NULL; + return 0; +} + +void tdb_mmap(struct tdb_context *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return; + +#ifdef HAVE_MMAP + if (!(tdb->flags & TDB_NOMMAP)) { + tdb->map_ptr = mmap(NULL, tdb->map_size, + PROT_READ|(tdb->read_only? 0:PROT_WRITE), + MAP_SHARED|MAP_FILE, tdb->fd, 0); + + /* + * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! + */ + + if (tdb->map_ptr == MAP_FAILED) { + tdb->map_ptr = NULL; + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", + tdb->map_size, strerror(errno))); + } + } else { + tdb->map_ptr = NULL; + } +#else + tdb->map_ptr = NULL; +#endif +} + +/* expand a file. we prefer to use ftruncate, as that is what posix + says to use for mmap expansion */ +static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition) +{ + char buf[1024]; + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + if (ftruncate(tdb->fd, size+addition) == -1) { + char b = 0; + if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } + } + + /* now fill the file with something. This ensures that the + file isn't sparse, which would be very bad if we ran out of + disk. This must be done with write, not via mmap */ + memset(buf, TDB_PAD_BYTE, sizeof(buf)); + while (addition) { + int n = addition>sizeof(buf)?sizeof(buf):addition; + int ret = pwrite(tdb->fd, buf, n, size); + if (ret != n) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n", + n, strerror(errno))); + return -1; + } + addition -= n; + size += n; + } + return 0; +} + + +/* expand the database at least size bytes by expanding the underlying + file and doing the mmap again if necessary */ +int tdb_expand(struct tdb_context *tdb, tdb_off_t size) +{ + struct list_struct rec; + tdb_off_t offset; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n")); + return -1; + } + + /* must know about any previous expansions by another process */ + tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); + + /* always make room for at least 10 more records, and round + the database up to a multiple of the page size */ + size = TDB_ALIGN(tdb->map_size + size*10, tdb->page_size) - tdb->map_size; + + if (!(tdb->flags & TDB_INTERNAL)) + tdb_munmap(tdb); + + /* + * We must ensure the file is unmapped before doing this + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* expand the file itself */ + if (!(tdb->flags & TDB_INTERNAL)) { + if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0) + goto fail; + } + + tdb->map_size += size; + + if (tdb->flags & TDB_INTERNAL) { + char *new_map_ptr = (char *)realloc(tdb->map_ptr, + tdb->map_size); + if (!new_map_ptr) { + tdb->map_size -= size; + goto fail; + } + tdb->map_ptr = new_map_ptr; + } else { + /* + * We must ensure the file is remapped before adding the space + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* We're ok if the mmap fails as we'll fallback to read/write */ + tdb_mmap(tdb); + } + + /* form a new freelist record */ + memset(&rec,'\0',sizeof(rec)); + rec.rec_len = size - sizeof(rec); + + /* link it into the free list */ + offset = tdb->map_size - size; + if (tdb_free(tdb, offset, &rec) == -1) + goto fail; + + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + +/* read/write a tdb_off_t */ +int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) +{ + return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); +} + +int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) +{ + tdb_off_t off = *d; + return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); +} + + +/* read a lump of data, allocating the space for it */ +unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) +{ + unsigned char *buf; + + /* some systems don't like zero length malloc */ + if (len == 0) { + len = 1; + } + + if (!(buf = (unsigned char *)malloc(len))) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_OOM; + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n", + len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_OOM, buf); + } + if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) { + SAFE_FREE(buf); + return NULL; + } + return buf; +} + +/* Give a piece of tdb data to a parser */ + +int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, + tdb_off_t offset, tdb_len_t len, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + TDB_DATA data; + int result; + + data.dsize = len; + + if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) { + /* + * Optimize by avoiding the malloc/memcpy/free, point the + * parser directly at the mmap area. + */ + if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) { + return -1; + } + data.dptr = offset + (unsigned char *)tdb->map_ptr; + return parser(key, data, private_data); + } + + if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) { + return -1; + } + + result = parser(key, data, private_data); + free(data.dptr); + return result; +} + +/* read/write a record */ +int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + if (TDB_BAD_MAGIC(rec)) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0); +} + +int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + struct list_struct r = *rec; + return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r)); +} + +static const struct tdb_methods io_methods = { + tdb_read, + tdb_write, + tdb_next_hash_chain, + tdb_oob, + tdb_expand_file, + tdb_brlock +}; + +/* + initialise the default methods table +*/ +void tdb_io_init(struct tdb_context *tdb) +{ + tdb->methods = &io_methods; +} + +/* file: transaction.c */ + +/* + transaction design: + + - only allow a single transaction at a time per database. This makes + using the transaction API simpler, as otherwise the caller would + have to cope with temporary failures in transactions that conflict + with other current transactions + + - keep the transaction recovery information in the same file as the + database, using a special 'transaction recovery' record pointed at + by the header. This removes the need for extra journal files as + used by some other databases + + - dynamically allocated the transaction recover record, re-using it + for subsequent transactions. If a larger record is needed then + tdb_free() the old record to place it on the normal tdb freelist + before allocating the new record + + - during transactions, keep a linked list of writes all that have + been performed by intercepting all tdb_write() calls. The hooked + transaction versions of tdb_read() and tdb_write() check this + linked list and try to use the elements of the list in preference + to the real database. + + - don't allow any locks to be held when a transaction starts, + otherwise we can end up with deadlock (plus lack of lock nesting + in posix locks would mean the lock is lost) + + - if the caller gains a lock during the transaction but doesn't + release it then fail the commit + + - allow for nested calls to tdb_transaction_start(), re-using the + existing transaction record. If the inner transaction is cancelled + then a subsequent commit will fail + + - keep a mirrored copy of the tdb hash chain heads to allow for the + fast hash heads scan on traverse, updating the mirrored copy in + the transaction version of tdb_write + + - allow callers to mix transaction and non-transaction use of tdb, + although once a transaction is started then an exclusive lock is + gained until the transaction is committed or cancelled + + - the commit stategy involves first saving away all modified data + into a linearised buffer in the transaction recovery area, then + marking the transaction recovery area with a magic value to + indicate a valid recovery record. In total 4 fsync/msync calls are + needed per commit to prevent race conditions. It might be possible + to reduce this to 3 or even 2 with some more work. + + - check for a valid recovery record on open of the tdb, while the + global lock is held. Automatically recover from the transaction + recovery area if needed, then continue with the open as + usual. This allows for smooth crash recovery with no administrator + intervention. + + - if TDB_NOSYNC is passed to flags in tdb_open then transactions are + still available, but no transaction recovery area is used and no + fsync/msync calls are made. + +*/ + +struct tdb_transaction_el { + struct tdb_transaction_el *next, *prev; + tdb_off_t offset; + tdb_len_t length; + unsigned char *data; +}; + +/* + hold the context of any current transaction +*/ +struct tdb_transaction { + /* we keep a mirrored copy of the tdb hash heads here so + tdb_next_hash_chain() can operate efficiently */ + u32 *hash_heads; + + /* the original io methods - used to do IOs to the real db */ + const struct tdb_methods *io_methods; + + /* the list of transaction elements. We use a doubly linked + list with a last pointer to allow us to keep the list + ordered, with first element at the front of the list. It + needs to be doubly linked as the read/write traversals need + to be backwards, while the commit needs to be forwards */ + struct tdb_transaction_el *elements, *elements_last; + + /* non-zero when an internal transaction error has + occurred. All write operations will then fail until the + transaction is ended */ + int transaction_error; + + /* when inside a transaction we need to keep track of any + nested tdb_transaction_start() calls, as these are allowed, + but don't create a new transaction */ + int nesting; + + /* old file size before transaction */ + tdb_len_t old_map_size; +}; + + +/* + read while in a transaction. We need to check first if the data is in our list + of transaction elements, then if not do a real read +*/ +static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, + tdb_len_t len, int cv) +{ + struct tdb_transaction_el *el; + + /* we need to walk the list backwards to get the most recent data */ + for (el=tdb->transaction->elements_last;el;el=el->prev) { + tdb_len_t partial; + + if (off+len <= el->offset) { + continue; + } + if (off >= el->offset + el->length) { + continue; + } + + /* an overlapping read - needs to be split into up to + 2 reads and a memcpy */ + if (off < el->offset) { + partial = el->offset - off; + if (transaction_read(tdb, off, buf, partial, cv) != 0) { + goto fail; + } + len -= partial; + off += partial; + buf = (void *)(partial + (char *)buf); + } + if (off + len <= el->offset + el->length) { + partial = len; + } else { + partial = el->offset + el->length - off; + } + memcpy(buf, el->data + (off - el->offset), partial); + if (cv) { + tdb_convert(buf, len); + } + len -= partial; + off += partial; + buf = (void *)(partial + (char *)buf); + + if (len != 0 && transaction_read(tdb, off, buf, len, cv) != 0) { + goto fail; + } + + return 0; + } + + /* its not in the transaction elements - do a real read */ + return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv); + +fail: + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len)); + tdb->ecode = TDB_ERR_IO; + tdb->transaction->transaction_error = 1; + return -1; +} + + +/* + write while in a transaction +*/ +static int transaction_write(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) +{ + struct tdb_transaction_el *el, *best_el=NULL; + + if (len == 0) { + return 0; + } + + /* if the write is to a hash head, then update the transaction + hash heads */ + if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP && + off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) { + u32 chain = (off-FREELIST_TOP) / sizeof(tdb_off_t); + memcpy(&tdb->transaction->hash_heads[chain], buf, len); + } + + /* first see if we can replace an existing entry */ + for (el=tdb->transaction->elements_last;el;el=el->prev) { + tdb_len_t partial; + + if (best_el == NULL && off == el->offset+el->length) { + best_el = el; + } + + if (off+len <= el->offset) { + continue; + } + if (off >= el->offset + el->length) { + continue; + } + + /* an overlapping write - needs to be split into up to + 2 writes and a memcpy */ + if (off < el->offset) { + partial = el->offset - off; + if (transaction_write(tdb, off, buf, partial) != 0) { + goto fail; + } + len -= partial; + off += partial; + buf = (const void *)(partial + (const char *)buf); + } + if (off + len <= el->offset + el->length) { + partial = len; + } else { + partial = el->offset + el->length - off; + } + memcpy(el->data + (off - el->offset), buf, partial); + len -= partial; + off += partial; + buf = (const void *)(partial + (const char *)buf); + + if (len != 0 && transaction_write(tdb, off, buf, len) != 0) { + goto fail; + } + + return 0; + } + + /* see if we can append the new entry to an existing entry */ + if (best_el && best_el->offset + best_el->length == off && + (off+len < tdb->transaction->old_map_size || + off > tdb->transaction->old_map_size)) { + unsigned char *data = best_el->data; + el = best_el; + el->data = (unsigned char *)realloc(el->data, + el->length + len); + if (el->data == NULL) { + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + el->data = data; + return -1; + } + if (buf) { + memcpy(el->data + el->length, buf, len); + } else { + memset(el->data + el->length, TDB_PAD_BYTE, len); + } + el->length += len; + return 0; + } + + /* add a new entry at the end of the list */ + el = (struct tdb_transaction_el *)malloc(sizeof(*el)); + if (el == NULL) { + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + return -1; + } + el->next = NULL; + el->prev = tdb->transaction->elements_last; + el->offset = off; + el->length = len; + el->data = (unsigned char *)malloc(len); + if (el->data == NULL) { + free(el); + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + return -1; + } + if (buf) { + memcpy(el->data, buf, len); + } else { + memset(el->data, TDB_PAD_BYTE, len); + } + if (el->prev) { + el->prev->next = el; + } else { + tdb->transaction->elements = el; + } + tdb->transaction->elements_last = el; + return 0; + +fail: + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len)); + tdb->ecode = TDB_ERR_IO; + tdb->transaction->transaction_error = 1; + return -1; +} + +/* + accelerated hash chain head search, using the cached hash heads +*/ +static void transaction_next_hash_chain(struct tdb_context *tdb, u32 *chain) +{ + u32 h = *chain; + for (;h < tdb->header.hash_size;h++) { + /* the +1 takes account of the freelist */ + if (0 != tdb->transaction->hash_heads[h+1]) { + break; + } + } + (*chain) = h; +} + +/* + out of bounds check during a transaction +*/ +static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe) +{ + if (len <= tdb->map_size) { + return 0; + } + return TDB_ERRCODE(TDB_ERR_IO, -1); +} + +/* + transaction version of tdb_expand(). +*/ +static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, + tdb_off_t addition) +{ + /* add a write to the transaction elements, so subsequent + reads see the zero data */ + if (transaction_write(tdb, size, NULL, addition) != 0) { + return -1; + } + + return 0; +} + +/* + brlock during a transaction - ignore them +*/ +static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, + int rw_type, int lck_type, int probe, size_t len) +{ + return 0; +} + +static const struct tdb_methods transaction_methods = { + transaction_read, + transaction_write, + transaction_next_hash_chain, + transaction_oob, + transaction_expand_file, + transaction_brlock +}; + + +/* + start a tdb transaction. No token is returned, as only a single + transaction is allowed to be pending per tdb_context +*/ +int tdb_transaction_start(struct tdb_context *tdb) +{ + /* some sanity checks */ + if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n")); + tdb->ecode = TDB_ERR_EINVAL; + return -1; + } + + /* cope with nested tdb_transaction_start() calls */ + if (tdb->transaction != NULL) { + tdb->transaction->nesting++; + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", + tdb->transaction->nesting)); + return 0; + } + + if (tdb->num_locks != 0 || tdb->global_lock.count) { + /* the caller must not have any locks when starting a + transaction as otherwise we'll be screwed by lack + of nested locks in posix */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + + if (tdb->travlocks.next != NULL) { + /* you cannot use transactions inside a traverse (although you can use + traverse inside a transaction) as otherwise you can end up with + deadlock */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + + tdb->transaction = (struct tdb_transaction *) + calloc(sizeof(struct tdb_transaction), 1); + if (tdb->transaction == NULL) { + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + /* get the transaction write lock. This is a blocking lock. As + discussed with Volker, there are a number of ways we could + make this async, which we will probably do in the future */ + if (tdb_transaction_lock(tdb, F_WRLCK) == -1) { + SAFE_FREE(tdb->transaction); + return -1; + } + + /* get a read lock from the freelist to the end of file. This + is upgraded to a write lock during the commit */ + if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n")); + tdb->ecode = TDB_ERR_LOCK; + goto fail; + } + + /* setup a copy of the hash table heads so the hash scan in + traverse can be fast */ + tdb->transaction->hash_heads = (u32 *) + calloc(tdb->header.hash_size+1, sizeof(u32)); + if (tdb->transaction->hash_heads == NULL) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads, + TDB_HASHTABLE_SIZE(tdb), 0) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n")); + tdb->ecode = TDB_ERR_IO; + goto fail; + } + + /* make sure we know about any file expansions already done by + anyone else */ + tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); + tdb->transaction->old_map_size = tdb->map_size; + + /* finally hook the io methods, replacing them with + transaction specific methods */ + tdb->transaction->io_methods = tdb->methods; + tdb->methods = &transaction_methods; + + /* by calling this transaction write here, we ensure that we don't grow the + transaction linked list due to hash table updates */ + if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, + TDB_HASHTABLE_SIZE(tdb)) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n")); + tdb->ecode = TDB_ERR_IO; + tdb->methods = tdb->transaction->io_methods; + goto fail; + } + + return 0; + +fail: + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_transaction_unlock(tdb); + SAFE_FREE(tdb->transaction->hash_heads); + SAFE_FREE(tdb->transaction); + return -1; +} + + +/* + cancel the current transaction +*/ +int tdb_transaction_cancel(struct tdb_context *tdb) +{ + if (tdb->transaction == NULL) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); + return -1; + } + + if (tdb->transaction->nesting != 0) { + tdb->transaction->transaction_error = 1; + tdb->transaction->nesting--; + return 0; + } + + tdb->map_size = tdb->transaction->old_map_size; + + /* free all the transaction elements */ + while (tdb->transaction->elements) { + struct tdb_transaction_el *el = tdb->transaction->elements; + tdb->transaction->elements = el->next; + free(el->data); + free(el); + } + + /* remove any global lock created during the transaction */ + if (tdb->global_lock.count != 0) { + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); + tdb->global_lock.count = 0; + } + + /* remove any locks created during the transaction */ + if (tdb->num_locks != 0) { + int i; + for (i=0;inum_lockrecs;i++) { + tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, + F_UNLCK,F_SETLKW, 0, 1); + } + tdb->num_locks = 0; + tdb->num_lockrecs = 0; + SAFE_FREE(tdb->lockrecs); + } + + /* restore the normal io methods */ + tdb->methods = tdb->transaction->io_methods; + + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_transaction_unlock(tdb); + SAFE_FREE(tdb->transaction->hash_heads); + SAFE_FREE(tdb->transaction); + + return 0; +} + +/* + sync to disk +*/ +static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) +{ + if (fsync(tdb->fd) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); + return -1; + } +#ifdef MS_SYNC + if (tdb->map_ptr) { + tdb_off_t moffset = offset & ~(tdb->page_size-1); + if (msync(moffset + (char *)tdb->map_ptr, + length + (offset - moffset), MS_SYNC) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", + strerror(errno))); + return -1; + } + } +#endif + return 0; +} + + +/* + work out how much space the linearised recovery data will consume +*/ +static tdb_len_t tdb_recovery_size(struct tdb_context *tdb) +{ + struct tdb_transaction_el *el; + tdb_len_t recovery_size = 0; + + recovery_size = sizeof(u32); + for (el=tdb->transaction->elements;el;el=el->next) { + if (el->offset >= tdb->transaction->old_map_size) { + continue; + } + recovery_size += 2*sizeof(tdb_off_t) + el->length; + } + + return recovery_size; +} + +/* + allocate the recovery area, or use an existing recovery area if it is + large enough +*/ +static int tdb_recovery_allocate(struct tdb_context *tdb, + tdb_len_t *recovery_size, + tdb_off_t *recovery_offset, + tdb_len_t *recovery_max_size) +{ + struct list_struct rec; + const struct tdb_methods *methods = tdb->transaction->io_methods; + tdb_off_t recovery_head; + + if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n")); + return -1; + } + + rec.rec_len = 0; + + if (recovery_head != 0 && + methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n")); + return -1; + } + + *recovery_size = tdb_recovery_size(tdb); + + if (recovery_head != 0 && *recovery_size <= rec.rec_len) { + /* it fits in the existing area */ + *recovery_max_size = rec.rec_len; + *recovery_offset = recovery_head; + return 0; + } + + /* we need to free up the old recovery area, then allocate a + new one at the end of the file. Note that we cannot use + tdb_allocate() to allocate the new one as that might return + us an area that is being currently used (as of the start of + the transaction) */ + if (recovery_head != 0) { + if (tdb_free(tdb, recovery_head, &rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n")); + return -1; + } + } + + /* the tdb_free() call might have increased the recovery size */ + *recovery_size = tdb_recovery_size(tdb); + + /* round up to a multiple of page size */ + *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec); + *recovery_offset = tdb->map_size; + recovery_head = *recovery_offset; + + if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, + (tdb->map_size - tdb->transaction->old_map_size) + + sizeof(rec) + *recovery_max_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n")); + return -1; + } + + /* remap the file (if using mmap) */ + methods->tdb_oob(tdb, tdb->map_size + 1, 1); + + /* we have to reset the old map size so that we don't try to expand the file + again in the transaction commit, which would destroy the recovery area */ + tdb->transaction->old_map_size = tdb->map_size; + + /* write the recovery header offset and sync - we can sync without a race here + as the magic ptr in the recovery record has not been set */ + CONVERT(recovery_head); + if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, + &recovery_head, sizeof(tdb_off_t)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); + return -1; + } + + return 0; +} + + +/* + setup the recovery data that will be used on a crash during commit +*/ +static int transaction_setup_recovery(struct tdb_context *tdb, + tdb_off_t *magic_offset) +{ + struct tdb_transaction_el *el; + tdb_len_t recovery_size; + unsigned char *data, *p; + const struct tdb_methods *methods = tdb->transaction->io_methods; + struct list_struct *rec; + tdb_off_t recovery_offset, recovery_max_size; + tdb_off_t old_map_size = tdb->transaction->old_map_size; + u32 magic, tailer; + + /* + check that the recovery area has enough space + */ + if (tdb_recovery_allocate(tdb, &recovery_size, + &recovery_offset, &recovery_max_size) == -1) { + return -1; + } + + data = (unsigned char *)malloc(recovery_size + sizeof(*rec)); + if (data == NULL) { + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + rec = (struct list_struct *)data; + memset(rec, 0, sizeof(*rec)); + + rec->magic = 0; + rec->data_len = recovery_size; + rec->rec_len = recovery_max_size; + rec->key_len = old_map_size; + CONVERT(rec); + + /* build the recovery data into a single blob to allow us to do a single + large write, which should be more efficient */ + p = data + sizeof(*rec); + for (el=tdb->transaction->elements;el;el=el->next) { + if (el->offset >= old_map_size) { + continue; + } + if (el->offset + el->length > tdb->transaction->old_map_size) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n")); + free(data); + tdb->ecode = TDB_ERR_CORRUPT; + return -1; + } + memcpy(p, &el->offset, 4); + memcpy(p+4, &el->length, 4); + if (DOCONV()) { + tdb_convert(p, 8); + } + /* the recovery area contains the old data, not the + new data, so we have to call the original tdb_read + method to get it */ + if (methods->tdb_read(tdb, el->offset, p + 8, el->length, 0) != 0) { + free(data); + tdb->ecode = TDB_ERR_IO; + return -1; + } + p += 8 + el->length; + } + + /* and the tailer */ + tailer = sizeof(*rec) + recovery_max_size; + memcpy(p, &tailer, 4); + CONVERT(p); + + /* write the recovery data to the recovery area */ + if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n")); + free(data); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* as we don't have ordered writes, we have to sync the recovery + data before we update the magic to indicate that the recovery + data is present */ + if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) { + free(data); + return -1; + } + + free(data); + + magic = TDB_RECOVERY_MAGIC; + CONVERT(magic); + + *magic_offset = recovery_offset + offsetof(struct list_struct, magic); + + if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* ensure the recovery magic marker is on disk */ + if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { + return -1; + } + + return 0; +} + +/* + commit the current transaction +*/ +int tdb_transaction_commit(struct tdb_context *tdb) +{ + const struct tdb_methods *methods; + tdb_off_t magic_offset = 0; + u32 zero = 0; + + if (tdb->transaction == NULL) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); + return -1; + } + + if (tdb->transaction->transaction_error) { + tdb->ecode = TDB_ERR_IO; + tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); + return -1; + } + + if (tdb->transaction->nesting != 0) { + tdb->transaction->nesting--; + return 0; + } + + /* check for a null transaction */ + if (tdb->transaction->elements == NULL) { + tdb_transaction_cancel(tdb); + return 0; + } + + methods = tdb->transaction->io_methods; + + /* if there are any locks pending then the caller has not + nested their locks properly, so fail the transaction */ + if (tdb->num_locks || tdb->global_lock.count) { + tdb->ecode = TDB_ERR_LOCK; + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n")); + tdb_transaction_cancel(tdb); + return -1; + } + + /* upgrade the main transaction lock region to a write lock */ + if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n")); + tdb->ecode = TDB_ERR_LOCK; + tdb_transaction_cancel(tdb); + return -1; + } + + /* get the global lock - this prevents new users attaching to the database + during the commit */ + if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n")); + tdb->ecode = TDB_ERR_LOCK; + tdb_transaction_cancel(tdb); + return -1; + } + + if (!(tdb->flags & TDB_NOSYNC)) { + /* write the recovery data to the end of the file */ + if (transaction_setup_recovery(tdb, &magic_offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_transaction_cancel(tdb); + return -1; + } + } + + /* expand the file to the new size if needed */ + if (tdb->map_size != tdb->transaction->old_map_size) { + if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, + tdb->map_size - + tdb->transaction->old_map_size) == -1) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_transaction_cancel(tdb); + return -1; + } + tdb->map_size = tdb->transaction->old_map_size; + methods->tdb_oob(tdb, tdb->map_size + 1, 1); + } + + /* perform all the writes */ + while (tdb->transaction->elements) { + struct tdb_transaction_el *el = tdb->transaction->elements; + + if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); + + /* we've overwritten part of the data and + possibly expanded the file, so we need to + run the crash recovery code */ + tdb->methods = methods; + tdb_transaction_recover(tdb); + + tdb_transaction_cancel(tdb); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); + return -1; + } + tdb->transaction->elements = el->next; + free(el->data); + free(el); + } + + if (!(tdb->flags & TDB_NOSYNC)) { + /* ensure the new data is on disk */ + if (transaction_sync(tdb, 0, tdb->map_size) == -1) { + return -1; + } + + /* remove the recovery marker */ + if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n")); + return -1; + } + + /* ensure the recovery marker has been removed on disk */ + if (transaction_sync(tdb, magic_offset, 4) == -1) { + return -1; + } + } + + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + + /* + TODO: maybe write to some dummy hdr field, or write to magic + offset without mmap, before the last sync, instead of the + utime() call + */ + + /* on some systems (like Linux 2.6.x) changes via mmap/msync + don't change the mtime of the file, this means the file may + not be backed up (as tdb rounding to block sizes means that + file size changes are quite rare too). The following forces + mtime changes when a transaction completes */ +#ifdef HAVE_UTIME + utime(tdb->name, NULL); +#endif + + /* use a transaction cancel to free memory and remove the + transaction locks */ + tdb_transaction_cancel(tdb); + return 0; +} + + +/* + recover from an aborted transaction. Must be called with exclusive + database write access already established (including the global + lock to prevent new processes attaching) +*/ +int tdb_transaction_recover(struct tdb_context *tdb) +{ + tdb_off_t recovery_head, recovery_eof; + unsigned char *data, *p; + u32 zero = 0; + struct list_struct rec; + + /* find the recovery area */ + if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + if (recovery_head == 0) { + /* we have never allocated a recovery record */ + return 0; + } + + /* read the recovery record */ + if (tdb->methods->tdb_read(tdb, recovery_head, &rec, + sizeof(rec), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + if (rec.magic != TDB_RECOVERY_MAGIC) { + /* there is no valid recovery data */ + return 0; + } + + if (tdb->read_only) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n")); + tdb->ecode = TDB_ERR_CORRUPT; + return -1; + } + + recovery_eof = rec.key_len; + + data = (unsigned char *)malloc(rec.data_len); + if (data == NULL) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n")); + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + /* read the full recovery data */ + if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data, + rec.data_len, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* recover the file data */ + p = data; + while (p+8 < data + rec.data_len) { + u32 ofs, len; + if (DOCONV()) { + tdb_convert(p, 8); + } + memcpy(&ofs, p, 4); + memcpy(&len, p+4, 4); + + if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) { + free(data); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs)); + tdb->ecode = TDB_ERR_IO; + return -1; + } + p += 8 + len; + } + + free(data); + + if (transaction_sync(tdb, 0, tdb->map_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* if the recovery area is after the recovered eof then remove it */ + if (recovery_eof <= recovery_head) { + if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + } + + /* remove the recovery magic */ + if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), + &zero) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* reduce the file size to the old size */ + tdb_munmap(tdb); + if (ftruncate(tdb->fd, recovery_eof) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + tdb->map_size = recovery_eof; + tdb_mmap(tdb); + + if (transaction_sync(tdb, 0, recovery_eof) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", + recovery_eof)); + + /* all done */ + return 0; +} + +/* file: freelist.c */ + +/* read a freelist record and check for simple errors */ +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) +{ + if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + + if (rec->magic == TDB_MAGIC) { + /* this happens when a app is showdown while deleting a record - we should + not completely fail when this happens */ + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n", + rec->magic, off)); + rec->magic = TDB_FREE_MAGIC; + if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1) + return -1; + } + + if (rec->magic != TDB_FREE_MAGIC) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", + rec->magic, off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) + return -1; + return 0; +} + + + +/* Remove an element from the freelist. Must have alloc lock. */ +static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next) +{ + tdb_off_t last_ptr, i; + + /* read in the freelist top */ + last_ptr = FREELIST_TOP; + while (tdb_ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { + if (i == off) { + /* We've found it! */ + return tdb_ofs_write(tdb, last_ptr, &next); + } + /* Follow chain (next offset is at start of record) */ + last_ptr = i; + } + TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); +} + + +/* update a record tailer (must hold allocation lock) */ +static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, + const struct list_struct *rec) +{ + tdb_off_t totalsize; + + /* Offset of tailer from record header */ + totalsize = sizeof(*rec) + rec->rec_len; + return tdb_ofs_write(tdb, offset + totalsize - sizeof(tdb_off_t), + &totalsize); +} + +/* Add an element into the freelist. Merge adjacent records if + neccessary. */ +int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + tdb_off_t right, left; + + /* Allocation and tailer lock */ + if (tdb_lock(tdb, -1, F_WRLCK) != 0) + return -1; + + /* set an initial tailer, so if we fail we don't leave a bogus record */ + if (update_tailer(tdb, offset, rec) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n")); + goto fail; + } + + /* Look right first (I'm an Australian, dammit) */ + right = offset + sizeof(*rec) + rec->rec_len; + if (right + sizeof(*rec) <= tdb->map_size) { + struct list_struct r; + + if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right)); + goto left; + } + + /* If it's free, expand to include it. */ + if (r.magic == TDB_FREE_MAGIC) { + if (remove_from_freelist(tdb, right, r.next) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right)); + goto left; + } + rec->rec_len += sizeof(r) + r.rec_len; + } + } + +left: + /* Look left */ + left = offset - sizeof(tdb_off_t); + if (left > TDB_DATA_START(tdb->header.hash_size)) { + struct list_struct l; + tdb_off_t leftsize; + + /* Read in tailer and jump back to header */ + if (tdb_ofs_read(tdb, left, &leftsize) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left)); + goto update; + } + + /* it could be uninitialised data */ + if (leftsize == 0 || leftsize == TDB_PAD_U32) { + goto update; + } + + left = offset - leftsize; + + /* Now read in record */ + if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); + goto update; + } + + /* If it's free, expand to include it. */ + if (l.magic == TDB_FREE_MAGIC) { + if (remove_from_freelist(tdb, left, l.next) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left)); + goto update; + } else { + offset = left; + rec->rec_len += leftsize; + } + } + } + +update: + if (update_tailer(tdb, offset, rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); + goto fail; + } + + /* Now, prepend to free list */ + rec->magic = TDB_FREE_MAGIC; + + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || + tdb_rec_write(tdb, offset, rec) == -1 || + tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset)); + goto fail; + } + + /* And we're done. */ + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + + +/* + the core of tdb_allocate - called when we have decided which + free list entry to use + */ +static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr, + struct list_struct *rec, tdb_off_t last_ptr) +{ + struct list_struct newrec; + tdb_off_t newrec_ptr; + + memset(&newrec, '\0', sizeof(newrec)); + + /* found it - now possibly split it up */ + if (rec->rec_len > length + MIN_REC_SIZE) { + /* Length of left piece */ + length = TDB_ALIGN(length, TDB_ALIGNMENT); + + /* Right piece to go on free list */ + newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); + newrec_ptr = rec_ptr + sizeof(*rec) + length; + + /* And left record is shortened */ + rec->rec_len = length; + } else { + newrec_ptr = 0; + } + + /* Remove allocated record from the free list */ + if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) { + return 0; + } + + /* Update header: do this before we drop alloc + lock, otherwise tdb_free() might try to + merge with us, thinking we're free. + (Thanks Jeremy Allison). */ + rec->magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { + return 0; + } + + /* Did we create new block? */ + if (newrec_ptr) { + /* Update allocated record tailer (we + shortened it). */ + if (update_tailer(tdb, rec_ptr, rec) == -1) { + return 0; + } + + /* Free new record */ + if (tdb_free(tdb, newrec_ptr, &newrec) == -1) { + return 0; + } + } + + /* all done - return the new record offset */ + return rec_ptr; +} + +/* allocate some space from the free list. The offset returned points + to a unconnected list_struct within the database with room for at + least length bytes of total data + + 0 is returned if the space could not be allocated + */ +tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec) +{ + tdb_off_t rec_ptr, last_ptr, newrec_ptr; + struct { + tdb_off_t rec_ptr, last_ptr; + tdb_len_t rec_len; + } bestfit; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) + return 0; + + /* Extra bytes required for tailer */ + length += sizeof(tdb_off_t); + + again: + last_ptr = FREELIST_TOP; + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) + goto fail; + + bestfit.rec_ptr = 0; + bestfit.last_ptr = 0; + bestfit.rec_len = 0; + + /* + this is a best fit allocation strategy. Originally we used + a first fit strategy, but it suffered from massive fragmentation + issues when faced with a slowly increasing record size. + */ + while (rec_ptr) { + if (tdb_rec_free_read(tdb, rec_ptr, rec) == -1) { + goto fail; + } + + if (rec->rec_len >= length) { + if (bestfit.rec_ptr == 0 || + rec->rec_len < bestfit.rec_len) { + bestfit.rec_len = rec->rec_len; + bestfit.rec_ptr = rec_ptr; + bestfit.last_ptr = last_ptr; + /* consider a fit to be good enough if + we aren't wasting more than half + the space */ + if (bestfit.rec_len < 2*length) { + break; + } + } + } + + /* move to the next record */ + last_ptr = rec_ptr; + rec_ptr = rec->next; + } + + if (bestfit.rec_ptr != 0) { + if (tdb_rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) { + goto fail; + } + + newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr); + tdb_unlock(tdb, -1, F_WRLCK); + return newrec_ptr; + } + + /* we didn't find enough space. See if we can expand the + database and if we can then try again */ + if (tdb_expand(tdb, length + sizeof(*rec)) == 0) + goto again; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return 0; +} + +/* file: freelistcheck.c */ + +/* Check the freelist is good and contains no loops. + Very memory intensive - only do this as a consistency + checker. Heh heh - uses an in memory tdb as the storage + for the "seen" record list. For some reason this strikes + me as extremely clever as I don't have to write another tree + data structure implementation :-). + */ + +static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr) +{ + TDB_DATA key, data; + + memset(&data, '\0', sizeof(data)); + key.dptr = (unsigned char *)&rec_ptr; + key.dsize = sizeof(rec_ptr); + return tdb_store(mem_tdb, key, data, TDB_INSERT); +} + +int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) +{ + struct tdb_context *mem_tdb = NULL; + struct list_struct rec; + tdb_off_t rec_ptr, last_ptr; + int ret = -1; + + *pnum_entries = 0; + + mem_tdb = tdb_open("flval", tdb->header.hash_size, + TDB_INTERNAL, O_RDWR, 0600); + if (!mem_tdb) { + return -1; + } + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + tdb_close(mem_tdb); + return 0; + } + + last_ptr = FREELIST_TOP; + + /* Store the FREELIST_TOP record. */ + if (seen_insert(mem_tdb, last_ptr) == -1) { + ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + goto fail; + } + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) { + goto fail; + } + + while (rec_ptr) { + + /* If we can't store this record (we've seen it + before) then the free list has a loop and must + be corrupt. */ + + if (seen_insert(mem_tdb, rec_ptr)) { + ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + goto fail; + } + + if (tdb_rec_free_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + /* move to the next record */ + last_ptr = rec_ptr; + rec_ptr = rec.next; + *pnum_entries += 1; + } + + ret = 0; + + fail: + + tdb_close(mem_tdb); + tdb_unlock(tdb, -1, F_WRLCK); + return ret; +} + +/* file: traverse.c */ + +/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ +static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock, + struct list_struct *rec) +{ + int want_next = (tlock->off != 0); + + /* Lock each chain from the start one. */ + for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { + if (!tlock->off && tlock->hash != 0) { + /* this is an optimisation for the common case where + the hash chain is empty, which is particularly + common for the use of tdb with ldb, where large + hashes are used. In that case we spend most of our + time in tdb_brlock(), locking empty hash chains. + + To avoid this, we do an unlocked pre-check to see + if the hash chain is empty before starting to look + inside it. If it is empty then we can avoid that + hash chain. If it isn't empty then we can't believe + the value we get back, as we read it without a + lock, so instead we get the lock and re-fetch the + value below. + + Notice that not doing this optimisation on the + first hash chain is critical. We must guarantee + that we have done at least one fcntl lock at the + start of a search to guarantee that memory is + coherent on SMP systems. If records are added by + others during the search then thats OK, and we + could possibly miss those with this trick, but we + could miss them anyway without this trick, so the + semantics don't change. + + With a non-indexed ldb search this trick gains us a + factor of around 80 in speed on a linux 2.6.x + system (testing using ldbtest). + */ + tdb->methods->next_hash_chain(tdb, &tlock->hash); + if (tlock->hash == tdb->header.hash_size) { + continue; + } + } + + if (tdb_lock(tdb, tlock->hash, tlock->lock_rw) == -1) + return -1; + + /* No previous record? Start at top of chain. */ + if (!tlock->off) { + if (tdb_ofs_read(tdb, TDB_HASH_TOP(tlock->hash), + &tlock->off) == -1) + goto fail; + } else { + /* Otherwise unlock the previous record. */ + if (tdb_unlock_record(tdb, tlock->off) != 0) + goto fail; + } + + if (want_next) { + /* We have offset of old record: grab next */ + if (tdb_rec_read(tdb, tlock->off, rec) == -1) + goto fail; + tlock->off = rec->next; + } + + /* Iterate through chain */ + while( tlock->off) { + tdb_off_t current; + if (tdb_rec_read(tdb, tlock->off, rec) == -1) + goto fail; + + /* Detect infinite loops. From "Shlomi Yaakobovich" . */ + if (tlock->off == rec->next) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n")); + goto fail; + } + + if (!TDB_DEAD(rec)) { + /* Woohoo: we found one! */ + if (tdb_lock_record(tdb, tlock->off) != 0) + goto fail; + return tlock->off; + } + + /* Try to clean dead ones from old traverses */ + current = tlock->off; + tlock->off = rec->next; + if (!(tdb->read_only || tdb->traverse_read) && + tdb_do_delete(tdb, current, rec) != 0) + goto fail; + } + tdb_unlock(tdb, tlock->hash, tlock->lock_rw); + want_next = 0; + } + /* We finished iteration without finding anything */ + return TDB_ERRCODE(TDB_SUCCESS, 0); + + fail: + tlock->off = 0; + if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n")); + return -1; +} + +/* traverse the entire database - calling fn(tdb, key, data) on each element. + return -1 on error or the record count traversed + if fn is NULL then it is not called + a non-zero return value from fn() indicates that the traversal should stop + */ +static int tdb_traverse_internal(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data, + struct tdb_traverse_lock *tl) +{ + TDB_DATA key, dbuf; + struct list_struct rec; + int ret, count = 0; + + /* This was in the initializaton, above, but the IRIX compiler + * did not like it. crh + */ + tl->next = tdb->travlocks.next; + + /* fcntl locks don't stack: beware traverse inside traverse */ + tdb->travlocks.next = tl; + + /* tdb_next_lock places locks on the record returned, and its chain */ + while ((ret = tdb_next_lock(tdb, tl, &rec)) > 0) { + count++; + /* now read the full record */ + key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), + rec.key_len + rec.data_len); + if (!key.dptr) { + ret = -1; + if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) + goto out; + if (tdb_unlock_record(tdb, tl->off) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); + goto out; + } + key.dsize = rec.key_len; + dbuf.dptr = key.dptr + rec.key_len; + dbuf.dsize = rec.data_len; + + /* Drop chain lock, call out */ + if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) { + ret = -1; + SAFE_FREE(key.dptr); + goto out; + } + if (fn && fn(tdb, key, dbuf, private_data)) { + /* They want us to terminate traversal */ + ret = count; + if (tdb_unlock_record(tdb, tl->off) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; + ret = -1; + } + SAFE_FREE(key.dptr); + goto out; + } + SAFE_FREE(key.dptr); + } +out: + tdb->travlocks.next = tl->next; + if (ret < 0) + return -1; + else + return count; +} + + +/* + a write style traverse - temporarily marks the db read only +*/ +int tdb_traverse_read(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data) +{ + struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; + int ret; + + /* we need to get a read lock on the transaction lock here to + cope with the lock ordering semantics of solaris10 */ + if (tdb_transaction_lock(tdb, F_RDLCK)) { + return -1; + } + + tdb->traverse_read++; + ret = tdb_traverse_internal(tdb, fn, private_data, &tl); + tdb->traverse_read--; + + tdb_transaction_unlock(tdb); + + return ret; +} + +/* + a write style traverse - needs to get the transaction lock to + prevent deadlocks +*/ +int tdb_traverse(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data) +{ + struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; + int ret; + + if (tdb->read_only || tdb->traverse_read) { + return tdb_traverse_read(tdb, fn, private_data); + } + + if (tdb_transaction_lock(tdb, F_WRLCK)) { + return -1; + } + + ret = tdb_traverse_internal(tdb, fn, private_data, &tl); + + tdb_transaction_unlock(tdb); + + return ret; +} + + +/* find the first entry in the database and return its key */ +TDB_DATA tdb_firstkey(struct tdb_context *tdb) +{ + TDB_DATA key; + struct list_struct rec; + + /* release any old lock */ + if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) + return tdb_null; + tdb->travlocks.off = tdb->travlocks.hash = 0; + tdb->travlocks.lock_rw = F_RDLCK; + + /* Grab first record: locks chain and returned record. */ + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) + return tdb_null; + /* now read the key */ + key.dsize = rec.key_len; + key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); + + /* Unlock the hash chain of the record we just read. */ + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n")); + return key; +} + +/* find the next entry in the database, returning its key */ +TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) +{ + u32 oldhash; + TDB_DATA key = tdb_null; + struct list_struct rec; + unsigned char *k = NULL; + + /* Is locked key the old key? If so, traverse will be reliable. */ + if (tdb->travlocks.off) { + if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw)) + return tdb_null; + if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1 + || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), + rec.key_len)) + || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { + /* No, it wasn't: unlock it and start from scratch */ + if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) { + SAFE_FREE(k); + return tdb_null; + } + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) { + SAFE_FREE(k); + return tdb_null; + } + tdb->travlocks.off = 0; + } + + SAFE_FREE(k); + } + + if (!tdb->travlocks.off) { + /* No previous element: do normal find, and lock record */ + tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec); + if (!tdb->travlocks.off) + return tdb_null; + tdb->travlocks.hash = BUCKET(rec.full_hash); + if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); + return tdb_null; + } + } + oldhash = tdb->travlocks.hash; + + /* Grab next record: locks chain and returned record, + unlocks old record */ + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { + key.dsize = rec.key_len; + key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), + key.dsize); + /* Unlock the chain of this new record */ + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + } + /* Unlock the chain of old record */ + if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + return key; +} + +/* file: dump.c */ + +static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash, + tdb_off_t offset) +{ + struct list_struct rec; + tdb_off_t tailer_ofs, tailer; + + if (tdb->methods->tdb_read(tdb, offset, (char *)&rec, + sizeof(rec), DOCONV()) == -1) { + printf("ERROR: failed to read record at %u\n", offset); + return 0; + } + + printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d " + "key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n", + hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, + rec.full_hash, rec.magic); + + tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off_t); + + if (tdb_ofs_read(tdb, tailer_ofs, &tailer) == -1) { + printf("ERROR: failed to read tailer at %u\n", tailer_ofs); + return rec.next; + } + + if (tailer != rec.rec_len + sizeof(rec)) { + printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n", + (unsigned int)tailer, (unsigned int)(rec.rec_len + sizeof(rec))); + } + return rec.next; +} + +static int tdb_dump_chain(struct tdb_context *tdb, int i) +{ + tdb_off_t rec_ptr, top; + + top = TDB_HASH_TOP(i); + + if (tdb_lock(tdb, i, F_WRLCK) != 0) + return -1; + + if (tdb_ofs_read(tdb, top, &rec_ptr) == -1) + return tdb_unlock(tdb, i, F_WRLCK); + + if (rec_ptr) + printf("hash=%d\n", i); + + while (rec_ptr) { + rec_ptr = tdb_dump_record(tdb, i, rec_ptr); + } + + return tdb_unlock(tdb, i, F_WRLCK); +} + +void tdb_dump_all(struct tdb_context *tdb) +{ + int i; + for (i=0;iheader.hash_size;i++) { + tdb_dump_chain(tdb, i); + } + printf("freelist:\n"); + tdb_dump_chain(tdb, -1); +} + +int tdb_printfreelist(struct tdb_context *tdb) +{ + int ret; + long total_free = 0; + tdb_off_t offset, rec_ptr; + struct list_struct rec; + + if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) + return ret; + + offset = FREELIST_TOP; + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, offset, &rec_ptr) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + } + + printf("freelist top=[0x%08x]\n", rec_ptr ); + while (rec_ptr) { + if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec, + sizeof(rec), DOCONV()) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + if (rec.magic != TDB_FREE_MAGIC) { + printf("bad magic 0x%08x in free list\n", rec.magic); + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] (end = 0x%08x)\n", + rec_ptr, rec.rec_len, rec.rec_len, rec_ptr + rec.rec_len); + total_free += rec.rec_len; + + /* move to the next record */ + rec_ptr = rec.next; + } + printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, + (int)total_free); + + return tdb_unlock(tdb, -1, F_WRLCK); +} + +/* file: tdb.c */ + +TDB_DATA tdb_null; + +/* + non-blocking increment of the tdb sequence number if the tdb has been opened using + the TDB_SEQNUM flag +*/ +void tdb_increment_seqnum_nonblock(struct tdb_context *tdb) +{ + tdb_off_t seqnum=0; + + if (!(tdb->flags & TDB_SEQNUM)) { + return; + } + + /* we ignore errors from this, as we have no sane way of + dealing with them. + */ + tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); + seqnum++; + tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum); +} + +/* + increment the tdb sequence number if the tdb has been opened using + the TDB_SEQNUM flag +*/ +static void tdb_increment_seqnum(struct tdb_context *tdb) +{ + if (!(tdb->flags & TDB_SEQNUM)) { + return; + } + + if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { + return; + } + + tdb_increment_seqnum_nonblock(tdb); + + tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1); +} + +static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data) +{ + return memcmp(data.dptr, key.dptr, data.dsize); +} + +/* Returns 0 on fail. On success, return offset of record, and fills + in rec */ +static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash, + struct list_struct *r) +{ + tdb_off_t rec_ptr; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, r) == -1) + return 0; + + if (!TDB_DEAD(r) && hash==r->full_hash + && key.dsize==r->key_len + && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r), + r->key_len, tdb_key_compare, + NULL) == 0) { + return rec_ptr; + } + rec_ptr = r->next; + } + return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); +} + +/* As tdb_find, but if you succeed, keep the lock */ +tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, + struct list_struct *rec) +{ + u32 rec_ptr; + + if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) + return 0; + if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) + tdb_unlock(tdb, BUCKET(hash), locktype); + return rec_ptr; +} + + +/* update an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1. +*/ +static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf) +{ + struct list_struct rec; + tdb_off_t rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) + return -1; + + /* must be long enough key, data and tailer */ + if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) { + tdb->ecode = TDB_SUCCESS; /* Not really an error */ + return -1; + } + + if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, + dbuf.dptr, dbuf.dsize) == -1) + return -1; + + if (dbuf.dsize != rec.data_len) { + /* update size */ + rec.data_len = dbuf.dsize; + return tdb_rec_write(tdb, rec_ptr, &rec); + } + + return 0; +} + +/* find an entry in the database given a key */ +/* If an entry doesn't exist tdb_err will be set to + * TDB_ERR_NOEXIST. If a key has no data attached + * then the TDB_DATA will have zero length but + * a non-zero pointer + */ +TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + TDB_DATA ret; + u32 hash; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) + return tdb_null; + + ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len); + ret.dsize = rec.data_len; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return ret; +} + +/* + * Find an entry in the database and hand the record's data to a parsing + * function. The parsing function is executed under the chain read lock, so it + * should be fast and should not block on other syscalls. + * + * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS. + * + * For mmapped tdb's that do not have a transaction open it points the parsing + * function directly at the mmap area, it avoids the malloc/memcpy in this + * case. If a transaction is open or no mmap is available, it has to do + * malloc/read/parse/free. + * + * This is interesting for all readers of potentially large data structures in + * the tdb records, ldb indexes being one example. + */ + +int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + int ret; + u32 hash; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + + if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { + return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + } + + ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len, parser, private_data); + + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + + return ret; +} + +/* check if an entry in the database exists + + note that 1 is returned if the key is found and 0 is returned if not found + this doesn't match the conventions in the rest of this module, but is + compatible with gdbm +*/ +static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) +{ + struct list_struct rec; + + if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) + return 0; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return 1; +} + +int tdb_exists(struct tdb_context *tdb, TDB_DATA key) +{ + u32 hash = tdb->hash_fn(&key); + return tdb_exists_hash(tdb, key, hash); +} + +/* actually delete an entry in the database given the offset */ +int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec) +{ + tdb_off_t last_ptr, i; + struct list_struct lastrec; + + if (tdb->read_only || tdb->traverse_read) return -1; + + if (tdb_write_lock_record(tdb, rec_ptr) == -1) { + /* Someone traversing here: mark it as dead */ + rec->magic = TDB_DEAD_MAGIC; + return tdb_rec_write(tdb, rec_ptr, rec); + } + if (tdb_write_unlock_record(tdb, rec_ptr) != 0) + return -1; + + /* find previous record in hash chain */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) + return -1; + for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) + if (tdb_rec_read(tdb, i, &lastrec) == -1) + return -1; + + /* unlink it: next ptr is at start of record. */ + if (last_ptr == 0) + last_ptr = TDB_HASH_TOP(rec->full_hash); + if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) + return -1; + + /* recover the space */ + if (tdb_free(tdb, rec_ptr, rec) == -1) + return -1; + return 0; +} + +static int tdb_count_dead(struct tdb_context *tdb, u32 hash) +{ + int res = 0; + tdb_off_t rec_ptr; + struct list_struct rec; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) + return 0; + + if (rec.magic == TDB_DEAD_MAGIC) { + res += 1; + } + rec_ptr = rec.next; + } + return res; +} + +/* + * Purge all DEAD records from a hash chain + */ +static int tdb_purge_dead(struct tdb_context *tdb, u32 hash) +{ + int res = -1; + struct list_struct rec; + tdb_off_t rec_ptr; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + return -1; + } + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + goto fail; + + while (rec_ptr) { + tdb_off_t next; + + if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + next = rec.next; + + if (rec.magic == TDB_DEAD_MAGIC + && tdb_do_delete(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + rec_ptr = next; + } + res = 0; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return res; +} + +/* delete an entry in the database given a key */ +static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + int ret; + + if (tdb->max_dead_records != 0) { + + /* + * Allow for some dead records per hash chain, mainly for + * tdb's with a very high create/delete rate like locking.tdb. + */ + + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) { + /* + * Don't let the per-chain freelist grow too large, + * delete all existing dead records + */ + tdb_purge_dead(tdb, hash); + } + + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) { + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return -1; + } + + /* + * Just mark the record as dead. + */ + rec.magic = TDB_DEAD_MAGIC; + ret = tdb_rec_write(tdb, rec_ptr, &rec); + } + else { + if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, + &rec))) + return -1; + + ret = tdb_do_delete(tdb, rec_ptr, &rec); + } + + if (ret == 0) { + tdb_increment_seqnum(tdb); + } + + if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n")); + return ret; +} + +int tdb_delete(struct tdb_context *tdb, TDB_DATA key) +{ + u32 hash = tdb->hash_fn(&key); + return tdb_delete_hash(tdb, key, hash); +} + +/* + * See if we have a dead record around with enough space + */ +static tdb_off_t tdb_find_dead(struct tdb_context *tdb, u32 hash, + struct list_struct *r, tdb_len_t length) +{ + tdb_off_t rec_ptr; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, r) == -1) + return 0; + + if (TDB_DEAD(r) && r->rec_len >= length) { + /* + * First fit for simple coding, TODO: change to best + * fit + */ + return rec_ptr; + } + rec_ptr = r->next; + } + return 0; +} + +/* store an element in the database, replacing any existing element + with the same key + + return 0 on success, -1 on failure +*/ +int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) +{ + struct list_struct rec; + u32 hash; + tdb_off_t rec_ptr; + char *p = NULL; + int ret = -1; + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + /* check for it existing, on insert. */ + if (flag == TDB_INSERT) { + if (tdb_exists_hash(tdb, key, hash)) { + tdb->ecode = TDB_ERR_EXISTS; + goto fail; + } + } else { + /* first try in-place update, on modify or replace. */ + if (tdb_update_hash(tdb, key, hash, dbuf) == 0) { + goto done; + } + if (tdb->ecode == TDB_ERR_NOEXIST && + flag == TDB_MODIFY) { + /* if the record doesn't exist and we are in TDB_MODIFY mode then + we should fail the store */ + goto fail; + } + } + /* reset the error code potentially set by the tdb_update() */ + tdb->ecode = TDB_SUCCESS; + + /* delete any existing record - if it doesn't exist we don't + care. Doing this first reduces fragmentation, and avoids + coalescing with `allocated' block before it's updated. */ + if (flag != TDB_INSERT) + tdb_delete_hash(tdb, key, hash); + + /* Copy key+value *before* allocating free space in case malloc + fails and we are left with a dead spot in the tdb. */ + + if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + + memcpy(p, key.dptr, key.dsize); + if (dbuf.dsize) + memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); + + if (tdb->max_dead_records != 0) { + /* + * Allow for some dead records per hash chain, look if we can + * find one that can hold the new record. We need enough space + * for key, data and tailer. If we find one, we don't have to + * consult the central freelist. + */ + rec_ptr = tdb_find_dead( + tdb, hash, &rec, + key.dsize + dbuf.dsize + sizeof(tdb_off_t)); + + if (rec_ptr != 0) { + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 + || tdb->methods->tdb_write( + tdb, rec_ptr + sizeof(rec), + p, key.dsize + dbuf.dsize) == -1) { + goto fail; + } + goto done; + } + } + + /* + * We have to allocate some space from the freelist, so this means we + * have to lock it. Use the chance to purge all the DEAD records from + * the hash chain under the freelist lock. + */ + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + goto fail; + } + + if ((tdb->max_dead_records != 0) + && (tdb_purge_dead(tdb, hash) == -1)) { + tdb_unlock(tdb, -1, F_WRLCK); + goto fail; + } + + /* we have to allocate some space */ + rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec); + + tdb_unlock(tdb, -1, F_WRLCK); + + if (rec_ptr == 0) { + goto fail; + } + + /* Read hash top into next ptr */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) + goto fail; + + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + + /* write out and point the top of the hash chain at it */ + if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 + || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 + || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { + /* Need to tdb_unallocate() here */ + goto fail; + } + + done: + ret = 0; + fail: + if (ret == 0) { + tdb_increment_seqnum(tdb); + } + + SAFE_FREE(p); + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return ret; +} + + +/* Append to an entry. Create if not exist. */ +int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) +{ + u32 hash; + TDB_DATA dbuf; + int ret = -1; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + dbuf = tdb_fetch(tdb, key); + + if (dbuf.dptr == NULL) { + dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize); + } else { + unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr, + dbuf.dsize + new_dbuf.dsize); + if (new_dptr == NULL) { + free(dbuf.dptr); + } + dbuf.dptr = new_dptr; + } + + if (dbuf.dptr == NULL) { + tdb->ecode = TDB_ERR_OOM; + goto failed; + } + + memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize); + dbuf.dsize += new_dbuf.dsize; + + ret = tdb_store(tdb, key, dbuf, 0); + +failed: + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + SAFE_FREE(dbuf.dptr); + return ret; +} + + +/* + return the name of the current tdb file + useful for external logging functions +*/ +const char *tdb_name(struct tdb_context *tdb) +{ + return tdb->name; +} + +/* + return the underlying file descriptor being used by tdb, or -1 + useful for external routines that want to check the device/inode + of the fd +*/ +int tdb_fd(struct tdb_context *tdb) +{ + return tdb->fd; +} + +/* + return the current logging function + useful for external tdb routines that wish to log tdb errors +*/ +tdb_log_func tdb_log_fn(struct tdb_context *tdb) +{ + return tdb->log.log_fn; +} + + +/* + get the tdb sequence number. Only makes sense if the writers opened + with TDB_SEQNUM set. Note that this sequence number will wrap quite + quickly, so it should only be used for a 'has something changed' + test, not for code that relies on the count of the number of changes + made. If you want a counter then use a tdb record. + + The aim of this sequence number is to allow for a very lightweight + test of a possible tdb change. +*/ +int tdb_get_seqnum(struct tdb_context *tdb) +{ + tdb_off_t seqnum=0; + + tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); + return seqnum; +} + +int tdb_hash_size(struct tdb_context *tdb) +{ + return tdb->header.hash_size; +} + +size_t tdb_map_size(struct tdb_context *tdb) +{ + return tdb->map_size; +} + +int tdb_get_flags(struct tdb_context *tdb) +{ + return tdb->flags; +} + + +/* + enable sequence number handling on an open tdb +*/ +void tdb_enable_seqnum(struct tdb_context *tdb) +{ + tdb->flags |= TDB_SEQNUM; +} + +/* file: open.c */ + +/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ +static struct tdb_context *tdbs = NULL; + + +/* This is based on the hash algorithm from gdbm */ +static unsigned int default_tdb_hash(TDB_DATA *key) +{ + u32 value; /* Used to compute the hash value. */ + u32 i; /* Used to cycle through random values. */ + + /* Set the initial value from the key size. */ + for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) + value = (value + (key->dptr[i] << (i*5 % 24))); + + return (1103515243 * value + 12345); +} + + +/* initialise a new database with a specified hash size */ +static int tdb_new_database(struct tdb_context *tdb, int hash_size) +{ + struct tdb_header *newdb; + int size, ret = -1; + + /* We make it up in memory, then write it out if not internal */ + size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t); + if (!(newdb = (struct tdb_header *)calloc(size, 1))) + return TDB_ERRCODE(TDB_ERR_OOM, -1); + + /* Fill in the header */ + newdb->version = TDB_VERSION; + newdb->hash_size = hash_size; + if (tdb->flags & TDB_INTERNAL) { + tdb->map_size = size; + tdb->map_ptr = (char *)newdb; + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Convert the `ondisk' version if asked. */ + CONVERT(*newdb); + return 0; + } + if (lseek(tdb->fd, 0, SEEK_SET) == -1) + goto fail; + + if (ftruncate(tdb->fd, 0) == -1) + goto fail; + + /* This creates an endian-converted header, as if read from disk */ + CONVERT(*newdb); + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Don't endian-convert the magic food! */ + memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); + if (write(tdb->fd, newdb, size) != size) { + ret = -1; + } else { + ret = 0; + } + + fail: + SAFE_FREE(newdb); + return ret; +} + + + +static int tdb_already_open(dev_t device, + ino_t ino) +{ + struct tdb_context *i; + + for (i = tdbs; i; i = i->next) { + if (i->device == device && i->inode == ino) { + return 1; + } + } + + return 0; +} + +/* open the database, creating it if necessary + + The open_flags and mode are passed straight to the open call on the + database file. A flags value of O_WRONLY is invalid. The hash size + is advisory, use zero for a default value. + + Return is NULL on error, in which case errno is also set. Don't + try to call tdb_error or tdb_errname, just do strerror(errno). + + @param name may be NULL for internal databases. */ +struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL); +} + +/* a default logging function */ +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ +} + + +struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + const struct tdb_logging_context *log_ctx, + tdb_hash_func hash_fn) +{ + struct tdb_context *tdb; + struct stat st; + int rev = 0, locked = 0; + unsigned char *vp; + u32 vertest; + + if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) { + /* Can't log this */ + errno = ENOMEM; + goto fail; + } + tdb_io_init(tdb); + tdb->fd = -1; + tdb->name = NULL; + tdb->map_ptr = NULL; + tdb->flags = tdb_flags; + tdb->open_flags = open_flags; + if (log_ctx) { + tdb->log = *log_ctx; + } else { + tdb->log.log_fn = null_log_fn; + tdb->log.log_private = NULL; + } + tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; + + /* cache the page size */ + tdb->page_size = getpagesize(); + if (tdb->page_size <= 0) { + tdb->page_size = 0x2000; + } + + if ((open_flags & O_ACCMODE) == O_WRONLY) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n", + name)); + errno = EINVAL; + goto fail; + } + + if (hash_size == 0) + hash_size = DEFAULT_HASH_SIZE; + if ((open_flags & O_ACCMODE) == O_RDONLY) { + tdb->read_only = 1; + /* read only databases don't do locking or clear if first */ + tdb->flags |= TDB_NOLOCK; + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + } + + /* internal databases don't mmap or lock, and start off cleared */ + if (tdb->flags & TDB_INTERNAL) { + tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + if (tdb_new_database(tdb, hash_size) != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!")); + goto fail; + } + goto internal; + } + + if ((tdb->fd = open(name, open_flags, mode)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: could not open file %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by open(2) */ + } + + /* ensure there is only one process initialising at once */ + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by tdb_brlock */ + } + + /* we need to zero database if we are the only one with it open */ + if ((tdb_flags & TDB_CLEAR_IF_FIRST) && + (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) { + open_flags |= O_CREAT; + if (ftruncate(tdb->fd, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "failed to truncate %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by ftruncate */ + } + } + + if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) + || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 + || (tdb->header.version != TDB_VERSION + && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { + /* its not a valid database - possibly initialise it */ + if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { + errno = EIO; /* ie bad format or something */ + goto fail; + } + rev = (tdb->flags & TDB_CONVERT); + } + vp = (unsigned char *)&tdb->header.version; + vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) | + (((u32)vp[2]) << 8) | (u32)vp[3]; + tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; + if (!rev) + tdb->flags &= ~TDB_CONVERT; + else { + tdb->flags |= TDB_CONVERT; + tdb_convert(&tdb->header, sizeof(tdb->header)); + } + if (fstat(tdb->fd, &st) == -1) + goto fail; + + if (tdb->header.rwlocks != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); + goto fail; + } + + /* Is it already in the open list? If so, fail. */ + if (tdb_already_open(st.st_dev, st.st_ino)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " + "%s (%d,%d) is already open in this process\n", + name, (int)st.st_dev, (int)st.st_ino)); + errno = EBUSY; + goto fail; + } + + if (!(tdb->name = (char *)strdup(name))) { + errno = ENOMEM; + goto fail; + } + + tdb->map_size = st.st_size; + tdb->device = st.st_dev; + tdb->inode = st.st_ino; + tdb->max_dead_records = 0; + tdb_mmap(tdb); + if (locked) { + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " + "failed to take ACTIVE_LOCK on %s: %s\n", + name, strerror(errno))); + goto fail; + } + + } + + /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if + we didn't get the initial exclusive lock as we need to let all other + users know we're using it. */ + + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + /* leave this lock in place to indicate it's in use */ + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1) + goto fail; + } + + /* if needed, run recovery */ + if (tdb_transaction_recover(tdb) == -1) { + goto fail; + } + + internal: + /* Internal (memory-only) databases skip all the code above to + * do with disk files, and resume here by releasing their + * global lock and hooking into the active list. */ + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1) + goto fail; + tdb->next = tdbs; + tdbs = tdb; + return tdb; + + fail: + { int save_errno = errno; + + if (!tdb) + return NULL; + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + if (close(tdb->fd) != 0) + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); + SAFE_FREE(tdb); + errno = save_errno; + return NULL; + } +} + +/* + * Set the maximum number of dead records per hash chain + */ + +void tdb_set_max_dead(struct tdb_context *tdb, int max_dead) +{ + tdb->max_dead_records = max_dead; +} + +/** + * Close a database. + * + * @returns -1 for error; 0 for success. + **/ +int tdb_close(struct tdb_context *tdb) +{ + struct tdb_context **i; + int ret = 0; + + if (tdb->transaction) { + tdb_transaction_cancel(tdb); + } + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + ret = close(tdb->fd); + SAFE_FREE(tdb->lockrecs); + + /* Remove from contexts list */ + for (i = &tdbs; *i; i = &(*i)->next) { + if (*i == tdb) { + *i = tdb->next; + break; + } + } + + memset(tdb, 0, sizeof(*tdb)); + SAFE_FREE(tdb); + + return ret; +} + +/* register a loging function */ +void tdb_set_logging_function(struct tdb_context *tdb, + const struct tdb_logging_context *log_ctx) +{ + tdb->log = *log_ctx; +} + +void *tdb_get_logging_private(struct tdb_context *tdb) +{ + return tdb->log.log_private; +} + +/* reopen a tdb - this can be used after a fork to ensure that we have an independent + seek pointer from our parent and to re-establish locks */ +int tdb_reopen(struct tdb_context *tdb) +{ + struct stat st; + + if (tdb->flags & TDB_INTERNAL) { + return 0; /* Nothing to do. */ + } + + if (tdb->num_locks != 0 || tdb->global_lock.count) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n")); + goto fail; + } + + if (tdb->transaction != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n")); + goto fail; + } + + if (tdb_munmap(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); + goto fail; + } + if (close(tdb->fd) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n")); + tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); + if (tdb->fd == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno))); + goto fail; + } + if ((tdb->flags & TDB_CLEAR_IF_FIRST) && + (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); + goto fail; + } + if (fstat(tdb->fd, &st) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); + goto fail; + } + if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n")); + goto fail; + } + tdb_mmap(tdb); + + return 0; + +fail: + tdb_close(tdb); + return -1; +} + +/* reopen all tdb's */ +int tdb_reopen_all(int parent_longlived) +{ + struct tdb_context *tdb; + + for (tdb=tdbs; tdb; tdb = tdb->next) { + /* + * If the parent is longlived (ie. a + * parent daemon architecture), we know + * it will keep it's active lock on a + * tdb opened with CLEAR_IF_FIRST. Thus + * for child processes we don't have to + * add an active lock. This is essential + * to improve performance on systems that + * keep POSIX locks as a non-scalable data + * structure in the kernel. + */ + if (parent_longlived) { + /* Ensure no clear-if-first. */ + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + } + + if (tdb_reopen(tdb) != 0) + return -1; + } + + return 0; +} diff --git a/lib/libext2fs/orig/tdb.h b/lib/libext2fs/orig/tdb.h new file mode 100644 index 0000000..bfcd943 --- /dev/null +++ b/lib/libext2fs/orig/tdb.h @@ -0,0 +1,215 @@ +#ifndef __TDB_H__ +#define __TDB_H__ + +/* + Unix SMB/CIFS implementation. + + trivial database library + + Copyright (C) Andrew Tridgell 1999-2004 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* flags to tdb_store() */ +#define TDB_REPLACE 1 +#define TDB_INSERT 2 +#define TDB_MODIFY 3 + +/* flags for tdb_open() */ +#define TDB_DEFAULT 0 /* just a readability place holder */ +#define TDB_CLEAR_IF_FIRST 1 +#define TDB_INTERNAL 2 /* don't store on disk */ +#define TDB_NOLOCK 4 /* don't do any locking */ +#define TDB_NOMMAP 8 /* don't use mmap */ +#define TDB_CONVERT 16 /* convert endian (internal use) */ +#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ +#define TDB_NOSYNC 64 /* don't use synchronous transactions */ +#define TDB_SEQNUM 128 /* maintain a sequence number */ + +#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) + +/* error codes */ +enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, + TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, + TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY}; + +/* debugging uses one of the following levels */ +enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, + TDB_DEBUG_WARNING, TDB_DEBUG_TRACE}; + +typedef struct TDB_DATA { + unsigned char *dptr; + size_t dsize; +} TDB_DATA; + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* ext2fs tdb renames */ +#define tdb_open ext2fs_tdb_open +#define tdb_open_ex ext2fs_tdb_open_ex +#define tdb_set_max_dead ext2fs_tdb_set_max_dead +#define tdb_reopen ext2fs_tdb_reopen +#define tdb_reopen_all ext2fs_tdb_reopen_all +#define tdb_set_logging_function ext2fs_tdb_set_logging_function +#define tdb_error ext2fs_tdb_error +#define tdb_errorstr ext2fs_tdb_errorstr +#define tdb_fetch ext2fs_tdb_fetch +#define tdb_parse_record ext2fs_tdb_parse_record +#define tdb_delete ext2fs_tdb_delete +#define tdb_store ext2fs_tdb_store +#define tdb_append ext2fs_tdb_append +#define tdb_close ext2fs_tdb_close +#define tdb_firstkey ext2fs_tdb_firstkey +#define tdb_nextkey ext2fs_tdb_nextkey +#define tdb_traverse ext2fs_tdb_traverse +#define tdb_traverse_read ext2fs_tdb_traverse_read +#define tdb_exists ext2fs_tdb_exists +#define tdb_lockall ext2fs_tdb_lockall +#define tdb_unlockall ext2fs_tdb_unlockall +#define tdb_lockall_read ext2fs_tdb_lockall_read +#define tdb_unlockall_read ext2fs_tdb_unlockall_read +#define tdb_name ext2fs_tdb_name +#define tdb_fd ext2fs_tdb_fd +#define tdb_log_fn ext2fs_tdb_log_fn +#define tdb_get_logging_private ext2fs_tdb_get_logging_private +#define tdb_transaction_start ext2fs_tdb_transaction_start +#define tdb_transaction_commit ext2fs_tdb_transaction_commit +#define tdb_transaction_cancel ext2fs_tdb_transaction_cancel +#define tdb_transaction_recover ext2fs_tdb_transaction_recover +#define tdb_get_seqnum ext2fs_tdb_get_seqnum +#define tdb_hash_size ext2fs_tdb_hash_size +#define tdb_map_size ext2fs_tdb_map_size +#define tdb_get_flags ext2fs_tdb_get_flags +#define tdb_chainlock ext2fs_tdb_chainlock +#define tdb_chainunlock ext2fs_tdb_chainunlock +#define tdb_chainlock_read ext2fs_tdb_chainlock_read +#define tdb_chainunlock_read ext2fs_tdb_chainunlock_read +#define tdb_dump_all ext2fs_tdb_dump_all +#define tdb_printfreelist ext2fs_tdb_printfreelist +#define tdb_validate_freelist ext2fs_tdb_validate_freelist +#define tdb_chainlock_mark ext2fs_tdb_chainlock_mark +#define tdb_chainlock_nonblock ext2fs_tdb_chainlock_nonblock +#define tdb_chainlock_unmark ext2fs_tdb_chainlock_unmark +#define tdb_enable_seqnum ext2fs_tdb_enable_seqnum +#define tdb_increment_seqnum_nonblock ext2fs_tdb_increment_seqnum_nonblock +#define tdb_lock_nonblock ext2fs_tdb_lock_nonblock +#define tdb_lockall_mark ext2fs_tdb_lockall_mark +#define tdb_lockall_nonblock ext2fs_tdb_lockall_nonblock +#define tdb_lockall_read_nonblock ext2fs_tdb_lockall_read_nonblock +#define tdb_lockall_unmark ext2fs_tdb_lockall_unmark + +/* this is the context structure that is returned from a db open */ +typedef struct tdb_context TDB_CONTEXT; + +typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *); +typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_ATTRIBUTE(3, 4); +typedef unsigned int (*tdb_hash_func)(TDB_DATA *key); + +struct tdb_logging_context { + tdb_log_func log_fn; + void *log_private; +}; + +struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode); +struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + const struct tdb_logging_context *log_ctx, + tdb_hash_func hash_fn); +void tdb_set_max_dead(struct tdb_context *tdb, int max_dead); + +int tdb_reopen(struct tdb_context *tdb); +int tdb_reopen_all(int parent_longlived); +void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx); +enum TDB_ERROR tdb_error(struct tdb_context *tdb); +const char *tdb_errorstr(struct tdb_context *tdb); +TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key); +int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data); +int tdb_delete(struct tdb_context *tdb, TDB_DATA key); +int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); +int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf); +int tdb_close(struct tdb_context *tdb); +TDB_DATA tdb_firstkey(struct tdb_context *tdb); +TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key); +int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *); +int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *); +int tdb_exists(struct tdb_context *tdb, TDB_DATA key); +int tdb_lockall(struct tdb_context *tdb); +int tdb_lockall_nonblock(struct tdb_context *tdb); +int tdb_unlockall(struct tdb_context *tdb); +int tdb_lockall_read(struct tdb_context *tdb); +int tdb_lockall_read_nonblock(struct tdb_context *tdb); +int tdb_unlockall_read(struct tdb_context *tdb); +int tdb_lockall_mark(struct tdb_context *tdb); +int tdb_lockall_unmark(struct tdb_context *tdb); +const char *tdb_name(struct tdb_context *tdb); +int tdb_fd(struct tdb_context *tdb); +tdb_log_func tdb_log_fn(struct tdb_context *tdb); +void *tdb_get_logging_private(struct tdb_context *tdb); +int tdb_transaction_start(struct tdb_context *tdb); +int tdb_transaction_commit(struct tdb_context *tdb); +int tdb_transaction_cancel(struct tdb_context *tdb); +int tdb_transaction_recover(struct tdb_context *tdb); +int tdb_get_seqnum(struct tdb_context *tdb); +int tdb_hash_size(struct tdb_context *tdb); +size_t tdb_map_size(struct tdb_context *tdb); +int tdb_get_flags(struct tdb_context *tdb); +void tdb_enable_seqnum(struct tdb_context *tdb); +void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); + +/* Low level locking functions: use with care */ +int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key); + +/* Debug functions. Not used in production. */ +void tdb_dump_all(struct tdb_context *tdb); +int tdb_printfreelist(struct tdb_context *tdb); +int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); + +extern TDB_DATA tdb_null; + +#ifdef __cplusplus +} +#endif + +#endif /* tdb.h */ diff --git a/lib/libext2fs/orig/unlink.c b/lib/libext2fs/orig/unlink.c new file mode 100644 index 0000000..7ffeb9e --- /dev/null +++ b/lib/libext2fs/orig/unlink.c @@ -0,0 +1,99 @@ +/* + * unlink.c --- delete links in a ext2fs directory + * + * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + struct ext2_dir_entry *prev; + int done; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int unlink_proc(struct ext2_dir_entry *dirent, + int offset, + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *prev; + + prev = ls->prev; + ls->prev = dirent; + + if (ls->name) { + if ((dirent->name_len & 0xFF) != ls->namelen) + return 0; + if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) + return 0; + } + if (ls->inode) { + if (dirent->inode != ls->inode) + return 0; + } else { + if (!dirent->inode) + return 0; + } + + if (offset) + prev->rec_len += dirent->rec_len; + else + dirent->inode = 0; + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, + const char *name, ext2_ino_t ino, + int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + struct link_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!name && !ino) + return EXT2_ET_INVALID_ARGUMENT; + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = 0; + ls.done = 0; + ls.prev = 0; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, unlink_proc, &ls); + if (retval) + return retval; + + return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; +} + diff --git a/lib/libext2fs/orig/valid_blk.c b/lib/libext2fs/orig/valid_blk.c new file mode 100644 index 0000000..ec3edd8 --- /dev/null +++ b/lib/libext2fs/orig/valid_blk.c @@ -0,0 +1,55 @@ +/* + * valid_blk.c --- does the inode have valid blocks? + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This function returns 1 if the inode's block entries actually + * contain block entries. + */ +int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) +{ + /* + * Only directories, regular files, and some symbolic links + * have valid block entries. + */ + if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && + !LINUX_S_ISLNK(inode->i_mode)) + return 0; + + /* + * If the symbolic link is a "fast symlink", then the symlink + * target is stored in the block entries. + */ + if (LINUX_S_ISLNK (inode->i_mode)) { + if (ext2fs_file_acl_block(inode) == 0) { + /* With no EA block, we can rely on i_blocks */ + if (inode->i_blocks == 0) + return 0; + } else { + /* With an EA block, life gets more tricky */ + if (inode->i_size >= EXT2_N_BLOCKS*4) + return 1; /* definitely using i_block[] */ + if (inode->i_size > 4 && inode->i_block[1] == 0) + return 1; /* definitely using i_block[] */ + return 0; /* Probably a fast symlink */ + } + } + return 1; +} diff --git a/lib/libext2fs/orig/version.c b/lib/libext2fs/orig/version.c new file mode 100644 index 0000000..e7f223b --- /dev/null +++ b/lib/libext2fs/orig/version.c @@ -0,0 +1,54 @@ +/* + * version.c --- Return the version of the ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +static const char *lib_version = ""; +static const char *lib_date = ""; + +int ext2fs_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0, dot_count = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') { + if (dot_count++) + break; + else + continue; + } + if (!isdigit((int)*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + + +int ext2fs_get_library_version(const char **ver_string, + const char **date_string) +{ + if (ver_string) + *ver_string = lib_version; + if (date_string) + *date_string = lib_date; + + return ext2fs_parse_version_string(lib_version); +} diff --git a/lib/libext2fs/orig/write_bb_file.c b/lib/libext2fs/orig/write_bb_file.c new file mode 100644 index 0000000..70bcf08 --- /dev/null +++ b/lib/libext2fs/orig/write_bb_file.c @@ -0,0 +1,34 @@ +/* + * write_bb_file.c --- write a list of bad blocks to a FILE * + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags EXT2FS_ATTR((unused)), + FILE *f) +{ + badblocks_iterate bb_iter; + blk_t blk; + errcode_t retval; + + retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); + if (retval) + return retval; + + while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { + fprintf(f, "%u\n", blk); + } + ext2fs_badblocks_list_iterate_end(bb_iter); + return 0; +} diff --git a/lib/libext2fs/source/Makefile b/lib/libext2fs/source/Makefile new file mode 100644 index 0000000..9edb946 --- /dev/null +++ b/lib/libext2fs/source/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +ifeq ($(PLATFORM),wii) +include $(DEVKITPPC)/wii_rules +endif + +ifeq ($(PLATFORM),cube) +include $(DEVKITPPC)/gamecube_rules +endif + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +BUILD ?= wii_release +SOURCES := . +INCLUDES := ../include +LIBDIR := ../lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -Os -Wall $(MACHDEP) $(INCLUDE) -DGEKKO \ + -DHAVE_UNISTD_H -DHAVE_SYS_STAT_H -DHAVE_SYS_TYPES_H -DHAVE_UTIME_H -DWORDS_BIGENDIAN \ + -DHAVE_ERRNO_H -DHAVE_STRDUP -DHAVE_SYS_RESOURCE_H +CXXFLAGS = $(CFLAGS) +ASFLAGS := -g +export EXT2BIN := $(LIBDIR)/$(PLATFORM)/libext2fs.a + +ifeq ($(BUILD),cube_debug) +CFLAGS += -DDEBUG_GEKKO +CXXFLAGS += -DDEBUG_GEKKO +endif +ifeq ($(BUILD),wii_debug) +CFLAGS += -DDEBUG_GEKKO +CXXFLAGS += -DDEBUG_GEKKO +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) + +all: $(EXT2BIN) + +install: + cp ../include/ext2.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libext2fs.a $(DEVKITPRO)/libogc/lib/wii + cp ../lib/cube/libext2fs.a $(DEVKITPRO)/libogc/lib/cube + +wii-install: + cp ../include/ext2.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libext2fs.a $(DEVKITPRO)/libogc/lib/wii + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(EXT2BIN): $(OFILES) $(LIBDIR)/$(PLATFORM) + @rm -f "../$(EXT2BIN)" + @$(AR) rcs "../$(EXT2BIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIBDIR)/$(PLATFORM): + mkdir -p ../$(LIBDIR)/$(PLATFORM) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libext2fs/source/alloc.c b/lib/libext2fs/source/alloc.c new file mode 100644 index 0000000..3a8f332 --- /dev/null +++ b/lib/libext2fs/source/alloc.c @@ -0,0 +1,308 @@ +/* + * alloc.c --- allocate new inodes, blocks for ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Check for uninit block bitmaps and deal with them appropriately + */ +static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map, + dgrp_t group) +{ + blk_t i; + blk64_t blk, super_blk, old_desc_blk, new_desc_blk; + int old_desc_blocks; + + if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) + return; + + blk = (group * fs->super->s_blocks_per_group) + + fs->super->s_first_data_block; + + ext2fs_super_and_bgd_loc2(fs, group, &super_blk, + &old_desc_blk, &new_desc_blk, 0); + + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) { + if ((blk == super_blk) || + (old_desc_blk && old_desc_blocks && + (blk >= old_desc_blk) && + (blk < old_desc_blk + old_desc_blocks)) || + (new_desc_blk && (blk == new_desc_blk)) || + (blk == ext2fs_block_bitmap_loc(fs, group)) || + (blk == ext2fs_inode_bitmap_loc(fs, group)) || + (blk >= ext2fs_inode_table_loc(fs, group) && + (blk < ext2fs_inode_table_loc(fs, group) + + fs->inode_blocks_per_group))) + ext2fs_fast_mark_block_bitmap2(map, blk); + else + ext2fs_fast_unmark_block_bitmap2(map, blk); + } + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, group); +} + +/* + * Check for uninit inode bitmaps and deal with them appropriately + */ +static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, + dgrp_t group) +{ + ext2_ino_t i, ino; + + if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) + return; + + ino = (group * fs->super->s_inodes_per_group) + 1; + for (i=0; i < fs->super->s_inodes_per_group; i++, ino++) + ext2fs_fast_unmark_inode_bitmap2(map, ino); + + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + check_block_uninit(fs, fs->block_map, group); +} + +/* + * Right now, just search forward from the parent directory's block + * group to find the next free inode. + * + * Should have a special policy for directories. + */ +errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, + int mode EXT2FS_ATTR((unused)), + ext2fs_inode_bitmap map, ext2_ino_t *ret) +{ + ext2_ino_t dir_group = 0; + ext2_ino_t i; + ext2_ino_t start_inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->inode_map; + if (!map) + return EXT2_ET_NO_INODE_BITMAP; + + if (dir > 0) + dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); + + start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; + if (start_inode < EXT2_FIRST_INODE(fs->super)) + start_inode = EXT2_FIRST_INODE(fs->super); + if (start_inode > fs->super->s_inodes_count) + return EXT2_ET_INODE_ALLOC_FAIL; + i = start_inode; + + do { + if (((i - 1) % EXT2_INODES_PER_GROUP(fs->super)) == 0) + check_inode_uninit(fs, map, (i - 1) / + EXT2_INODES_PER_GROUP(fs->super)); + + if (!ext2fs_fast_test_inode_bitmap2(map, i)) + break; + i++; + if (i > fs->super->s_inodes_count) + i = EXT2_FIRST_INODE(fs->super); + } while (i != start_inode); + + if (ext2fs_test_inode_bitmap2(map, i)) + return EXT2_ET_INODE_ALLOC_FAIL; + *ret = i; + return 0; +} + +/* + * Stupid algorithm --- we now just search forward starting from the + * goal. Should put in a smarter one someday.... + */ +errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret) +{ + blk64_t i; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!goal || (goal >= ext2fs_blocks_count(fs->super))) + goal = fs->super->s_first_data_block; + i = goal; + check_block_uninit(fs, map, + (i - fs->super->s_first_data_block) / + EXT2_BLOCKS_PER_GROUP(fs->super)); + do { + if (((i - fs->super->s_first_data_block) % + EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) + check_block_uninit(fs, map, + (i - fs->super->s_first_data_block) / + EXT2_BLOCKS_PER_GROUP(fs->super)); + + if (!ext2fs_fast_test_block_bitmap2(map, i)) { + *ret = i; + return 0; + } + i++; + if (i >= ext2fs_blocks_count(fs->super)) + i = fs->super->s_first_data_block; + } while (i != goal); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} + +errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_new_block2(fs, goal, map, &val); + if (!retval) + *ret = (blk_t) val; + return retval; +} + +/* + * This function zeros out the allocated block, and updates all of the + * appropriate filesystem records. + */ +errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, + char *block_buf, blk64_t *ret) +{ + errcode_t retval; + blk64_t block; + char *buf = 0; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + memset(block_buf, 0, fs->blocksize); + + if (fs->get_alloc_block) { + retval = (fs->get_alloc_block)(fs, goal, &block); + if (retval) + goto fail; + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + goto fail; + } + + retval = ext2fs_new_block2(fs, goal, 0, &block); + if (retval) + goto fail; + } + + retval = io_channel_write_blk64(fs->io, block, 1, block_buf); + if (retval) + goto fail; + + ext2fs_block_alloc_stats2(fs, block, +1); + *ret = block; + +fail: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_alloc_block2(fs, goal, block_buf, &val); + if (!retval) + *ret = (blk_t) val; + return retval; +} + +errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish, + int num, ext2fs_block_bitmap map, blk64_t *ret) +{ + blk64_t b = start; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!b) + b = fs->super->s_first_data_block; + if (!finish) + finish = start; + if (!num) + num = 1; + do { + if (b+num-1 > ext2fs_blocks_count(fs->super)) + b = fs->super->s_first_data_block; + if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) { + *ret = b; + return 0; + } + b++; + } while (b != finish); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} + +errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, + int num, ext2fs_block_bitmap map, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_get_free_blocks2(fs, start, finish, num, map, &val); + if(!retval) + *ret = (blk_t) val; + return retval; +} + +void ext2fs_set_alloc_block_callback(ext2_filsys fs, + errcode_t (*func)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret), + errcode_t (**old)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret)) +{ + if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) + return; + + if (old) + *old = fs->get_alloc_block; + + fs->get_alloc_block = func; +} diff --git a/lib/libext2fs/source/alloc_sb.c b/lib/libext2fs/source/alloc_sb.c new file mode 100644 index 0000000..d5fca3b --- /dev/null +++ b/lib/libext2fs/source/alloc_sb.c @@ -0,0 +1,86 @@ +/* + * alloc_sb.c --- Allocate the superblock and block group descriptors for a + * newly initialized filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This function reserves the superblock and block group descriptors + * for a given block group. It currently returns the number of free + * blocks assuming that inode table and allocation bitmaps will be in + * the group. This is not necessarily the case when the flex_bg + * feature is enabled, so callers should take care! It was only + * really intended for use by mke2fs, and even there it's not that + * useful. In the future, when we redo this function for 64-bit block + * numbers, we should probably return the number of blocks used by the + * super block and group descriptors instead. + * + * See also the comment for ext2fs_super_and_bgd_loc() + */ +int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap) +{ + blk64_t super_blk, old_desc_blk, new_desc_blk; + blk_t used_blks; + int j, old_desc_blocks, num_blocks; + + ext2fs_super_and_bgd_loc2(fs, group, &super_blk, + &old_desc_blk, &new_desc_blk, &used_blks); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + if (super_blk || (group == 0)) + ext2fs_mark_block_bitmap2(bmap, super_blk); + + if (old_desc_blk) { + if (fs->super->s_reserved_gdt_blocks && fs->block_map == bmap) + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + for (j=0; j < old_desc_blocks; j++) + if (old_desc_blk + j < ext2fs_blocks_count(fs->super)) + ext2fs_mark_block_bitmap2(bmap, + old_desc_blk + j); + } + if (new_desc_blk) + ext2fs_mark_block_bitmap2(bmap, new_desc_blk); + + if (group == fs->group_desc_count-1) { + num_blocks = (ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block) % + fs->super->s_blocks_per_group; + if (!num_blocks) + num_blocks = fs->super->s_blocks_per_group; + } else + num_blocks = fs->super->s_blocks_per_group; + + num_blocks -= 2 + fs->inode_blocks_per_group + used_blks; + + return num_blocks ; +} diff --git a/lib/libext2fs/source/alloc_stats.c b/lib/libext2fs/source/alloc_stats.c new file mode 100644 index 0000000..0f27665 --- /dev/null +++ b/lib/libext2fs/source/alloc_stats.c @@ -0,0 +1,106 @@ +/* + * alloc_stats.c --- Update allocation statistics for ext2fs + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir) +{ + int group = ext2fs_group_of_ino(fs, ino); + +#ifndef OMIT_COM_ERR + if (ino > fs->super->s_inodes_count) { + com_err("ext2fs_inode_alloc_stats2", 0, + "Illegal inode number: %lu", (unsigned long) ino); + return; + } +#endif + if (inuse > 0) + ext2fs_mark_inode_bitmap2(fs->inode_map, ino); + else + ext2fs_unmark_inode_bitmap2(fs->inode_map, ino); + ext2fs_bg_free_inodes_count_set(fs, group, ext2fs_bg_free_inodes_count(fs, group) - inuse); + if (isdir) + ext2fs_bg_used_dirs_count_set(fs, group, ext2fs_bg_used_dirs_count(fs, group) + inuse); + + /* We don't strictly need to be clearing the uninit flag if inuse < 0 + * (i.e. freeing inodes) but it also means something is bad. */ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group - + ext2fs_bg_itable_unused(fs, group) + + group * fs->super->s_inodes_per_group + 1; + + if (ino >= first_unused_inode) + ext2fs_bg_itable_unused_set(fs, group, group * fs->super->s_inodes_per_group + fs->super->s_inodes_per_group - ino); + ext2fs_group_desc_csum_set(fs, group); + } + + fs->super->s_free_inodes_count -= inuse; + ext2fs_mark_super_dirty(fs); + ext2fs_mark_ib_dirty(fs); +} + +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) +{ + ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); +} + +void ext2fs_block_alloc_stats2(ext2_filsys fs, blk64_t blk, int inuse) +{ + int group = ext2fs_group_of_blk2(fs, blk); + +#ifndef OMIT_COM_ERR + if (blk >= ext2fs_blocks_count(fs->super)) { + com_err("ext2fs_block_alloc_stats", 0, + "Illegal block number: %lu", (unsigned long) blk); + return; + } +#endif + if (inuse > 0) + ext2fs_mark_block_bitmap2(fs->block_map, blk); + else + ext2fs_unmark_block_bitmap2(fs->block_map, blk); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) - inuse); + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, group); + + ext2fs_free_blocks_count_add(fs->super, -inuse); + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); + if (fs->block_alloc_stats) + (fs->block_alloc_stats)(fs, (blk64_t) blk, inuse); +} + +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) +{ + ext2fs_block_alloc_stats2(fs, blk, inuse); +} + +void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, + void (*func)(ext2_filsys fs, + blk64_t blk, + int inuse), + void (**old)(ext2_filsys fs, + blk64_t blk, + int inuse)) +{ + if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) + return; + if (old) + *old = fs->block_alloc_stats; + + fs->block_alloc_stats = func; +} diff --git a/lib/libext2fs/source/alloc_tables.c b/lib/libext2fs/source/alloc_tables.c new file mode 100644 index 0000000..1c4532b --- /dev/null +++ b/lib/libext2fs/source/alloc_tables.c @@ -0,0 +1,239 @@ +/* + * alloc_tables.c --- Allocate tables for a newly initialized + * filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +/* + * This routine searches for free blocks that can allocate a full + * group of bitmaps or inode tables for a flexbg group. Returns the + * block number with a correct offset were the bitmaps and inode + * tables can be allocated continously and in order. + */ +static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk, + ext2fs_block_bitmap bmap, int offset, int size, + int elem_size) +{ + int flexbg, flexbg_size; + blk64_t last_blk, first_free = 0; + dgrp_t last_grp; + + flexbg_size = 1 << fs->super->s_log_groups_per_flex; + flexbg = group / flexbg_size; + + if (size > (int) (fs->super->s_blocks_per_group / 8)) + size = (int) fs->super->s_blocks_per_group / 8; + + if (offset) + offset -= 1; + + /* + * Don't do a long search if the previous block + * search is still valid. + */ + if (start_blk && group % flexbg_size) { + if (ext2fs_test_block_bitmap_range2(bmap, start_blk + elem_size, + size)) + return start_blk + elem_size; + } + + start_blk = ext2fs_group_first_block2(fs, flexbg_size * flexbg); + last_grp = group | (flexbg_size - 1); + if (last_grp > fs->group_desc_count) + last_grp = fs->group_desc_count; + last_blk = ext2fs_group_last_block2(fs, last_grp); + + /* Find the first available block */ + if (ext2fs_get_free_blocks2(fs, start_blk, last_blk, 1, bmap, + &first_free)) + return first_free; + + if (ext2fs_get_free_blocks2(fs, first_free + offset, last_blk, size, + bmap, &first_free)) + return first_free; + + return first_free; +} + +errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap) +{ + errcode_t retval; + blk64_t group_blk, start_blk, last_blk, new_blk, blk; + dgrp_t last_grp = 0; + int j, rem_grps = 0, flexbg_size = 0; + + group_blk = ext2fs_group_first_block2(fs, group); + last_blk = ext2fs_group_last_block2(fs, group); + + if (!bmap) + bmap = fs->block_map; + + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_FLEX_BG) && + fs->super->s_log_groups_per_flex) { + flexbg_size = 1 << fs->super->s_log_groups_per_flex; + last_grp = group | (flexbg_size - 1); + rem_grps = last_grp - group; + if (last_grp > fs->group_desc_count) + last_grp = fs->group_desc_count; + } + + /* + * Allocate the block and inode bitmaps, if necessary + */ + if (fs->stride) { + retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk, + 1, bmap, &start_blk); + if (retval) + return retval; + start_blk += fs->inode_blocks_per_group; + start_blk += ((fs->stride * group) % + (last_blk - start_blk + 1)); + if (start_blk >= last_blk) + start_blk = group_blk; + } else + start_blk = group_blk; + + if (flexbg_size) { + blk64_t prev_block = 0; + + if (group && ext2fs_block_bitmap_loc(fs, group - 1)) + prev_block = ext2fs_block_bitmap_loc(fs, group - 1); + start_blk = flexbg_offset(fs, group, prev_block, bmap, + 0, rem_grps, 1); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_block_bitmap_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks2(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap2(bmap, new_blk); + ext2fs_block_bitmap_loc_set(fs, group, new_blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + + if (flexbg_size) { + blk64_t prev_block = 0; + if (group && ext2fs_inode_bitmap_loc(fs, group - 1)) + prev_block = ext2fs_inode_bitmap_loc(fs, group - 1); + start_blk = flexbg_offset(fs, group, prev_block, bmap, + flexbg_size, rem_grps, 1); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_inode_bitmap_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks2(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap2(bmap, new_blk); + ext2fs_inode_bitmap_loc_set(fs, group, new_blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + + /* + * Allocate the inode table + */ + if (flexbg_size) { + blk64_t prev_block = 0; + if (group && ext2fs_inode_table_loc(fs, group - 1)) + prev_block = ext2fs_inode_table_loc(fs, group - 1); + if (last_grp == fs->group_desc_count) + rem_grps = last_grp - group; + group_blk = flexbg_offset(fs, group, prev_block, bmap, + flexbg_size * 2, + fs->inode_blocks_per_group * + rem_grps, + fs->inode_blocks_per_group); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_inode_table_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk, + fs->inode_blocks_per_group, + bmap, &new_blk); + if (retval) + return retval; + for (j=0, blk = new_blk; + j < fs->inode_blocks_per_group; + j++, blk++) { + ext2fs_mark_block_bitmap2(bmap, blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, + EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + ext2fs_inode_table_loc_set(fs, group, new_blk); + } + ext2fs_group_desc_csum_set(fs, group); + return 0; +} + +errcode_t ext2fs_allocate_tables(ext2_filsys fs) +{ + errcode_t retval; + dgrp_t i; + struct ext2fs_numeric_progress_struct progress; + + ext2fs_numeric_progress_init(fs, &progress, NULL, + fs->group_desc_count); + + for (i = 0; i < fs->group_desc_count; i++) { + ext2fs_numeric_progress_update(fs, &progress, i); + retval = ext2fs_allocate_group_table(fs, i, fs->block_map); + if (retval) + return retval; + } + ext2fs_numeric_progress_close(fs, &progress, NULL); + return 0; +} + diff --git a/lib/libext2fs/source/badblocks.c b/lib/libext2fs/source/badblocks.c new file mode 100644 index 0000000..5eb28b7 --- /dev/null +++ b/lib/libext2fs/source/badblocks.c @@ -0,0 +1,327 @@ +/* + * badblocks.c --- routines to manipulate the bad block structure + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Helper function for making a badblocks list + */ +static errcode_t make_u32_list(int size, int num, __u32 *list, + ext2_u32_list *ret) +{ + ext2_u32_list bb; + errcode_t retval; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); + if (retval) + return retval; + memset(bb, 0, sizeof(struct ext2_struct_u32_list)); + bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; + bb->size = size ? size : 10; + bb->num = num; + retval = ext2fs_get_array(bb->size, sizeof(blk_t), &bb->list); + if (retval) { + ext2fs_free_mem(&bb); + return retval; + } + if (list) + memcpy(bb->list, list, bb->size * sizeof(blk_t)); + else + memset(bb->list, 0, bb->size * sizeof(blk_t)); + *ret = bb; + return 0; +} + + +/* + * This procedure creates an empty u32 list. + */ +errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) +{ + return make_u32_list(size, 0, 0, ret); +} + +/* + * This procedure creates an empty badblocks list. + */ +errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) +{ + return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); +} + + +/* + * This procedure copies a badblocks list + */ +errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) +{ + errcode_t retval; + + retval = make_u32_list(src->size, src->num, src->list, dest); + if (retval) + return retval; + (*dest)->badblocks_flags = src->badblocks_flags; + return 0; +} + +errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest) +{ + return ext2fs_u32_copy((ext2_u32_list) src, + (ext2_u32_list *) dest); +} + +/* + * This procedure frees a badblocks list. + * + * (note: moved to closefs.c) + */ + + +/* + * This procedure adds a block to a badblocks list. + */ +errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) +{ + errcode_t retval; + int i, j; + unsigned long old_size; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb->num >= bb->size) { + old_size = bb->size * sizeof(__u32); + bb->size += 100; + retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), + &bb->list); + if (retval) { + bb->size -= 100; + return retval; + } + } + + /* + * Add special case code for appending to the end of the list + */ + i = bb->num-1; + if ((bb->num != 0) && (bb->list[i] == blk)) + return 0; + if ((bb->num == 0) || (bb->list[i] < blk)) { + bb->list[bb->num++] = blk; + return 0; + } + + j = bb->num; + for (i=0; i < bb->num; i++) { + if (bb->list[i] == blk) + return 0; + if (bb->list[i] > blk) { + j = i; + break; + } + } + for (i=bb->num; i > j; i--) + bb->list[i] = bb->list[i-1]; + bb->list[j] = blk; + bb->num++; + return 0; +} + +errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); +} + +/* + * This procedure finds a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) +{ + int low, high, mid; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return -1; + + if (bb->num == 0) + return -1; + + low = 0; + high = bb->num-1; + if (blk == bb->list[low]) + return low; + if (blk == bb->list[high]) + return high; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (blk == bb->list[mid]) + return mid; + if (blk < bb->list[mid]) + high = mid; + else + low = mid; + } + return -1; +} + +/* + * This procedure tests to see if a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) +{ + if (ext2fs_u32_list_find(bb, blk) < 0) + return 0; + else + return 1; +} + +int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); +} + + +/* + * Remove a block from the badblock list + */ +int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) +{ + int remloc, i; + + if (bb->num == 0) + return -1; + + remloc = ext2fs_u32_list_find(bb, blk); + if (remloc < 0) + return -1; + + for (i = remloc ; i < bb->num-1; i++) + bb->list[i] = bb->list[i+1]; + bb->num--; + return 0; +} + +void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) +{ + ext2fs_u32_list_del(bb, blk); +} + +errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret) +{ + ext2_u32_iterate iter; + errcode_t retval; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); + if (retval) + return retval; + + iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; + iter->bb = bb; + iter->ptr = 0; + *ret = iter; + return 0; +} + +errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret) +{ + return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, + (ext2_u32_iterate *) ret); +} + + +int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) +{ + ext2_u32_list bb; + + if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) + return 0; + + bb = iter->bb; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return 0; + + if (iter->ptr < bb->num) { + *blk = bb->list[iter->ptr++]; + return 1; + } + *blk = 0; + return 0; +} + +int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, + (__u32 *) blk); +} + + +void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) +{ + if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) + return; + + iter->bb = 0; + ext2fs_free_mem(&iter); +} + +void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) +{ + ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); +} + + +int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) +{ + EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); + EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb1->num != bb2->num) + return 0; + + if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) + return 0; + return 1; +} + +int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) +{ + return ext2fs_u32_list_equal((ext2_u32_list) bb1, + (ext2_u32_list) bb2); +} + +int ext2fs_u32_list_count(ext2_u32_list bb) +{ + return bb->num; +} diff --git a/lib/libext2fs/source/bb_compat.c b/lib/libext2fs/source/bb_compat.c new file mode 100644 index 0000000..a94e3e4 --- /dev/null +++ b/lib/libext2fs/source/bb_compat.c @@ -0,0 +1,63 @@ +/* + * bb_compat.c --- compatibility badblocks routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t badblocks_list_create(badblocks_list *ret, int size) +{ + return ext2fs_badblocks_list_create(ret, size); +} + +void badblocks_list_free(badblocks_list bb) +{ + ext2fs_badblocks_list_free(bb); +} + +errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_add(bb, blk); +} + +int badblocks_list_test(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_test(bb, blk); +} + +errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret) +{ + return ext2fs_badblocks_list_iterate_begin(bb, ret); +} + +int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_badblocks_list_iterate(iter, blk); +} + +void badblocks_list_iterate_end(badblocks_iterate iter) +{ + ext2fs_badblocks_list_iterate_end(iter); +} diff --git a/lib/libext2fs/source/bb_inode.c b/lib/libext2fs/source/bb_inode.c new file mode 100644 index 0000000..0b6c3dd --- /dev/null +++ b/lib/libext2fs/source/bb_inode.c @@ -0,0 +1,266 @@ +/* + * bb_inode.c --- routines to update the bad block inode. + * + * WARNING: This routine modifies a lot of state in the filesystem; if + * this routine returns an error, the bad block inode may be in an + * inconsistent state. + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct set_badblock_record { + ext2_badblocks_iterate bb_iter; + int bad_block_count; + blk_t *ind_blocks; + int max_ind_blocks; + int ind_blocks_size; + int ind_blocks_ptr; + char *block_buf; + errcode_t err; +}; + +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); + +/* + * Given a bad blocks bitmap, update the bad blocks inode to reflect + * the map. + */ +errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) +{ + errcode_t retval; + struct set_badblock_record rec; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + memset(&rec, 0, sizeof(rec)); + rec.max_ind_blocks = 10; + retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t), + &rec.ind_blocks); + if (retval) + return retval; + memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); + retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); + if (retval) + goto cleanup; + memset(rec.block_buf, 0, fs->blocksize); + rec.err = 0; + + /* + * First clear the old bad blocks (while saving the indirect blocks) + */ + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_DEPTH_TRAVERSE, 0, + clear_bad_block_proc, &rec); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + + /* + * Now set the bad blocks! + * + * First, mark the bad blocks as used. This prevents a bad + * block from being used as an indirecto block for the bad + * block inode (!). + */ + if (bb_list) { + retval = ext2fs_badblocks_list_iterate_begin(bb_list, + &rec.bb_iter); + if (retval) + goto cleanup; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_APPEND, 0, + set_bad_block_proc, &rec); + ext2fs_badblocks_list_iterate_end(rec.bb_iter); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + } + + /* + * Update the bad block inode's mod time and block count + * field. + */ + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + if (!inode.i_ctime) + inode.i_ctime = fs->now ? fs->now : time(0); + ext2fs_iblk_set(fs, &inode, rec.bad_block_count); + inode.i_size = rec.bad_block_count * fs->blocksize; + + retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + +cleanup: + ext2fs_free_mem(&rec.ind_blocks); + ext2fs_free_mem(&rec.block_buf); + return retval; +} + +/* + * Helper function for update_bb_inode() + * + * Clear the bad blocks in the bad block inode, while saving the + * indirect blocks. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + unsigned long old_size; + + if (!*block_nr) + return 0; + + /* + * If the block number is outrageous, clear it and ignore it. + */ + if (*block_nr >= ext2fs_blocks_count(fs->super) || + *block_nr < fs->super->s_first_data_block) { + *block_nr = 0; + return BLOCK_CHANGED; + } + + if (blockcnt < 0) { + if (rec->ind_blocks_size >= rec->max_ind_blocks) { + old_size = rec->max_ind_blocks * sizeof(blk_t); + rec->max_ind_blocks += 10; + retval = ext2fs_resize_mem(old_size, + rec->max_ind_blocks * sizeof(blk_t), + &rec->ind_blocks); + if (retval) { + rec->max_ind_blocks -= 10; + rec->err = retval; + return BLOCK_ABORT; + } + } + rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; + } + + /* + * Mark the block as unused, and update accounting information + */ + ext2fs_block_alloc_stats2(fs, *block_nr, -1); + + *block_nr = 0; + return BLOCK_CHANGED; +} + + +/* + * Helper function for update_bb_inode() + * + * Set the block list in the bad block inode, using the supplied bitmap. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + blk_t blk; + + if (blockcnt >= 0) { + /* + * Get the next bad block. + */ + if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) + return BLOCK_ABORT; + rec->bad_block_count++; + } else { + /* + * An indirect block; fetch a block from the + * previously used indirect block list. The block + * most be not marked as used; if so, get another one. + * If we run out of reserved indirect blocks, allocate + * a new one. + */ + retry: + if (rec->ind_blocks_ptr < rec->ind_blocks_size) { + blk = rec->ind_blocks[rec->ind_blocks_ptr++]; + if (ext2fs_test_block_bitmap2(fs->block_map, blk)) + goto retry; + } else { + retval = ext2fs_new_block(fs, 0, 0, &blk); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + retval = io_channel_write_blk64(fs->io, blk, 1, rec->block_buf); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + + /* + * Update block counts + */ + ext2fs_block_alloc_stats2(fs, blk, +1); + + *block_nr = blk; + return BLOCK_CHANGED; +} + + + + + + diff --git a/lib/libext2fs/source/bit_ops.h b/lib/libext2fs/source/bit_ops.h new file mode 100644 index 0000000..762be0b --- /dev/null +++ b/lib/libext2fs/source/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/lib/libext2fs/source/bitmaps.c b/lib/libext2fs/source/bitmaps.c new file mode 100644 index 0000000..c53d61e --- /dev/null +++ b/lib/libext2fs/source/bitmaps.c @@ -0,0 +1,256 @@ +/* + * bitmaps.c --- routines to read, write, and manipulate the inode and + * block bitmaps. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + ext2fs_free_generic_bmap(bitmap); +} + +void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) +{ + ext2fs_free_generic_bmap(bitmap); +} + +errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + return (ext2fs_copy_generic_bmap(src, dest)); +} +void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) +{ + ext2fs_set_generic_bmap_padding(map); +} + +errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret) +{ + __u64 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = 1; + end = fs->super->s_inodes_count; + real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); + + /* Are we permitted to use new-style bitmaps? */ + if (fs->flags & EXT2_FLAG_64BITS) + return (ext2fs_alloc_generic_bmap(fs, + EXT2_ET_MAGIC_INODE_BITMAP64, + EXT2FS_BMAP64_BITARRAY, + start, end, real_end, descr, ret)); + + /* Otherwise, check to see if the file system is small enough + * to use old-style 32-bit bitmaps */ + if ((end > ~0U) || (real_end > ~0U)) + return EXT2_ET_CANT_USE_LEGACY_BITMAPS; + + return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, fs, + start, end, real_end, + descr, 0, + (ext2fs_generic_bitmap *) ret)); +} + +errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret) +{ + __u64 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = fs->super->s_first_data_block; + end = ext2fs_blocks_count(fs->super)-1; + real_end = ((__u64) EXT2_BLOCKS_PER_GROUP(fs->super) + * (__u64) fs->group_desc_count)-1 + start; + + if (fs->flags & EXT2_FLAG_64BITS) + return (ext2fs_alloc_generic_bmap(fs, + EXT2_ET_MAGIC_BLOCK_BITMAP64, + EXT2FS_BMAP64_BITARRAY, + start, end, real_end, descr, ret)); + + if ((end > ~0U) || (real_end > ~0U)) + return EXT2_ET_CANT_USE_LEGACY_BITMAPS; + + return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, fs, + start, end, real_end, + descr, 0, + (ext2fs_generic_bitmap *) ret)); +} + +errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend) +{ + __u64 tmp_oend; + int retval; + + retval = ext2fs_fudge_generic_bmap_end((ext2fs_generic_bitmap) bitmap, + EXT2_ET_FUDGE_INODE_BITMAP_END, + end, &tmp_oend); + if (oend) + *oend = tmp_oend; + return retval; +} + +errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend) +{ + return (ext2fs_fudge_generic_bitmap_end(bitmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + EXT2_ET_FUDGE_BLOCK_BITMAP_END, + end, oend)); +} + +errcode_t ext2fs_fudge_block_bitmap_end2(ext2fs_block_bitmap bitmap, + blk64_t end, blk64_t *oend) +{ + return (ext2fs_fudge_generic_bmap_end(bitmap, + EXT2_ET_FUDGE_BLOCK_BITMAP_END, + end, oend)); +} + +void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + ext2fs_clear_generic_bmap(bitmap); +} + +void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) +{ + ext2fs_clear_generic_bmap(bitmap); +} + +errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap) +{ + return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, + new_end, new_real_end, bmap)); +} + +errcode_t ext2fs_resize_inode_bitmap2(__u64 new_end, __u64 new_real_end, + ext2fs_inode_bitmap bmap) +{ + return (ext2fs_resize_generic_bmap(bmap, new_end, new_real_end)); +} + +errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap) +{ + return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, + new_end, new_real_end, bmap)); +} + +errcode_t ext2fs_resize_block_bitmap2(__u64 new_end, __u64 new_real_end, + ext2fs_block_bitmap bmap) +{ + return (ext2fs_resize_generic_bmap(bmap, new_end, new_real_end)); +} + +errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2) +{ + return (ext2fs_compare_generic_bmap(EXT2_ET_NEQ_BLOCK_BITMAP, + bm1, bm2)); +} + +errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2) +{ + return (ext2fs_compare_generic_bmap(EXT2_ET_NEQ_INODE_BITMAP, + bm1, bm2)); +} + +errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *in) +{ + return (ext2fs_set_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_INODE_BITMAP, + start, num, in)); +} + +errcode_t ext2fs_set_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *in) +{ + return (ext2fs_set_generic_bmap_range(bmap, start, num, in)); +} + +errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *out) +{ + return (ext2fs_get_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_INODE_BITMAP, + start, num, out)); +} + +errcode_t ext2fs_get_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *out) +{ + return (ext2fs_get_generic_bmap_range(bmap, start, num, out)); +} + +errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *in) +{ + return (ext2fs_set_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + start, num, in)); +} + +errcode_t ext2fs_set_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *in) +{ + return (ext2fs_set_generic_bmap_range(bmap, start, num, in)); +} + +errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *out) +{ + return (ext2fs_get_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + start, num, out)); +} + +errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *out) +{ + return (ext2fs_get_generic_bmap_range(bmap, start, num, out)); +} diff --git a/lib/libext2fs/source/bitops.c b/lib/libext2fs/source/bitops.c new file mode 100644 index 0000000..a3f72c3 --- /dev/null +++ b/lib/libext2fs/source/bitops.c @@ -0,0 +1,117 @@ +/* + * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined + * routines. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef _EXT2_HAVE_ASM_BITOPS_ + +/* + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. You should + * recode these in the native assmebly language, if at all possible. + * + * C language equivalents written by Theodore Ts'o, 9/26/92. + * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian + * systems, as well as non-32 bit systems. + */ + +int ext2fs_set_bit(unsigned int nr,void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR |= mask; + return retval; +} + +int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR &= ~mask; + return retval; +} + +int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + int mask; + const unsigned char *ADDR = (const unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + return (mask & *ADDR); +} + +#endif /* !_EXT2_HAVE_ASM_BITOPS_ */ + +void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description) +{ +#ifndef OMIT_COM_ERR + if (description) + com_err(0, errcode, "#%lu for %s", arg, description); + else + com_err(0, errcode, "#%lu", arg); +#endif +} + +/* + * C-only 64 bit ops. + */ + +int ext2fs_set_bit64(__u64 nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR |= mask; + return retval; +} + +int ext2fs_clear_bit64(__u64 nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR &= ~mask; + return retval; +} + +int ext2fs_test_bit64(__u64 nr, const void * addr) +{ + int mask; + const unsigned char *ADDR = (const unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + return (mask & *ADDR); +} + diff --git a/lib/libext2fs/source/bitops.h b/lib/libext2fs/source/bitops.h new file mode 100644 index 0000000..bf6ee82 --- /dev/null +++ b/lib/libext2fs/source/bitops.h @@ -0,0 +1,638 @@ +/* + * bitops.h --- Bitmap frobbing code. The byte swapping routines are + * also included here. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ +#ifndef _BITOPS_H_ +#define _BITOPS_H_ + +extern int ext2fs_set_bit(unsigned int nr,void * addr); +extern int ext2fs_clear_bit(unsigned int nr, void * addr); +extern int ext2fs_test_bit(unsigned int nr, const void * addr); +extern void ext2fs_fast_set_bit(unsigned int nr,void * addr); +extern void ext2fs_fast_clear_bit(unsigned int nr, void * addr); +extern int ext2fs_set_bit64(__u64 nr,void * addr); +extern int ext2fs_clear_bit64(__u64 nr, void * addr); +extern int ext2fs_test_bit64(__u64 nr, const void * addr); +extern void ext2fs_fast_set_bit64(__u64 nr,void * addr); +extern void ext2fs_fast_clear_bit64(__u64 nr, void * addr); +extern __u16 ext2fs_swab16(__u16 val); +extern __u32 ext2fs_swab32(__u32 val); +extern __u64 ext2fs_swab64(__u64 val); + +#ifdef WORDS_BIGENDIAN +#define ext2fs_cpu_to_le64(x) ext2fs_swab64((x)) +#define ext2fs_le64_to_cpu(x) ext2fs_swab64((x)) +#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) +#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) +#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) +#define ext2fs_cpu_to_be32(x) ((__u32)(x)) +#define ext2fs_be32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_be16(x) ((__u16)(x)) +#define ext2fs_be16_to_cpu(x) ((__u16)(x)) +#else +#define ext2fs_cpu_to_le64(x) ((__u64)(x)) +#define ext2fs_le64_to_cpu(x) ((__u64)(x)) +#define ext2fs_cpu_to_le32(x) ((__u32)(x)) +#define ext2fs_le32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_le16(x) ((__u16)(x)) +#define ext2fs_le16_to_cpu(x) ((__u16)(x)) +#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) +#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) +#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) +#endif + +/* + * EXT2FS bitmap manipulation routines. + */ + +/* Support for sending warning messages from the inline subroutines */ +extern const char *ext2fs_block_string; +extern const char *ext2fs_inode_string; +extern const char *ext2fs_mark_string; +extern const char *ext2fs_unmark_string; +extern const char *ext2fs_test_string; +extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description); +extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg); + +extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); +extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); + +extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); +extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); + +extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); + +extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); +extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); + +extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, + ino_t inode, int num); +extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); + +/* These routines moved to gen_bitmap.c (actually, some of the above, too) */ +extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno); +extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); +extern int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap); +extern __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap); + +/* 64-bit versions */ + +extern int ext2fs_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); + +extern int ext2fs_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); + +extern void ext2fs_fast_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern void ext2fs_fast_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_fast_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); + +extern void ext2fs_fast_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern void ext2fs_fast_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_start2(ext2fs_inode_bitmap bitmap); +extern blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_end2(ext2fs_inode_bitmap bitmap); + +extern int ext2fs_fast_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +extern void ext2fs_fast_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +extern void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +/* These routines moved to gen_bitmap64.c */ +extern void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap); +extern errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +extern void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap); +extern int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern __u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap); +extern __u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap); +extern int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); + +/* + * The inline routines themselves... + * + * If NO_INLINE_FUNCS is defined, then we won't try to do inline + * functions at all; they will be included as normal functions in + * inline.c + */ +#ifdef NO_INLINE_FUNCS +#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__mc68000__))) + /* This prevents bitops.c from trying to include the C */ + /* function version of these functions */ +#define _EXT2_HAVE_ASM_BITOPS_ +#endif +#endif /* NO_INLINE_FUNCS */ + +#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef INCLUDE_INLINE_FUNCS +#define _INLINE_ extern +#else +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif +#endif + +/* + * Fast bit set/clear functions that doesn't need to return the + * previous bit value. + */ + +_INLINE_ void ext2fs_fast_set_bit(unsigned int nr,void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR |= (1 << (nr & 0x07)); +} + +_INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR &= ~(1 << (nr & 0x07)); +} + + +_INLINE_ void ext2fs_fast_set_bit64(__u64 nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR |= (1 << (nr & 0x07)); +} + +_INLINE_ void ext2fs_fast_clear_bit64(__u64 nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR &= ~(1 << (nr & 0x07)); +} + + +#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ + (defined(__i386__) || defined(__i486__) || defined(__i586__))) + +#define _EXT2_HAVE_ASM_BITOPS_ +#define _EXT2_HAVE_ASM_SWAB_ + +/* + * These are done by inline assembly for speed reasons..... + * + * All bitoperations return 0 if the bit was cleared before the + * operation and != 0 if it was not. Bit 0 is the LSB of addr; bit 32 + * is the LSB of (addr+1). + */ + +/* + * Some hacks to defeat gcc over-optimizations.. + */ +struct __dummy_h { unsigned long a[100]; }; +#define EXT2FS_ADDR (*(struct __dummy_h *) addr) +#define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr) + +_INLINE_ int ext2fs_set_bit(unsigned int nr, void * addr) +{ + int oldbit; + + addr = (void *) (((unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (EXT2FS_ADDR) + :"r" (nr & 7)); + return oldbit; +} + +_INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + int oldbit; + + addr = (void *) (((unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (EXT2FS_ADDR) + :"r" (nr & 7)); + return oldbit; +} + +_INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + int oldbit; + + addr = (const void *) (((const unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit) + :"m" (EXT2FS_CONST_ADDR),"r" (nr & 7)); + return oldbit; +} + +_INLINE_ __u32 ext2fs_swab32(__u32 val) +{ +#ifdef EXT2FS_REQUIRE_486 + __asm__("bswap %0" : "=r" (val) : "0" (val)); +#else + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (val) + : "0" (val)); +#endif + return val; +} + +_INLINE_ __u16 ext2fs_swab16(__u16 val) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ \ + : "=q" (val) \ + : "0" (val)); \ + return val; +} + +#undef EXT2FS_ADDR + +#endif /* i386 */ + +#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ + (defined(__mc68000__))) + +#define _EXT2_HAVE_ASM_BITOPS_ + +_INLINE_ int ext2fs_set_bit(unsigned int nr,void * addr) +{ + char retval; + + __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +_INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + char retval; + + __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +_INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + char retval; + + __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +#endif /* __mc68000__ */ + + +#if !defined(_EXT2_HAVE_ASM_SWAB_) + +_INLINE_ __u16 ext2fs_swab16(__u16 val) +{ + return (val >> 8) | (val << 8); +} + +_INLINE_ __u32 ext2fs_swab32(__u32 val) +{ + return ((val>>24) | ((val>>8)&0xFF00) | + ((val<<8)&0xFF0000) | (val<<24)); +} + +#endif /* !_EXT2_HAVE_ASM_SWAB */ + +_INLINE_ __u64 ext2fs_swab64(__u64 val) +{ + return (ext2fs_swab32(val >> 32) | + (((__u64)ext2fs_swab32(val & 0xFFFFFFFFUL)) << 32)); +} + +_INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + return ext2fs_test_block_bitmap_range(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + ext2fs_mark_block_bitmap_range(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + ext2fs_unmark_block_bitmap_range(bitmap, block, num); +} + +/* 64-bit versions */ + +_INLINE_ int ext2fs_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ void ext2fs_fast_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ void ext2fs_fast_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start2(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end2(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + return ext2fs_test_block_bitmap_range2(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + ext2fs_mark_block_bitmap_range2(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + ext2fs_unmark_block_bitmap_range2(bitmap, block, num); +} + +#undef _INLINE_ +#endif + +#endif diff --git a/lib/libext2fs/source/blkmap64_ba.c b/lib/libext2fs/source/blkmap64_ba.c new file mode 100644 index 0000000..395aba9 --- /dev/null +++ b/lib/libext2fs/source/blkmap64_ba.c @@ -0,0 +1,327 @@ +/* + * blkmap64_ba.c --- Simple bitarray implementation for bitmaps + * + * Copyright (C) 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "bmap64.h" + +/* + * Private data for bit array implementation of bitmap ops. + * Currently, this is just a pointer to our big flat hunk of memory, + * exactly equivalent to the old-skool char * bitmap member. + */ + +struct ext2fs_ba_private_struct { + char *bitarray; +}; + +typedef struct ext2fs_ba_private_struct *ext2fs_ba_private; + +static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp; + errcode_t retval; + size_t size; + + /* + * Since we only have the one pointer, we could just shove our + * private data in the void *private field itself, but then + * we'd have to do a fair bit of rewriting if we ever added a + * field. I'm agnostic. + */ + retval = ext2fs_get_mem(sizeof (ext2fs_ba_private), &bp); + if (retval) + return retval; + + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + + retval = ext2fs_get_mem(size, &bp->bitarray); + if (retval) { + ext2fs_free_mem(&bp); + bp = 0; + return retval; + } + bitmap->private = (void *) bp; + return 0; +} + +static errcode_t ba_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)), + ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp; + errcode_t retval; + size_t size; + + retval = ba_alloc_private_data (bitmap); + if (retval) + return retval; + + bp = (ext2fs_ba_private) bitmap->private; + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + memset(bp->bitarray, 0, size); + + return 0; +} + +static void ba_free_bmap(ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + if (!bp) + return; + + if (bp->bitarray) { + ext2fs_free_mem (&bp->bitarray); + bp->bitarray = 0; + } + ext2fs_free_mem (&bp); + bp = 0; +} + +static errcode_t ba_copy_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap dest) +{ + ext2fs_ba_private src_bp = (ext2fs_ba_private) src->private; + ext2fs_ba_private dest_bp; + errcode_t retval; + size_t size; + + retval = ba_alloc_private_data (dest); + if (retval) + return retval; + + dest_bp = (ext2fs_ba_private) dest->private; + + size = (size_t) (((src->real_end - src->start) / 8) + 1); + memcpy (dest_bp->bitarray, src_bp->bitarray, size); + + return 0; +} + +static errcode_t ba_resize_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, __u64 new_real_end) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bmap->private; + errcode_t retval; + size_t size, new_size; + __u64 bitno; + + /* + * If we're expanding the bitmap, make sure all of the new + * parts of the bitmap are zero. + */ + if (new_end > bmap->end) { + bitno = bmap->real_end; + if (bitno > new_end) + bitno = new_end; + for (; bitno > bmap->end; bitno--) + ext2fs_clear_bit64(bitno - bmap->start, bp->bitarray); + } + if (new_real_end == bmap->real_end) { + bmap->end = new_end; + return 0; + } + + size = ((bmap->real_end - bmap->start) / 8) + 1; + new_size = ((new_real_end - bmap->start) / 8) + 1; + + if (size != new_size) { + retval = ext2fs_resize_mem(size, new_size, &bp->bitarray); + if (retval) + return retval; + } + if (new_size > size) + memset(bp->bitarray + size, 0, new_size - size); + + bmap->end = new_end; + bmap->real_end = new_real_end; + return 0; + +} + +static int ba_mark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_set_bit64(bitno - bitmap->start, bp->bitarray); +} + +static int ba_unmark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_clear_bit64(bitno - bitmap->start, bp->bitarray); +} + +static int ba_test_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_test_bit64(bitno - bitmap->start, bp->bitarray); +} + +static void ba_mark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + unsigned int i; + + for (i = 0; i < num; i++) + ext2fs_fast_set_bit64(bitno + i - bitmap->start, bp->bitarray); +} + +static void ba_unmark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + unsigned int i; + + for (i = 0; i < num; i++) + ext2fs_fast_clear_bit64(bitno + i - bitmap->start, bp->bitarray); +} + +static int ba_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int len) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + __u64 start_byte, len_byte = len >> 3; + unsigned int start_bit, len_bit = len % 8; + unsigned int first_bit = 0; + unsigned int last_bit = 0; + int mark_count = 0; + int mark_bit = 0; + int i; + const char *ADDR; + + ADDR = bp->bitarray; + start -= bitmap->start; + start_byte = start >> 3; + start_bit = start % 8; + + if (start_bit != 0) { + /* + * The compared start block number or start inode number + * is not the first bit in a byte. + */ + mark_count = 8 - start_bit; + if (len < 8 - start_bit) { + mark_count = (int)len; + mark_bit = len + start_bit - 1; + } else + mark_bit = 7; + + for (i = mark_count; i > 0; i--, mark_bit--) + first_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the first byte. + * If there is any marked bit, this function returns 0. + */ + if (first_bit & ADDR[start_byte]) + return 0; + else if (len <= 8 - start_bit) + return 1; + + start_byte++; + len_bit = (len - mark_count) % 8; + len_byte = (len - mark_count) >> 3; + } + + /* + * The compared start block number or start inode number is + * the first bit in a byte. + */ + if (len_bit != 0) { + /* + * The compared end block number or end inode number is + * not the last bit in a byte. + */ + for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--) + last_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the last byte. + * If there is any marked bit, this function returns 0. + */ + if (last_bit & ADDR[start_byte + len_byte]) + return 0; + else if (len_byte == 0) + return 1; + } + + /* Check whether all bytes are 0 */ + return ext2fs_mem_is_zero(ADDR + start_byte, len_byte); +} + + +static errcode_t ba_set_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *in) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memcpy (bp->bitarray + (start >> 3), in, (num + 7) >> 3); + + return 0; +} + +static errcode_t ba_get_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *out) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memcpy (out, bp->bitarray + (start >> 3), (num + 7) >> 3); + + return 0; +} + +static void ba_clear_bmap(ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memset(bp->bitarray, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} + +struct ext2_bitmap_ops ext2fs_blkmap64_bitarray = { + .type = EXT2FS_BMAP64_BITARRAY, + .new_bmap = ba_new_bmap, + .free_bmap = ba_free_bmap, + .copy_bmap = ba_copy_bmap, + .resize_bmap = ba_resize_bmap, + .mark_bmap = ba_mark_bmap, + .unmark_bmap = ba_unmark_bmap, + .test_bmap = ba_test_bmap, + .test_clear_bmap_extent = ba_test_clear_bmap_extent, + .mark_bmap_extent = ba_mark_bmap_extent, + .unmark_bmap_extent = ba_unmark_bmap_extent, + .set_bmap_range = ba_set_bmap_range, + .get_bmap_range = ba_get_bmap_range, + .clear_bmap = ba_clear_bmap, +}; diff --git a/lib/libext2fs/source/blknum.c b/lib/libext2fs/source/blknum.c new file mode 100644 index 0000000..b3e6dca --- /dev/null +++ b/lib/libext2fs/source/blknum.c @@ -0,0 +1,477 @@ +/* + * blknum.c --- Functions to handle blk64_t and high/low 64-bit block + * number. + * + * Copyright IBM Corporation, 2007 + * Author Jose R. Santos + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" + +/* + * Return the group # of a block + */ +dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t blk) +{ + return (blk - fs->super->s_first_data_block) / + fs->super->s_blocks_per_group; +} + +/* + * Return the first block (inclusive) in a group + */ +blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group) +{ + return fs->super->s_first_data_block + + ((blk64_t)group * fs->super->s_blocks_per_group); +} + +/* + * Return the last block (inclusive) in a group + */ +blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group) +{ + return (group == fs->group_desc_count - 1 ? + ext2fs_blocks_count(fs->super) - 1 : + ext2fs_group_first_block2(fs, group) + + (fs->super->s_blocks_per_group - 1)); +} + +/* + * Return the inode data block count + */ +blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs, + struct ext2_inode *inode) +{ + return (inode->i_blocks | + ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ? + (__u64) inode->osd2.linux2.l_i_blocks_hi << 32 : 0)) - + (inode->i_file_acl ? fs->blocksize >> 9 : 0); +} + +/* + * Return the inode i_blocks count + */ +blk64_t ext2fs_inode_i_blocks(ext2_filsys fs, + struct ext2_inode *inode) +{ + return (inode->i_blocks | + ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ? + (__u64)inode->osd2.linux2.l_i_blocks_hi << 32 : 0)); +} + +/* + * Return the fs block count + */ +blk64_t ext2fs_blocks_count(struct ext2_super_block *super) +{ + return super->s_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_blocks_count_hi << 32 : 0); +} + +/* + * Set the fs block count + */ +void ext2fs_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_blocks_count_hi = (__u64) blk >> 32; +} + +/* + * Add to the current fs block count + */ +void ext2fs_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_blocks_count(super) + blk; + ext2fs_blocks_count_set(super, tmp); +} + +/* + * Return the fs reserved block count + */ +blk64_t ext2fs_r_blocks_count(struct ext2_super_block *super) +{ + return super->s_r_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_r_blocks_count_hi << 32 : 0); +} + +/* + * Set the fs reserved block count + */ +void ext2fs_r_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_r_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_r_blocks_count_hi = (__u64) blk >> 32; +} + +/* + * Add to the current reserved fs block count + */ +void ext2fs_r_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_r_blocks_count(super) + blk; + ext2fs_r_blocks_count_set(super, tmp); +} + +/* + * Return the fs free block count + */ +blk64_t ext2fs_free_blocks_count(struct ext2_super_block *super) +{ + return super->s_free_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_free_blocks_hi << 32 : 0); +} + +/* + * Set the fs free block count + */ +void ext2fs_free_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_free_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_free_blocks_hi = (__u64) blk >> 32; +} + +/* + * Add to the current free fs block count + */ +void ext2fs_free_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_free_blocks_count(super) + blk; + ext2fs_free_blocks_count_set(super, tmp); +} + +/* + * Get a pointer to a block group descriptor. We need the explicit + * pointer to the group desc for code that swaps block group + * descriptors before writing them out, as it wants to make a copy and + * do the swap there. + */ +struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group) +{ + if (fs->super->s_desc_size >= EXT2_MIN_DESC_SIZE_64BIT) + return (struct ext2_group_desc *) + ((struct ext4_group_desc *) gdp + group); + else + return (struct ext2_group_desc *) gdp + group; +} + +/* Do the same but as an ext4 group desc for internal use here */ +static struct ext4_group_desc *ext4fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group) +{ + return (struct ext4_group_desc *)ext2fs_group_desc(fs, gdp, group); +} + +/* + * Return the block bitmap block of a group + */ +blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_block_bitmap | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64)gdp->bg_block_bitmap_hi << 32 : 0); +} + +/* + * Set the block bitmap block of a group + */ +void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_block_bitmap = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_block_bitmap_hi = (__u64) blk >> 32; +} + +/* + * Return the inode bitmap block of a group + */ +blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_inode_bitmap | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) gdp->bg_inode_bitmap_hi << 32 : 0); +} + +/* + * Set the inode bitmap block of a group + */ +void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_inode_bitmap = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_inode_bitmap_hi = (__u64) blk >> 32; +} + +/* + * Return the inode table block of a group + */ +blk64_t ext2fs_inode_table_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_inode_table | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) gdp->bg_inode_table_hi << 32 : 0); +} + +/* + * Set the inode table block of a group + */ +void ext2fs_inode_table_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_inode_table = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_inode_table_hi = (__u64) blk >> 32; +} + +/* + * Return the free blocks count of a group + */ +__u32 ext2fs_bg_free_blocks_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_free_blocks_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_free_blocks_count_hi << 16 : 0); +} + +/* + * Set the free blocks count of a group + */ +void ext2fs_bg_free_blocks_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_free_blocks_count = n; + + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_free_blocks_count_hi = (__u32) n >> 16; +} + +/* + * Return the free inodes count of a group + */ +__u32 ext2fs_bg_free_inodes_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_free_inodes_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_free_inodes_count_hi << 16 : 0); +} + +/* + * Set the free inodes count of a group + */ +void ext2fs_bg_free_inodes_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_free_inodes_count = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_free_inodes_count_hi = (__u32) n >> 16; +} + +/* + * Return the used dirs count of a group + */ +__u32 ext2fs_bg_used_dirs_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_used_dirs_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_used_dirs_count_hi << 16 : 0); +} + +/* + * Set the used dirs count of a group + */ +void ext2fs_bg_used_dirs_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_used_dirs_count = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_used_dirs_count_hi = (__u32) n >> 16; +} + +/* + * Return the unused inodes count of a group + */ +__u32 ext2fs_bg_itable_unused(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_itable_unused | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_itable_unused_hi << 16 : 0); +} + +/* + * Set the unused inodes count of a group + */ +void ext2fs_bg_itable_unused_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_itable_unused = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_itable_unused_hi = (__u32) n >> 16; +} + +/* + * Get the flags for this block group + */ +__u16 ext2fs_bg_flags(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_flags; +} + +/* + * Zero out the flags for this block group + */ +void ext2fs_bg_flags_zap(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags = 0; + return; +} + +/* + * Get the value of a particular flag for this block group + */ +int ext2fs_bg_flags_test(ext2_filsys fs, dgrp_t group, __u16 bg_flag) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_flags & bg_flag; +} + +/* + * Set a flag or set of flags for this block group + */ +void ext2fs_bg_flags_set(ext2_filsys fs, dgrp_t group, __u16 bg_flags) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags |= bg_flags; + return; +} + +/* + * Clear a flag or set of flags for this block group + */ +void ext2fs_bg_flags_clear(ext2_filsys fs, dgrp_t group, __u16 bg_flags) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags &= ~bg_flags; + return; +} + +/* + * Get the checksum for this block group + */ +__u16 ext2fs_bg_checksum(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_checksum; +} + +/* + * Set the checksum for this block group to a previously calculated value + */ +void ext2fs_bg_checksum_set(ext2_filsys fs, dgrp_t group, __u16 checksum) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_checksum = checksum; + return; +} + +/* + * Get the acl block of a file + * + * XXX Ignoring 64-bit file system flag - most places where this is + * called don't have access to the fs struct, and the high bits should + * be 0 in the non-64-bit case anyway. + */ +blk64_t ext2fs_file_acl_block(const struct ext2_inode *inode) +{ + return (inode->i_file_acl | + (__u64) inode->osd2.linux2.l_i_file_acl_high << 32); +} + +/* + * Set the acl block of a file + */ +void ext2fs_file_acl_block_set(struct ext2_inode *inode, blk64_t blk) +{ + inode->i_file_acl = blk; + inode->osd2.linux2.l_i_file_acl_high = (__u64) blk >> 32; +} + diff --git a/lib/libext2fs/source/block.c b/lib/libext2fs/source/block.c new file mode 100644 index 0000000..0e4ec77 --- /dev/null +++ b/lib/libext2fs/source/block.c @@ -0,0 +1,623 @@ +/* + * block.c --- iterate over all blocks in an inode + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct block_context { + ext2_filsys fs; + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t bcount, + blk64_t ref_blk, + int ref_offset, + void *priv_data); + e2_blkcnt_t bcount; + int bsize; + int flags; + errcode_t errcode; + char *ind_buf; + char *dind_buf; + char *tind_buf; + void *priv_data; +}; + +#define check_for_ro_violation_return(ctx, ret) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + return ret; \ + } \ + } while (0) + +#define check_for_ro_violation_goto(ctx, ret, label) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + goto label; \ + } \ + } while (0) + +static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) { + blk64 = *ind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + *ind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*ind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit; + return ret; + } + if (*ind_block >= ext2fs_blocks_count(ctx->fs->super) || + *ind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_IND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->ind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + blk64 = *block_nr; + flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + *block_nr = blk64; + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + if (*block_nr == 0) + goto skip_sparse; + blk64 = *block_nr; + flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + *block_nr = blk64; + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + skip_sparse: + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *ind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + *ind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) { + blk64 = *dind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + *dind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*dind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit; + return ret; + } + if (*dind_block >= ext2fs_blocks_count(ctx->fs->super) || + *dind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->dind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit; + continue; + } + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *dind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + *dind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) { + blk64 = *tind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + *tind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*tind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit*limit; + return ret; + } + if (*tind_block >= ext2fs_blocks_count(ctx->fs->super) || + *tind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->tind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit*limit; + continue; + } + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *tind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + *tind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +errcode_t ext2fs_block_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + int i; + int r, ret = 0; + struct ext2_inode inode; + errcode_t retval; + struct block_context ctx; + int limit; + blk64_t blk64; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ctx.errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx.errcode) + return ctx.errcode; + + /* + * Check to see if we need to limit large files + */ + if (flags & BLOCK_FLAG_NO_LARGE) { + if (!LINUX_S_ISDIR(inode.i_mode) && + (inode.i_size_high != 0)) + return EXT2_ET_FILE_TOO_BIG; + } + + limit = fs->blocksize >> 2; + + ctx.fs = fs; + ctx.func = func; + ctx.priv_data = priv_data; + ctx.flags = flags; + ctx.bcount = 0; + if (block_buf) { + ctx.ind_buf = block_buf; + } else { + retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf); + if (retval) + return retval; + } + ctx.dind_buf = ctx.ind_buf + fs->blocksize; + ctx.tind_buf = ctx.dind_buf + fs->blocksize; + + /* + * Iterate over the HURD translator block (if present) + */ + if ((fs->super->s_creator_os == EXT2_OS_HURD) && + !(flags & BLOCK_FLAG_DATA_ONLY)) { + if (inode.osd1.hurd1.h_i_translator) { + blk64 = inode.osd1.hurd1.h_i_translator; + ret |= (*ctx.func)(fs, &blk64, + BLOCK_COUNT_TRANSLATOR, + 0, 0, priv_data); + inode.osd1.hurd1.h_i_translator = (blk_t) blk64; + if (ret & BLOCK_ABORT) + goto abort_exit; + check_for_ro_violation_goto(&ctx, ret, abort_exit); + } + } + + if (inode.i_flags & EXT4_EXTENTS_FL) { + ext2_extent_handle_t handle; + struct ext2fs_extent extent; + e2_blkcnt_t blockcnt = 0; + blk64_t blk, new_blk; + int op = EXT2_EXTENT_ROOT; + int uninit; + unsigned int j; + + ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle); + if (ctx.errcode) + goto abort_exit; + + while (1) { + ctx.errcode = ext2fs_extent_get(handle, op, &extent); + if (ctx.errcode) { + if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT) + break; + ctx.errcode = 0; + if (!(flags & BLOCK_FLAG_APPEND)) + break; + next_block_set: + blk = 0; + r = (*ctx.func)(fs, &blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt++, + (blk64_t) blk, 0); + if (ctx.errcode || (ret & BLOCK_ABORT)) + break; + if (blk) + goto next_block_set; + } + break; + } + + op = EXT2_EXTENT_NEXT; + blk = extent.e_pblk; + if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { + if (ctx.flags & BLOCK_FLAG_DATA_ONLY) + continue; + if ((!(extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) || + ((extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) { + ret |= (*ctx.func)(fs, &blk, + -1, 0, 0, priv_data); + if (ret & BLOCK_CHANGED) { + extent.e_pblk = blk; + ctx.errcode = + ext2fs_extent_replace(handle, 0, &extent); + if (ctx.errcode) + break; + } + } + continue; + } + uninit = 0; + if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + uninit = EXT2_EXTENT_SET_BMAP_UNINIT; + for (blockcnt = extent.e_lblk, j = 0; + j < extent.e_len; + blk++, blockcnt++, j++) { + new_blk = blk; + r = (*ctx.func)(fs, &new_blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt, + new_blk, uninit); + if (ctx.errcode) + goto extent_errout; + } + if (ret & BLOCK_ABORT) + break; + } + } + + extent_errout: + ext2fs_extent_free(handle); + ret |= BLOCK_ERROR | BLOCK_ABORT; + goto errout; + } + + /* + * Iterate over normal data blocks + */ + for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { + if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { + blk64 = inode.i_block[i]; + ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, + priv_data); + inode.i_block[i] = (blk_t) blk64; + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } + check_for_ro_violation_goto(&ctx, ret, abort_exit); + if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK], + 0, EXT2_IND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit; + if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK], + 0, EXT2_DIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit * limit; + if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK], + 0, EXT2_TIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + +abort_exit: + if (ret & BLOCK_CHANGED) { + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + ret |= BLOCK_ERROR; + ctx.errcode = retval; + } + } +errout: + if (!block_buf) + ext2fs_free_mem(&ctx.ind_buf); + + return (ret & BLOCK_ERROR) ? ctx.errcode : 0; +} + +/* + * Emulate the old ext2fs_block_iterate function! + */ + +struct xlate64 { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data); + void *real_private; +}; + +static int xlate64_func(ext2_filsys fs, blk64_t *blocknr, + e2_blkcnt_t blockcnt, blk64_t ref_blk, + int ref_offset, void *priv_data) +{ + struct xlate64 *xl = (struct xlate64 *) priv_data; + int ret; + blk_t block32 = *blocknr; + + ret = (*xl->func)(fs, &block32, blockcnt, (blk_t) ref_blk, ref_offset, + xl->real_private); + *blocknr = block32; + return ret; +} + +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + struct xlate64 xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_block_iterate3(fs, ino, flags, block_buf, + xlate64_func, &xl); +} + + +struct xlate { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int bcount, + void *priv_data); + void *real_private; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); +} + +errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, + block_buf, xlate_func, &xl); +} + diff --git a/lib/libext2fs/source/bmap.c b/lib/libext2fs/source/bmap.c new file mode 100644 index 0000000..fbcb375 --- /dev/null +++ b/lib/libext2fs/source/bmap.c @@ -0,0 +1,335 @@ +/* + * bmap.c --- logical to physical block mapping + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) +#define _BMAP_INLINE_ __inline__ +#else +#define _BMAP_INLINE_ +#endif + +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); + +#define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) + +static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, + blk_t ind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + errcode_t retval; + blk_t b; + + if (!ind) { + if (flags & BMAP_SET) + return EXT2_ET_SET_BMAP_NO_IND; + *ret_blk = 0; + return 0; + } + retval = io_channel_read_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + if (flags & BMAP_SET) { + b = *ret_blk; +#ifdef WORDS_BIGENDIAN + b = ext2fs_swab32(b); +#endif + ((blk_t *) block_buf)[nr] = b; + return io_channel_write_blk(fs->io, ind, 1, block_buf); + } + + b = ((blk_t *) block_buf)[nr]; + +#ifdef WORDS_BIGENDIAN + b = ext2fs_swab32(b); +#endif + + if (!b && (flags & BMAP_ALLOC)) { + b = nr ? ((blk_t *) block_buf)[nr-1] : 0; + retval = ext2fs_alloc_block(fs, b, + block_buf + fs->blocksize, &b); + if (retval) + return retval; + +#ifdef WORDS_BIGENDIAN + ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); +#else + ((blk_t *) block_buf)[nr] = b; +#endif + + retval = io_channel_write_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + (*blocks_alloc)++; + } + + *ret_blk = b; + return 0; +} + +static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, + blk_t dind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, + blk_t tind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk64_t block, + int *ret_flags, blk64_t *phys_blk) +{ + struct ext2_inode inode_buf; + ext2_extent_handle_t handle = 0; + blk_t addr_per_block; + blk_t b, blk32; + char *buf = 0; + errcode_t retval = 0; + int blocks_alloc = 0, inode_dirty = 0; + + if (!(bmap_flags & BMAP_SET)) + *phys_blk = 0; + + if (ret_flags) + *ret_flags = 0; + + /* Read inode structure if necessary */ + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + addr_per_block = (blk_t) fs->blocksize >> 2; + + if (inode->i_flags & EXT4_EXTENTS_FL) { + struct ext2fs_extent extent; + unsigned int offset; + + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + goto done; + if (bmap_flags & BMAP_SET) { + retval = ext2fs_extent_set_bmap(handle, block, + *phys_blk, 0); + goto done; + } + retval = ext2fs_extent_goto(handle, block); + if (retval) { + /* If the extent is not found, return phys_blk = 0 */ + if (retval == EXT2_ET_EXTENT_NOT_FOUND) + goto got_block; + goto done; + } + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + offset = block - extent.e_lblk; + if (block >= extent.e_lblk && (offset <= extent.e_len)) { + *phys_blk = extent.e_pblk + offset; + if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + *ret_flags |= BMAP_RET_UNINIT; + } + got_block: + if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + retval = ext2fs_extent_set_bmap(handle, block, + (blk64_t) b, 0); + if (retval) + goto done; + /* Update inode after setting extent */ + retval = ext2fs_read_inode(fs, ino, inode); + if (retval) + return retval; + blocks_alloc++; + *phys_blk = b; + } + retval = 0; + goto done; + } + + if (!block_buf) { + retval = ext2fs_get_array(2, fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + if (block < EXT2_NDIR_BLOCKS) { + if (bmap_flags & BMAP_SET) { + b = *phys_blk; + inode_bmap(inode, block) = b; + inode_dirty++; + goto done; + } + + *phys_blk = inode_bmap(inode, block); + b = block ? inode_bmap(inode, block-1) : 0; + + if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, block) = b; + blocks_alloc++; + *phys_blk = b; + } + goto done; + } + + /* Indirect block */ + block -= EXT2_NDIR_BLOCKS; + blk32 = *phys_blk; + if (block < addr_per_block) { + b = inode_bmap(inode, EXT2_IND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK-1); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_IND_BLOCK) = b; + blocks_alloc++; + } + retval = block_ind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; + goto done; + } + + /* Doubly indirect block */ + block -= addr_per_block; + if (block < addr_per_block * addr_per_block) { + b = inode_bmap(inode, EXT2_DIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_DIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_dind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; + goto done; + } + + /* Triply indirect block */ + block -= addr_per_block * addr_per_block; + b = inode_bmap(inode, EXT2_TIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_DIND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_TIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_tind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; +done: + if (buf) + ext2fs_free_mem(&buf); + if (handle) + ext2fs_extent_free(handle); + if ((retval == 0) && (blocks_alloc || inode_dirty)) { + ext2fs_iblk_add_blocks(fs, inode, blocks_alloc); + retval = ext2fs_write_inode(fs, ino, inode); + } + return retval; +} + +errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk_t block, + blk_t *phys_blk) +{ + errcode_t ret; + blk64_t ret_blk = *phys_blk; + + ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block, + 0, &ret_blk); + if (ret) + return ret; + if (ret_blk >= ((long long) 1 << 32)) + return EOVERFLOW; + *phys_blk = ret_blk; + return 0; +} diff --git a/lib/libext2fs/source/bmap64.h b/lib/libext2fs/source/bmap64.h new file mode 100644 index 0000000..b0aa84c --- /dev/null +++ b/lib/libext2fs/source/bmap64.h @@ -0,0 +1,61 @@ +/* + * bmap64.h --- 64-bit bitmap structure + * + * Copyright (C) 2007, 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct ext2fs_struct_generic_bitmap { + errcode_t magic; + ext2_filsys fs; + struct ext2_bitmap_ops *bitmap_ops; + int flags; + __u64 start, end; + __u64 real_end; + char *description; + void *private; + errcode_t base_error_code; +}; + +#define EXT2FS_IS_32_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP)) + +#define EXT2FS_IS_64_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP64)) + +struct ext2_bitmap_ops { + int type; + /* Generic bmap operators */ + errcode_t (*new_bmap)(ext2_filsys fs, ext2fs_generic_bitmap bmap); + void (*free_bmap)(ext2fs_generic_bitmap bitmap); + errcode_t (*copy_bmap)(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap dest); + errcode_t (*resize_bmap)(ext2fs_generic_bitmap bitmap, + __u64 new_end, + __u64 new_real_end); + /* bit set/test operators */ + int (*mark_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + int (*unmark_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + int (*test_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + void (*mark_bmap_extent)(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num); + void (*unmark_bmap_extent)(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num); + int (*test_clear_bmap_extent)(ext2fs_generic_bitmap bitmap, + __u64 arg, unsigned int num); + errcode_t (*set_bmap_range)(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *in); + errcode_t (*get_bmap_range)(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *out); + void (*clear_bmap)(ext2fs_generic_bitmap bitmap); +}; + +extern struct ext2_bitmap_ops ext2fs_blkmap64_bitarray; diff --git a/lib/libext2fs/source/bmove.c b/lib/libext2fs/source/bmove.c new file mode 100644 index 0000000..deabf38 --- /dev/null +++ b/lib/libext2fs/source/bmove.c @@ -0,0 +1,166 @@ +/* + * bmove.c --- Move blocks around to make way for a particular + * filesystem structure. + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_TIME_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +struct process_block_struct { + ext2_ino_t ino; + struct ext2_inode * inode; + ext2fs_block_bitmap reserve; + ext2fs_block_bitmap alloc_map; + errcode_t error; + char *buf; + int add_dir; + int flags; +}; + +static int process_block(ext2_filsys fs, blk64_t *block_nr, + e2_blkcnt_t blockcnt, blk64_t ref_block, + int ref_offset, void *priv_data) +{ + struct process_block_struct *pb; + errcode_t retval; + int ret; + blk64_t block, orig; + + pb = (struct process_block_struct *) priv_data; + block = orig = *block_nr; + ret = 0; + + /* + * Let's see if this is one which we need to relocate + */ + if (ext2fs_test_block_bitmap2(pb->reserve, block)) { + do { + if (++block >= ext2fs_blocks_count(fs->super)) + block = fs->super->s_first_data_block; + if (block == orig) { + pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; + return BLOCK_ABORT; + } + } while (ext2fs_test_block_bitmap2(pb->reserve, block) || + ext2fs_test_block_bitmap2(pb->alloc_map, block)); + + retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + retval = io_channel_write_blk64(fs->io, block, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + *block_nr = block; + ext2fs_mark_block_bitmap2(pb->alloc_map, block); + ret = BLOCK_CHANGED; + if (pb->flags & EXT2_BMOVE_DEBUG) + printf("ino=%u, blockcnt=%lld, %llu->%llu\n", + (unsigned) pb->ino, blockcnt, + (unsigned long long) orig, + (unsigned long long) block); + } + if (pb->add_dir) { + retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, + block, blockcnt); + if (retval) { + pb->error = retval; + ret |= BLOCK_ABORT; + } + } + return ret; +} + +errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags) +{ + ext2_ino_t ino; + struct ext2_inode inode; + errcode_t retval; + struct process_block_struct pb; + ext2_inode_scan scan; + char *block_buf; + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) + return retval; + + pb.reserve = reserve; + pb.error = 0; + pb.alloc_map = alloc_map ? alloc_map : fs->block_map; + pb.flags = flags; + + retval = ext2fs_get_array(4, fs->blocksize, &block_buf); + if (retval) + return retval; + pb.buf = block_buf + fs->blocksize * 3; + + /* + * If GET_DBLIST is set in the flags field, then we should + * gather directory block information while we're doing the + * block move. + */ + if (flags & EXT2_BMOVE_GET_DBLIST) { + if (fs->dblist) { + ext2fs_free_dblist(fs->dblist); + fs->dblist = NULL; + } + retval = ext2fs_init_dblist(fs, 0); + if (retval) + return retval; + } + + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) + return retval; + + while (ino) { + if ((inode.i_links_count == 0) || + !ext2fs_inode_has_valid_blocks(&inode)) + goto next; + + pb.ino = ino; + pb.inode = &inode; + + pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && + flags & EXT2_BMOVE_GET_DBLIST); + + retval = ext2fs_block_iterate3(fs, ino, 0, block_buf, + process_block, &pb); + if (retval) + return retval; + if (pb.error) + return pb.error; + + next: + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + goto next; + } + return 0; +} + diff --git a/lib/libext2fs/source/brel.h b/lib/libext2fs/source/brel.h new file mode 100644 index 0000000..a0dd5b9 --- /dev/null +++ b/lib/libext2fs/source/brel.h @@ -0,0 +1,86 @@ +/* + * brel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +struct ext2_block_relocate_entry { + blk_t new; + __s16 offset; + __u16 flags; + union { + blk_t block_ref; + ext2_ino_t inode_ref; + } owner; +}; + +#define RELOCATE_TYPE_REF 0x0007 +#define RELOCATE_BLOCK_REF 0x0001 +#define RELOCATE_INODE_REF 0x0002 + +typedef struct ext2_block_relocation_table *ext2_brel; + +struct ext2_block_relocation_table { + __u32 magic; + char *name; + blk_t current; + void *priv_data; + + /* + * Add a block relocation entry. + */ + errcode_t (*put)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Get a block relocation entry. + */ + errcode_t (*get)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Initialize for iterating over the block relocation entries. + */ + errcode_t (*start_iter)(ext2_brel brel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); + + /* + * Move the inode relocation table from one block number to + * another. + */ + errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); + + /* + * Remove a block relocation entry. + */ + errcode_t (*delete)(ext2_brel brel, blk_t old); + + + /* + * Free the block relocation table. + */ + errcode_t (*free)(ext2_brel brel); +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *brel); + +#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) +#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) +#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) +#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) +#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) +#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) +#define ext2fs_brel_free(brel) ((brel)->free((brel))) + diff --git a/lib/libext2fs/source/brel_ma.c b/lib/libext2fs/source/brel_ma.c new file mode 100644 index 0000000..1a55702 --- /dev/null +++ b/lib/libext2fs/source/brel_ma.c @@ -0,0 +1,198 @@ +/* + * brel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * TODO: rewrite to not use a direct array!!! (Fortunately this + * module isn't really used yet.) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "brel.h" + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_start_iter(ext2_brel brel); +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); +static errcode_t bma_delete(ext2_brel brel, blk_t old); +static errcode_t bma_free(ext2_brel brel); + +struct brel_ma { + __u32 magic; + blk_t max_block; + struct ext2_block_relocate_entry *entries; +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *new_brel) +{ + ext2_brel brel = 0; + errcode_t retval; + struct brel_ma *ma = 0; + size_t size; + + *new_brel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), + &brel); + if (retval) + goto errout; + memset(brel, 0, sizeof(struct ext2_block_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &brel->name); + if (retval) + goto errout; + strcpy(brel->name, name); + + retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct brel_ma)); + brel->priv_data = ma; + + size = (size_t) (sizeof(struct ext2_block_relocate_entry) * + (max_block+1)); + retval = ext2fs_get_array(max_block+1, + sizeof(struct ext2_block_relocate_entry), &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + ma->max_block = max_block; + + /* + * Fill in the brel data structure + */ + brel->put = bma_put; + brel->get = bma_get; + brel->start_iter = bma_start_iter; + brel->next = bma_next; + brel->move = bma_move; + brel->delete = bma_delete; + brel->free = bma_free; + + *new_brel = brel; + return 0; + +errout: + bma_free(brel); + return retval; +} + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + ma->entries[(unsigned)old] = *ent; + return 0; +} + +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + *ent = ma->entries[old]; + return 0; +} + +static errcode_t bma_start_iter(ext2_brel brel) +{ + brel->current = 0; + return 0; +} + +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + while (++brel->current < ma->max_block) { + if (ma->entries[(unsigned)brel->current].new == 0) + continue; + *old = brel->current; + *ent = ma->entries[(unsigned)brel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if ((old > ma->max_block) || (new > ma->max_block)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)new] = ma->entries[old]; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_delete(ext2_brel brel, blk_t old) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_free(ext2_brel brel) +{ + struct brel_ma *ma; + + if (!brel) + return 0; + + ma = brel->priv_data; + + if (ma) { + if (ma->entries) + ext2fs_free_mem(&ma->entries); + ext2fs_free_mem(&ma); + } + if (brel->name) + ext2fs_free_mem(&brel->name); + ext2fs_free_mem(&brel); + return 0; +} diff --git a/lib/libext2fs/source/check_desc.c b/lib/libext2fs/source/check_desc.c new file mode 100644 index 0000000..7929cd9 --- /dev/null +++ b/lib/libext2fs/source/check_desc.c @@ -0,0 +1,103 @@ +/* + * check_desc.c --- Check the group descriptors of an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This routine sanity checks the group descriptors + */ +errcode_t ext2fs_check_desc(ext2_filsys fs) +{ + ext2fs_block_bitmap bmap; + errcode_t retval; + dgrp_t i; + blk64_t first_block = fs->super->s_first_data_block; + blk64_t last_block = ext2fs_blocks_count(fs->super)-1; + blk64_t blk, b; + int j; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_allocate_block_bitmap(fs, "check_desc map", &bmap); + if (retval) + return retval; + + for (i = 0; i < fs->group_desc_count; i++) + ext2fs_reserve_super_and_bgd(fs, i, bmap); + + for (i = 0; i < fs->group_desc_count; i++) { + if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_FLEX_BG)) { + first_block = ext2fs_group_first_block2(fs, i); + last_block = ext2fs_group_last_block2(fs, i); + if (i == (fs->group_desc_count - 1)) + last_block = ext2fs_blocks_count(fs->super)-1; + } + + /* + * Check to make sure the block bitmap for group is sane + */ + blk = ext2fs_block_bitmap_loc(fs, i); + if (blk < first_block || blk > last_block || + ext2fs_test_block_bitmap2(bmap, blk)) { + retval = EXT2_ET_GDESC_BAD_BLOCK_MAP; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, blk); + + /* + * Check to make sure the inode bitmap for group is sane + */ + blk = ext2fs_inode_bitmap_loc(fs, i); + if (blk < first_block || blk > last_block || + ext2fs_test_block_bitmap2(bmap, blk)) { + retval = EXT2_ET_GDESC_BAD_INODE_MAP; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, blk); + + /* + * Check to make sure the inode table for group is sane + */ + blk = ext2fs_inode_table_loc(fs, i); + if (blk < first_block || + ((blk + fs->inode_blocks_per_group - 1) > last_block)) { + retval = EXT2_ET_GDESC_BAD_INODE_TABLE; + goto errout; + } + for (j = 0, b = blk; j < fs->inode_blocks_per_group; + j++, b++) { + if (ext2fs_test_block_bitmap2(bmap, b)) { + retval = EXT2_ET_GDESC_BAD_INODE_TABLE; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, b); + } + } +errout: + ext2fs_free_block_bitmap(bmap); + return retval; +} diff --git a/lib/libext2fs/source/closefs.c b/lib/libext2fs/source/closefs.c new file mode 100644 index 0000000..8242622 --- /dev/null +++ b/lib/libext2fs/source/closefs.c @@ -0,0 +1,461 @@ +/* + * closefs.c --- close an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2fs_bg_has_super(ext2_filsys fs, int group_block) +{ + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + return 1; + + if (test_root(group_block, 3) || (test_root(group_block, 5)) || + test_root(group_block, 7)) + return 1; + + return 0; +} + +/* + * ext2fs_super_and_bgd_loc2() + * @fs: ext2 fs pointer + * @group given block group + * @ret_super_blk: if !NULL, returns super block location + * @ret_old_desc_blk: if !NULL, returns location of the old block + * group descriptor + * @ret_new_desc_blk: if !NULL, returns location of meta_bg block + * group descriptor + * @ret_used_blks: if !NULL, returns number of blocks used by + * super block and group_descriptors. + * + * Returns errcode_t of 0 + */ +errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, + dgrp_t group, + blk64_t *ret_super_blk, + blk64_t *ret_old_desc_blk, + blk64_t *ret_new_desc_blk, + blk_t *ret_used_blks) +{ + blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; + unsigned int meta_bg, meta_bg_size; + blk_t numblocks = 0; + blk64_t old_desc_blocks; + int has_super; + + group_block = ext2fs_group_first_block2(fs, group); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + has_super = ext2fs_bg_has_super(fs, group); + + if (has_super) { + super_blk = group_block; + numblocks++; + } + meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); + meta_bg = group / meta_bg_size; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (meta_bg < fs->super->s_first_meta_bg)) { + if (has_super) { + old_desc_blk = group_block + 1; + numblocks += old_desc_blocks; + } + } else { + if (((group % meta_bg_size) == 0) || + ((group % meta_bg_size) == 1) || + ((group % meta_bg_size) == (meta_bg_size-1))) { + if (has_super) + has_super = 1; + new_desc_blk = group_block + has_super; + numblocks++; + } + } + + if (ret_super_blk) + *ret_super_blk = super_blk; + if (ret_old_desc_blk) + *ret_old_desc_blk = old_desc_blk; + if (ret_new_desc_blk) + *ret_new_desc_blk = new_desc_blk; + if (ret_used_blks) + *ret_used_blks = numblocks; + + return 0; +} + +/* + * This function returns the location of the superblock, block group + * descriptors for a given block group. It currently returns the + * number of free blocks assuming that inode table and allocation + * bitmaps will be in the group. This is not necessarily the case + * when the flex_bg feature is enabled, so callers should take care! + * It was only really intended for use by mke2fs, and even there it's + * not that useful. + * + * The ext2fs_super_and_bgd_loc2() function is 64-bit block number + * capable and returns the number of blocks used by super block and + * group descriptors. + */ +int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg) +{ + blk64_t ret_super_blk2; + blk64_t ret_old_desc_blk2; + blk64_t ret_new_desc_blk2; + blk_t ret_used_blks; + blk_t numblocks; + unsigned int meta_bg_size; + + ext2fs_super_and_bgd_loc2(fs, group, &ret_super_blk2, + &ret_old_desc_blk2, + &ret_new_desc_blk2, + &ret_used_blks); + + if (group == fs->group_desc_count-1) { + numblocks = (ext2fs_blocks_count(fs->super) - + (blk64_t) fs->super->s_first_data_block) % + (blk64_t) fs->super->s_blocks_per_group; + if (!numblocks) + numblocks = fs->super->s_blocks_per_group; + } else + numblocks = fs->super->s_blocks_per_group; + + if (ret_super_blk) + *ret_super_blk = (blk_t)ret_super_blk2; + if (ret_old_desc_blk) + *ret_old_desc_blk = (blk_t)ret_old_desc_blk2; + if (ret_new_desc_blk) + *ret_new_desc_blk = (blk_t)ret_new_desc_blk2; + if (ret_meta_bg) { + meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); + *ret_meta_bg = group / meta_bg_size; + } + + numblocks -= 2 + fs->inode_blocks_per_group + ret_used_blks; + + return numblocks; +} + +/* + * This function forces out the primary superblock. We need to only + * write out those fields which we have changed, since if the + * filesystem is mounted, it may have changed some of the other + * fields. + * + * It takes as input a superblock which has already been byte swapped + * (if necessary). + * + */ +static errcode_t write_primary_superblock(ext2_filsys fs, + struct ext2_super_block *super) +{ + __u16 *old_super, *new_super; + int check_idx, write_idx, size; + errcode_t retval; + + if (!fs->io->manager->write_byte || !fs->orig_super) { + fallback: + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + retval = io_channel_write_blk64(fs->io, 1, -SUPERBLOCK_SIZE, + super); + io_channel_set_blksize(fs->io, fs->blocksize); + return retval; + } + + old_super = (__u16 *) fs->orig_super; + new_super = (__u16 *) super; + + for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { + if (old_super[check_idx] == new_super[check_idx]) + continue; + write_idx = check_idx; + for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) + if (old_super[check_idx] == new_super[check_idx]) + break; + size = 2 * (check_idx - write_idx); +#if 0 + printf("Writing %d bytes starting at %d\n", + size, write_idx*2); +#endif + retval = io_channel_write_byte(fs->io, + SUPERBLOCK_OFFSET + (2 * write_idx), size, + new_super + write_idx); + if (retval == EXT2_ET_UNIMPLEMENTED) + goto fallback; + if (retval) + return retval; + } + memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); + return 0; +} + + +/* + * Updates the revision to EXT2_DYNAMIC_REV + */ +void ext2fs_update_dynamic_rev(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + + if (sb->s_rev_level > EXT2_GOOD_OLD_REV) + return; + + sb->s_rev_level = EXT2_DYNAMIC_REV; + sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + /* s_uuid is handled by e2fsck already */ + /* other fields should be left alone */ +} + +static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, + blk_t group_block, + struct ext2_super_block *super_shadow) +{ + dgrp_t sgrp = group; + + if (sgrp > ((1 << 16) - 1)) + sgrp = (1 << 16) - 1; +#ifdef WORDS_BIGENDIAN + super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); +#else + fs->super->s_block_group_nr = sgrp; +#endif + + return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, + super_shadow); +} + +errcode_t ext2fs_flush(ext2_filsys fs) +{ + dgrp_t i; + errcode_t retval; + unsigned long fs_state; + __u32 feature_incompat; + struct ext2_super_block *super_shadow = 0; + struct ext2_group_desc *group_shadow = 0; +#ifdef WORDS_BIGENDIAN + struct ext2_group_desc *gdp; + dgrp_t j; +#endif + char *group_ptr; + int old_desc_blocks; + struct ext2fs_numeric_progress_struct progress; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs_state = fs->super->s_state; + feature_incompat = fs->super->s_feature_incompat; + + fs->super->s_wtime = fs->now ? fs->now : time(NULL); + fs->super->s_block_group_nr = 0; +#ifdef WORDS_BIGENDIAN + retval = EXT2_ET_NO_MEMORY; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); + if (retval) + goto errout; + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &group_shadow); + if (retval) + goto errout; + memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize * + fs->desc_blocks); + + /* swap the group descriptors */ + for (j=0; j < fs->group_desc_count; j++) { + gdp = ext2fs_group_desc(fs, (struct opaque_ext2_group_desc *) group_shadow, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#else + super_shadow = fs->super; + group_shadow = ext2fs_group_desc(fs, fs->group_desc, 0); +#endif + + /* + * Set the state of the FS to be non-valid. (The state has + * already been backed up earlier, and will be restored after + * we write out the backup superblocks.) + */ + fs->super->s_state &= ~EXT2_VALID_FS; + fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; +#ifdef WORDS_BIGENDIAN + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); +#endif + + /* + * If this is an external journal device, don't write out the + * block group descriptors or any of the backup superblocks + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + goto write_primary_superblock_only; + + /* + * Write out the master group descriptors, and the backup + * superblocks and group descriptors. + */ + group_ptr = (char *) group_shadow; + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = fs->desc_blocks; + + ext2fs_numeric_progress_init(fs, &progress, NULL, + fs->group_desc_count); + + + for (i = 0; i < fs->group_desc_count; i++) { + blk64_t super_blk, old_desc_blk, new_desc_blk; + + ext2fs_numeric_progress_update(fs, &progress, i); + ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, + &new_desc_blk, 0); + + if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { + retval = write_backup_super(fs, i, super_blk, + super_shadow); + if (retval) + goto errout; + } + if (fs->flags & EXT2_FLAG_SUPER_ONLY) + continue; + if ((old_desc_blk) && + (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { + retval = io_channel_write_blk64(fs->io, + old_desc_blk, old_desc_blocks, group_ptr); + if (retval) + goto errout; + } + if (new_desc_blk) { + int meta_bg = i / EXT2_DESC_PER_BLOCK(fs->super); + + retval = io_channel_write_blk64(fs->io, new_desc_blk, + 1, group_ptr + (meta_bg*fs->blocksize)); + if (retval) + goto errout; + } + } + + ext2fs_numeric_progress_close(fs, &progress, NULL); + + /* + * If the write_bitmaps() function is present, call it to + * flush the bitmaps. This is done this way so that a simple + * program that doesn't mess with the bitmaps doesn't need to + * drag in the bitmaps.c code. + */ + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + goto errout; + } + +write_primary_superblock_only: + /* + * Write out master superblock. This has to be done + * separately, since it is located at a fixed location + * (SUPERBLOCK_OFFSET). We flush all other pending changes + * out to disk first, just to avoid a race condition with an + * insy-tinsy window.... + */ + + fs->super->s_block_group_nr = 0; + fs->super->s_state = fs_state; + fs->super->s_feature_incompat = feature_incompat; +#ifdef WORDS_BIGENDIAN + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); +#endif + + retval = io_channel_flush(fs->io); + retval = write_primary_superblock(fs, super_shadow); + if (retval) + goto errout; + + fs->flags &= ~EXT2_FLAG_DIRTY; + + retval = io_channel_flush(fs->io); +errout: + fs->super->s_state = fs_state; +#ifdef WORDS_BIGENDIAN + if (super_shadow) + ext2fs_free_mem(&super_shadow); + if (group_shadow) + ext2fs_free_mem(&group_shadow); +#endif + return retval; +} + +errcode_t ext2fs_close(ext2_filsys fs) +{ + errcode_t retval; + int meta_blks; + io_stats stats = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + return retval; + } + if (fs->super->s_kbytes_written && + fs->io->manager->get_stats) + fs->io->manager->get_stats(fs->io, &stats); + if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { + fs->super->s_kbytes_written += stats->bytes_written >> 10; + meta_blks = fs->desc_blocks + 1; + if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) + fs->super->s_kbytes_written += meta_blks / + (fs->blocksize / 1024); + if ((fs->flags & EXT2_FLAG_DIRTY) == 0) + fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; + } + if (fs->flags & EXT2_FLAG_DIRTY) { + retval = ext2fs_flush(fs); + if (retval) + return retval; + } + ext2fs_free(fs); + return 0; +} + diff --git a/lib/libext2fs/source/com_err.c b/lib/libext2fs/source/com_err.c new file mode 100644 index 0000000..c27853e --- /dev/null +++ b/lib/libext2fs/source/com_err.c @@ -0,0 +1,35 @@ +/* + * Copyright 1987, 1988 by MIT Student Information Processing Board. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose is hereby granted, provided that + * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. M.I.T. and the + * M.I.T. S.I.P.B. make no representations about the suitability of + * this software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" + +void com_err (const char *whoami, + errcode_t code, + const char *fmt, ...) +{ + if(whoami) + ext2_log_trace("%s: ", whoami); + + ext2_log_trace("error code: %i ", (int) code); + + if(fmt) + ext2_log_trace(fmt); + + ext2_log_trace("\n"); +} diff --git a/lib/libext2fs/source/com_err.h b/lib/libext2fs/source/com_err.h new file mode 100644 index 0000000..0c31a4e --- /dev/null +++ b/lib/libext2fs/source/com_err.h @@ -0,0 +1,70 @@ +/* + * Header file for common error description library. + * + * Copyright 1988, Student Information Processing Board of the + * Massachusetts Institute of Technology. + * + * For copyright and distribution info, see the documentation supplied + * with this package. + */ + +#if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__) + +#ifdef __GNUC__ +#define COM_ERR_ATTR(x) __attribute__(x) +#else +#define COM_ERR_ATTR(x) +#endif + +#ifndef DEBUG_GEKKO +#define OMIT_COM_ERR +#endif + +#include +#include + +typedef long errcode_t; + +struct error_table { + char const * const * msgs; + long base; + int n_msgs; +}; +struct et_list; + +extern void com_err (const char *, long, const char *, ...) + COM_ERR_ATTR((format(printf, 3, 4))); + +extern void com_err_va (const char *whoami, errcode_t code, const char *fmt, + va_list args) + COM_ERR_ATTR((format(printf, 3, 0))); + +extern char const *error_message (long); +extern void (*com_err_hook) (const char *, long, const char *, va_list); +extern void (*set_com_err_hook (void (*) (const char *, long, + const char *, va_list))) + (const char *, long, const char *, va_list); +extern void (*reset_com_err_hook (void)) (const char *, long, + const char *, va_list); +extern int init_error_table(const char * const *msgs, long base, int count); + +extern errcode_t add_error_table(const struct error_table * et); +extern errcode_t remove_error_table(const struct error_table * et); +extern void add_to_error_table(struct et_list *new_table); + +/* Provided for Heimdall compatibility */ +extern const char *com_right(struct et_list *list, long code); +extern const char *com_right_r(struct et_list *list, long code, char *str, size_t len); +extern void initialize_error_table_r(struct et_list **list, + const char **messages, + int num_errors, + long base); +extern void free_error_table(struct et_list *et); + +/* Provided for compatibility with other com_err libraries */ +extern int et_list_lock(void); +extern int et_list_unlock(void); + +#define __COM_ERR_H +#define __COM_ERR_H__ +#endif /* !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)*/ diff --git a/lib/libext2fs/source/config.h b/lib/libext2fs/source/config.h new file mode 100644 index 0000000..be09663 --- /dev/null +++ b/lib/libext2fs/source/config.h @@ -0,0 +1,10 @@ + +#define HAVE_UNISTD_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UTIME_H 1 +#define WORDS_BIGENDIAN 1 +#define HAVE_ERRNO_H 1 +#define EXT2_FLAT_INCLUDES 1 +#define HAVE_STRDUP 1 +#define HAVE_SYS_RESOURCE_H 1 diff --git a/lib/libext2fs/source/crc16.c b/lib/libext2fs/source/crc16.c new file mode 100644 index 0000000..02d81e4 --- /dev/null +++ b/lib/libext2fs/source/crc16.c @@ -0,0 +1,73 @@ +/* + * crc16.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#if HAVE_SYS_TYPES_H +#include +#endif +#include "ext2_types.h" + +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ +static __u16 const crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * Compute the CRC-16 for the data buffer + * + * @param crc previous CRC value + * @param buffer data pointer + * @param len number of bytes in the buffer + * @return the updated CRC value + */ +crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len) +{ + const unsigned char *cp = buffer; + + while (len--) + /* + * for an unknown reason, PPC treats __u16 as signed + * and keeps doing sign extension on the value. + * Instead, use only the low 16 bits of an unsigned + * int for holding the CRC value to avoid this. + */ + crc = (((crc >> 8) & 0xffU) ^ + crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; + return crc; +} diff --git a/lib/libext2fs/source/crc16.h b/lib/libext2fs/source/crc16.h new file mode 100644 index 0000000..322e68d --- /dev/null +++ b/lib/libext2fs/source/crc16.h @@ -0,0 +1,26 @@ +/* + * crc16.h - CRC-16 routine + * + * Implements the standard CRC-16: + * Width 16 + * Poly 0x8005 (x16 + x15 + x2 + 1) + * Init 0 + * + * Copyright (c) 2005 Ben Gardner + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#ifndef __CRC16_H +#define __CRC16_H + +/* for an unknown reason, PPC treats __u16 as signed and keeps doing sign + * extension on the value. Instead, use only the low 16 bits of an + * unsigned int for holding the CRC value to avoid this. + */ +typedef unsigned int crc16_t; + +extern crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len); + +#endif /* __CRC16_H */ diff --git a/lib/libext2fs/source/csum.c b/lib/libext2fs/source/csum.c new file mode 100644 index 0000000..58e2971 --- /dev/null +++ b/lib/libext2fs/source/csum.c @@ -0,0 +1,288 @@ +/* + * csum.c --- checksumming of ext3 structures + * + * Copyright (C) 2006 Cluster File Systems, Inc. + * Copyright (C) 2006, 2007 by Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "crc16.h" +#include + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) +{ + __u16 crc = 0; + struct ext2_group_desc *desc; + size_t size; + + size = fs->super->s_desc_size; + if (size < EXT2_MIN_DESC_SIZE) + size = EXT2_MIN_DESC_SIZE; + if (size > sizeof(struct ext4_group_desc)) { + printf("%s: illegal s_desc_size(%zd)\n", __func__, size); + size = sizeof(struct ext4_group_desc); + } + + desc = ext2fs_group_desc(fs, fs->group_desc, group); + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { + int offset = offsetof(struct ext2_group_desc, bg_checksum); + +#ifdef WORDS_BIGENDIAN + struct ext4_group_desc swabdesc; + + /* Have to swab back to little-endian to do the checksum */ + memcpy(&swabdesc, desc, size); + ext2fs_swap_group_desc2(fs, + (struct ext2_group_desc *) &swabdesc); + desc = (struct ext2_group_desc *) &swabdesc; + + group = ext2fs_swab32(group); +#endif + crc = ext2fs_crc16(~0, fs->super->s_uuid, + sizeof(fs->super->s_uuid)); + crc = ext2fs_crc16(crc, &group, sizeof(group)); + crc = ext2fs_crc16(crc, desc, offset); + offset += sizeof(desc->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (offset < size) { + crc = ext2fs_crc16(crc, (char *)desc + offset, + size - offset); + } + } + + return crc; +} + +int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) +{ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && + (ext2fs_bg_checksum(fs, group) != + ext2fs_group_desc_csum(fs, group))) + return 0; + + return 1; +} + +void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + return; + + /* ext2fs_bg_checksum_set() sets the actual checksum field but + * does not calculate the checksum itself. */ + ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); +} + +static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, + __u32 inodes_per_grp, dgrp_t grp_no) +{ + ext2_ino_t i, start_ino, end_ino; + + start_ino = grp_no * inodes_per_grp + 1; + end_ino = start_ino + inodes_per_grp - 1; + + for (i = end_ino; i >= start_ino; i--) { + if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) + return i - start_ino + 1; + } + return inodes_per_grp; +} + +/* update the bitmap flags, set the itable high watermark, and calculate + * checksums for the group descriptors */ +errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + int dirty = 0; + dgrp_t i; + + if (!fs->inode_map) + return EXT2_ET_NO_INODE_BITMAP; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + return 0; + + for (i = 0; i < fs->group_desc_count; i++) { + unsigned int old_csum = ext2fs_bg_checksum(fs, i); + int old_unused = ext2fs_bg_itable_unused(fs, i); + unsigned int old_flags = ext2fs_bg_flags(fs, i); + int old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); + + if (old_free_inodes_count == sb->s_inodes_per_group) { + ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); + } else { + int unused = + sb->s_inodes_per_group - + find_last_inode_ingrp(fs->inode_map, + sb->s_inodes_per_group, i); + + ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, i, unused); + } + + ext2fs_group_desc_csum_set(fs, i); + if (old_flags != ext2fs_bg_flags(fs, i)) + dirty = 1; + if (old_unused != ext2fs_bg_itable_unused(fs, i)) + dirty = 1; + if (old_csum != ext2fs_bg_checksum(fs, i)) + dirty = 1; + } + if (dirty) + ext2fs_mark_super_dirty(fs); + return 0; +} + +#ifdef DEBUG +#include "e2p/e2p.h" + +void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) +{ + __u16 crc1, crc2, crc3; + dgrp_t swabgroup; + struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, group); + size_t size; + struct ext2_super_block *sb = fs->super; + int offset = offsetof(struct ext2_group_desc, bg_checksum); +#ifdef WORDS_BIGENDIAN + struct ext4_group_desc swabdesc; +#endif + + size = fs->super->s_desc_size; + if (size < EXT2_MIN_DESC_SIZE) + size = EXT2_MIN_DESC_SIZE; + if (size > sizeof(struct ext4_group_desc)) + size = sizeof(struct ext4_group_desc); +#ifdef WORDS_BIGENDIAN + /* Have to swab back to little-endian to do the checksum */ + memcpy(&swabdesc, desc, size); + ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); + desc = (struct ext2_group_desc *) &swabdesc; + + swabgroup = ext2fs_swab32(group); +#else + swabgroup = group; +#endif + + crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); + crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); + crc3 = ext2fs_crc16(crc2, desc, offset); + offset += sizeof(desc->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (offset < size) + crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); + + printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n", + msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, + ext2fs_group_desc_csum(fs, group)); +} + +unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, + 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; + +int main(int argc, char **argv) +{ + struct ext2_super_block param; + errcode_t retval; + ext2_filsys fs; + int i; + __u16 csum1, csum2, csum_known = 0xd3a4; + + memset(¶m, 0, sizeof(param)); + ext2fs_blocks_count_set(¶m, 32768); + + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + test_io_manager, &fs); + if (retval) { + com_err("setup", retval, + "While initializing filesystem"); + exit(1); + } + memcpy(fs->super->s_uuid, sb_uuid, 16); + fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + + for (i=0; i < fs->group_desc_count; i++) { + ext2fs_block_bitmap_loc_set(fs, i, 124); + ext2fs_inode_bitmap_loc_set(fs, i, 125); + ext2fs_inode_table_loc_set(fs, i, 126); + ext2fs_bg_free_blocks_count_set(fs, i, 31119); + ext2fs_bg_free_inodes_count_set(fs, i, 15701); + ext2fs_bg_used_dirs_count_set(fs, i, 2); + ext2fs_bg_flags_zap(fs, i); + }; + + csum1 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum0000", fs, 0); + + if (csum1 != csum_known) { + printf("checksum for group 0 should be %04x\n", csum_known); + exit(1); + } + csum2 = ext2fs_group_desc_csum(fs, 1); + print_csum("csum0001", fs, 1); + if (csum1 == csum2) { + printf("checksums for different groups shouldn't match\n"); + exit(1); + } + csum2 = ext2fs_group_desc_csum(fs, 2); + print_csum("csumffff", fs, 2); + if (csum1 == csum2) { + printf("checksums for different groups shouldn't match\n"); + exit(1); + } + ext2fs_bg_checksum_set(fs, 0, csum1); + csum2 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum_set", fs, 0); + if (csum1 != csum2) { + printf("checksums should not depend on checksum field\n"); + exit(1); + } + if (!ext2fs_group_desc_csum_verify(fs, 0)) { + printf("checksums should verify against gd_checksum\n"); + exit(1); + } + memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); + print_csum("new_uuid", fs, 0); + if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { + printf("checksums for different filesystems shouldn't match\n"); + exit(1); + } + csum1 = ext2fs_group_desc_csum(fs, 0); + ext2fs_bg_checksum_set(fs, 0, csum1); + print_csum("csum_new", fs, 0); + ext2fs_bg_free_blocks_count_set(fs, 0, 1); + csum2 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum_blk", fs, 0); + if (csum1 == csum2) { + printf("checksums for different data shouldn't match\n"); + exit(1); + } + + return 0; +} +#endif diff --git a/lib/libext2fs/source/dblist.c b/lib/libext2fs/source/dblist.c new file mode 100644 index 0000000..8ee61b4 --- /dev/null +++ b/lib/libext2fs/source/dblist.c @@ -0,0 +1,414 @@ +/* + * dblist.c -- directory block list functions + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b); +static EXT2_QSORT_TYPE dir_block_cmp2(const void *a, const void *b); +static EXT2_QSORT_TYPE (*sortfunc32)(const void *a, const void *b); + +/* + * Returns the number of directories in the filesystem as reported by + * the group descriptors. Of course, the group descriptors could be + * wrong! + */ +errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) +{ + dgrp_t i; + ext2_ino_t num_dirs, max_dirs; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + num_dirs = 0; + max_dirs = fs->super->s_inodes_per_group; + for (i = 0; i < fs->group_desc_count; i++) { + if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs) + num_dirs += max_dirs / 8; + else + num_dirs += ext2fs_bg_used_dirs_count(fs, i); + } + if (num_dirs > fs->super->s_inodes_count) + num_dirs = fs->super->s_inodes_count; + + *ret_num_dirs = num_dirs; + + return 0; +} + +/* + * helper function for making a new directory block list (for + * initialize and copy). + */ +static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, + ext2_ino_t count, + struct ext2_db_entry2 *list, + ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + ext2_ino_t num_dirs; + size_t len; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if ((ret_dblist == 0) && fs->dblist && + (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) + return 0; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); + if (retval) + return retval; + memset(dblist, 0, sizeof(struct ext2_struct_dblist)); + + dblist->magic = EXT2_ET_MAGIC_DBLIST; + dblist->fs = fs; + if (size) + dblist->size = size; + else { + retval = ext2fs_get_num_dirs(fs, &num_dirs); + if (retval) + goto cleanup; + dblist->size = (num_dirs * 2) + 12; + } + len = (size_t) sizeof(struct ext2_db_entry2) * dblist->size; + dblist->count = count; + retval = ext2fs_get_array(dblist->size, sizeof(struct ext2_db_entry2), + &dblist->list); + if (retval) + goto cleanup; + + if (list) + memcpy(dblist->list, list, len); + else + memset(dblist->list, 0, len); + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + return 0; +cleanup: + if (dblist) + ext2fs_free_mem(&dblist); + return retval; +} + +/* + * Initialize a directory block list + */ +errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(fs, 0, 0, 0, &dblist); + if (retval) + return retval; + + dblist->sorted = 1; + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + + return 0; +} + +/* + * Copy a directory block list + */ +errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(src->fs, src->size, src->count, src->list, + &dblist); + if (retval) + return retval; + dblist->sorted = src->sorted; + *dest = dblist; + return 0; +} + +/* + * Close a directory block list + * + * (moved to closefs.c) + */ + + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt) +{ + struct ext2_db_entry2 *new_entry; + errcode_t retval; + unsigned long old_size; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count >= dblist->size) { + old_size = dblist->size * sizeof(struct ext2_db_entry2); + dblist->size += dblist->size > 200 ? dblist->size / 2 : 100; + retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * + sizeof(struct ext2_db_entry2), + &dblist->list); + if (retval) { + dblist->size -= 100; + return retval; + } + } + new_entry = dblist->list + ( dblist->count++); + new_entry->blk = blk; + new_entry->ino = ino; + new_entry->blockcnt = blockcnt; + + dblist->sorted = 0; + + return 0; +} + +/* + * Change the directory block to the directory block list + */ +errcode_t ext2fs_set_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt) +{ + dgrp_t i; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + for (i=0; i < dblist->count; i++) { + if ((dblist->list[i].ino != ino) || + (dblist->list[i].blockcnt != blockcnt)) + continue; + dblist->list[i].blk = blk; + dblist->sorted = 0; + return 0; + } + return EXT2_ET_DB_NOT_FOUND; +} + +void ext2fs_dblist_sort2(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)) +{ + if (!sortfunc) + sortfunc = dir_block_cmp2; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry2), sortfunc); + dblist->sorted = 1; +} + +/* + * This function iterates over the directory block list + */ +errcode_t ext2fs_dblist_iterate2(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry2 *db_info, + void *priv_data), + void *priv_data) +{ + unsigned long long i; + int ret; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (!dblist->sorted) + ext2fs_dblist_sort2(dblist, 0); + for (i=0; i < dblist->count; i++) { + ret = (*func)(dblist->fs, &dblist->list[i], priv_data); + if (ret & DBLIST_ABORT) + return 0; + } + return 0; +} + +static EXT2_QSORT_TYPE dir_block_cmp2(const void *a, const void *b) +{ + const struct ext2_db_entry2 *db_a = + (const struct ext2_db_entry2 *) a; + const struct ext2_db_entry2 *db_b = + (const struct ext2_db_entry2 *) b; + + if (db_a->blk != db_b->blk) + return (int) (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (int) (db_a->ino - db_b->ino); + + return (db_a->blockcnt - db_b->blockcnt); +} + +blk64_t ext2fs_dblist_count2(ext2_dblist dblist) +{ + return dblist->count; +} + +errcode_t ext2fs_dblist_get_last2(ext2_dblist dblist, + struct ext2_db_entry2 **entry) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + if (entry) + *entry = dblist->list + ( dblist->count-1); + return 0; +} + +errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + dblist->count--; + return 0; +} + +/* + * Legacy 32-bit versions + */ + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + return ext2fs_add_dir_block2(dblist, ino, blk, blockcnt); +} + +/* + * Change the directory block to the directory block list + */ +errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + return ext2fs_set_dir_block2(dblist, ino, blk, blockcnt); +} + +void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)) +{ + if (sortfunc) { + sortfunc32 = sortfunc; + sortfunc = dir_block_cmp; + } else + sortfunc = dir_block_cmp2; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry2), sortfunc); + dblist->sorted = 1; +} + +/* + * This function iterates over the directory block list + */ +struct iterate_passthrough { + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *priv_data); + void *priv_data; +}; + +static int passthrough_func(ext2_filsys fs, + struct ext2_db_entry2 *db_info, + void *priv_data) +{ + struct iterate_passthrough *p = priv_data; + struct ext2_db_entry db; + int ret; + + db.ino = db_info->ino; + db.blk = (blk_t) db_info->blk; + db.blockcnt = (int) db_info->blockcnt; + ret = (p->func)(fs, &db, p->priv_data); + db_info->ino = db.ino; + db_info->blk = db.blk; + db_info->blockcnt = db.blockcnt; + return ret; +} + +errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data) +{ + struct iterate_passthrough pass; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + pass.func = func; + pass.priv_data = priv_data; + + return ext2fs_dblist_iterate2(dblist, passthrough_func, &pass); +} + +static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry2 *db_a = + (const struct ext2_db_entry2 *) a; + const struct ext2_db_entry2 *db_b = + (const struct ext2_db_entry2 *) b; + + struct ext2_db_entry a32, b32; + + a32.ino = db_a->ino; a32.blk = db_a->blk; + a32.blockcnt = db_a->blockcnt; + + b32.ino = db_b->ino; b32.blk = db_b->blk; + b32.blockcnt = db_b->blockcnt; + + return sortfunc32(&a32, &b32); +} + +int ext2fs_dblist_count(ext2_dblist dblist) +{ + return dblist->count; +} + +errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, + struct ext2_db_entry **entry) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + static struct ext2_db_entry ret_entry; + struct ext2_db_entry2 *last; + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + if (!entry) + return 0; + + last = dblist->list + dblist->count -1; + + ret_entry.ino = last->ino; + ret_entry.blk = last->blk; + ret_entry.blockcnt = last->blockcnt; + *entry = &ret_entry; + + return 0; +} + diff --git a/lib/libext2fs/source/dblist_dir.c b/lib/libext2fs/source/dblist_dir.c new file mode 100644 index 0000000..07ed8af --- /dev/null +++ b/lib/libext2fs/source/dblist_dir.c @@ -0,0 +1,79 @@ +/* + * dblist_dir.c --- iterate by directory entry + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data); + +errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + errcode_t retval; + struct dir_context ctx; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + ctx.dir = 0; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + + retval = ext2fs_dblist_iterate2(dblist, db_dir_proc, &ctx); + + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data) +{ + struct dir_context *ctx; + int ret; + + ctx = (struct dir_context *) priv_data; + ctx->dir = db_info->ino; + ctx->errcode = 0; + + ret = ext2fs_process_dir_block(fs, &db_info->blk, + db_info->blockcnt, 0, 0, priv_data); + if ((ret & BLOCK_ABORT) && !ctx->errcode) + return DBLIST_ABORT; + return 0; +} diff --git a/lib/libext2fs/source/dir_iterate.c b/lib/libext2fs/source/dir_iterate.c new file mode 100644 index 0000000..88f6b47 --- /dev/null +++ b/lib/libext2fs/source/dir_iterate.c @@ -0,0 +1,270 @@ +/* + * dir_iterate.c --- ext2fs directory iteration operations + * + * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +#define EXT4_MAX_REC_LEN ((1<<16)-1) + +errcode_t ext2fs_get_rec_len(ext2_filsys fs, + struct ext2_dir_entry *dirent, + unsigned int *rec_len) +{ + unsigned int len = dirent->rec_len; + + if (fs->blocksize < 65536) + *rec_len = len; + else if (len == EXT4_MAX_REC_LEN || len == 0) + *rec_len = fs->blocksize; + else + *rec_len = (len & 65532) | ((len & 3) << 16); + return 0; +} + +errcode_t ext2fs_set_rec_len(ext2_filsys fs, + unsigned int len, + struct ext2_dir_entry *dirent) +{ + if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) + return EINVAL; + if (len < 65536) { + dirent->rec_len = len; + return 0; + } + if (len == fs->blocksize) { + if (fs->blocksize == 65536) + dirent->rec_len = EXT4_MAX_REC_LEN; + else + dirent->rec_len = 0; + } else + dirent->rec_len = (len & 65532) | ((len >> 16) & 3); + return 0; +} + +/* + * This function checks to see whether or not a potential deleted + * directory entry looks valid. What we do is check the deleted entry + * and each successive entry to make sure that they all look valid and + * that the last deleted entry ends at the beginning of the next + * undeleted entry. Returns 1 if the deleted entry looks valid, zero + * if not valid. + */ +static int ext2fs_validate_entry(ext2_filsys fs, char *buf, + unsigned int offset, + unsigned int final_offset) +{ + struct ext2_dir_entry *dirent; + unsigned int rec_len; +#define DIRENT_MIN_LENGTH 12 + + while ((offset < final_offset) && + (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { + dirent = (struct ext2_dir_entry *)(buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return 0; + offset += rec_len; + if ((rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) + return 0; + } + return (offset == final_offset); +} + +errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, + ext2fs_process_dir_block, &ctx); + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +struct xlate { + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *real_private; +}; + +static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); +} + +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_dir_iterate2(fs, dir, flags, block_buf, + xlate_func, &xl); +} + + +/* + * Helper function which is private to this module. Used by + * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() + */ +int ext2fs_process_dir_block(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + unsigned int rec_len, size; + int entry; + struct ext2_dir_entry *dirent; + + if (blockcnt < 0) + return 0; + + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0); + if (ctx->errcode) + return BLOCK_ABORT; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (((offset + rec_len) > fs->blocksize) || + (rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + fs->blocksize, ctx->buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (rec_len != size) { + unsigned int final_offset; + + final_offset = offset + rec_len; + offset += size; + while (offset < final_offset && + !ext2fs_validate_entry(fs, ctx->buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf, + 0); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} + diff --git a/lib/libext2fs/source/dirblock.c b/lib/libext2fs/source/dirblock.c new file mode 100644 index 0000000..73e1f0a --- /dev/null +++ b/lib/libext2fs/source/dirblock.c @@ -0,0 +1,126 @@ +/* + * dirblock.c --- directory block routines. + * + * Copyright (C) 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + char *p, *end; + struct ext2_dir_entry *dirent; + unsigned int name_len, rec_len; + + + retval = io_channel_read_blk64(fs->io, block, 1, buf); + if (retval) + return retval; + + p = (char *) buf; + end = (char *) buf + fs->blocksize; + while (p < end-8) { + dirent = (struct ext2_dir_entry *) p; +#ifdef WORDS_BIGENDIAN + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + name_len = dirent->name_len; +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || (rec_len % 4)) { + rec_len = 8; + retval = EXT2_ET_DIR_CORRUPTED; + } else if (((name_len & 0xFF) + 8) > rec_len) + retval = EXT2_ET_DIR_CORRUPTED; + p += rec_len; + } + return retval; +} + +errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_read_dir_block3(fs, block, buf, flags); +} + +errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf) +{ + return ext2fs_read_dir_block3(fs, block, buf, 0); +} + + +errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ +#ifdef WORDS_BIGENDIAN + errcode_t retval; + char *p, *end; + char *buf = 0; + unsigned int rec_len; + struct ext2_dir_entry *dirent; + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memcpy(buf, inbuf, fs->blocksize); + p = buf; + end = buf + fs->blocksize; + while (p < end) { + dirent = (struct ext2_dir_entry *) p; + if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || + (rec_len % 4)) { + ext2fs_free_mem(&buf); + return (EXT2_ET_DIR_CORRUPTED); + } + p += rec_len; + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); + + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); + } + retval = io_channel_write_blk64(fs->io, block, 1, buf); + ext2fs_free_mem(&buf); + return retval; +#else + return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf); +#endif +} + +errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_write_dir_block3(fs, block, inbuf, flags); +} + +errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *inbuf) +{ + return ext2fs_write_dir_block3(fs, block, inbuf, 0); +} + diff --git a/lib/libext2fs/source/dirhash.c b/lib/libext2fs/source/dirhash.c new file mode 100644 index 0000000..a069706 --- /dev/null +++ b/lib/libext2fs/source/dirhash.c @@ -0,0 +1,257 @@ +/* + * dirhash.c -- Calculate the hash of a directory entry + * + * Copyright (c) 2001 Daniel Phillips + * + * Copyright (c) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Keyed 32-bit hash function using TEA in a Davis-Meyer function + * H0 = Key + * Hi = E Mi(Hi-1) + Hi-1 + * + * (see Applied Cryptography, 2nd edition, p448). + * + * Jeremy Fitzhardinge 1998 + * + * This code is made available under the terms of the GPL + */ +#define DELTA 0x9E3779B9 + +static void TEA_transform(__u32 buf[4], __u32 const in[]) +{ + __u32 sum = 0; + __u32 b0 = buf[0], b1 = buf[1]; + __u32 a = in[0], b = in[1], c = in[2], d = in[3]; + int n = 16; + + do { + sum += DELTA; + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); + } while(--n); + + buf[0] += b0; + buf[1] += b1; +} + +/* F, G and H are basic MD4 functions: selection, majority, parity */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The generic round function. The application is so specific that + * we don't bother protecting all the arguments with parens, as is generally + * good macro practice, in favor of extra legibility. + * Rotation is separate from addition to prevent recomputation + */ +#define ROUND(f, a, b, c, d, x, s) \ + (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) +#define K1 0 +#define K2 013240474631UL +#define K3 015666365641UL + +/* + * Basic cut-down MD4 transform. Returns only 32 bits of result. + */ +static void halfMD4Transform (__u32 buf[4], __u32 const in[]) +{ + __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ + ROUND(F, a, b, c, d, in[0] + K1, 3); + ROUND(F, d, a, b, c, in[1] + K1, 7); + ROUND(F, c, d, a, b, in[2] + K1, 11); + ROUND(F, b, c, d, a, in[3] + K1, 19); + ROUND(F, a, b, c, d, in[4] + K1, 3); + ROUND(F, d, a, b, c, in[5] + K1, 7); + ROUND(F, c, d, a, b, in[6] + K1, 11); + ROUND(F, b, c, d, a, in[7] + K1, 19); + + /* Round 2 */ + ROUND(G, a, b, c, d, in[1] + K2, 3); + ROUND(G, d, a, b, c, in[3] + K2, 5); + ROUND(G, c, d, a, b, in[5] + K2, 9); + ROUND(G, b, c, d, a, in[7] + K2, 13); + ROUND(G, a, b, c, d, in[0] + K2, 3); + ROUND(G, d, a, b, c, in[2] + K2, 5); + ROUND(G, c, d, a, b, in[4] + K2, 9); + ROUND(G, b, c, d, a, in[6] + K2, 13); + + /* Round 3 */ + ROUND(H, a, b, c, d, in[3] + K3, 3); + ROUND(H, d, a, b, c, in[7] + K3, 9); + ROUND(H, c, d, a, b, in[2] + K3, 11); + ROUND(H, b, c, d, a, in[6] + K3, 15); + ROUND(H, a, b, c, d, in[1] + K3, 3); + ROUND(H, d, a, b, c, in[5] + K3, 9); + ROUND(H, c, d, a, b, in[0] + K3, 11); + ROUND(H, b, c, d, a, in[4] + K3, 15); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#undef ROUND +#undef F +#undef G +#undef H +#undef K1 +#undef K2 +#undef K3 + +/* The old legacy hash */ +static ext2_dirhash_t dx_hack_hash (const char *name, int len, + int unsigned_flag) +{ + __u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; + const unsigned char *ucp = (const unsigned char *) name; + const signed char *scp = (const signed char *) name; + int c; + + while (len--) { + if (unsigned_flag) + c = (int) *ucp++; + else + c = (int) *scp++; + hash = hash1 + (hash0 ^ (c * 7152373)); + + if (hash & 0x80000000) hash -= 0x7fffffff; + hash1 = hash0; + hash0 = hash; + } + return (hash0 << 1); +} + +static void str2hashbuf(const char *msg, int len, __u32 *buf, int num, + int unsigned_flag) +{ + __u32 pad, val; + int i, c; + const unsigned char *ucp = (const unsigned char *) msg; + const signed char *scp = (const signed char *) msg; + + pad = (__u32)len | ((__u32)len << 8); + pad |= pad << 16; + + val = pad; + if (len > num*4) + len = num * 4; + for (i=0; i < len; i++) { + if ((i % 4) == 0) + val = pad; + if (unsigned_flag) + c = (int) ucp[i]; + else + c = (int) scp[i]; + + val = c + (val << 8); + if ((i % 4) == 3) { + *buf++ = val; + val = pad; + num--; + } + } + if (--num >= 0) + *buf++ = val; + while (--num >= 0) + *buf++ = pad; +} + +/* + * Returns the hash of a filename. If len is 0 and name is NULL, then + * this function can be used to test whether or not a hash version is + * supported. + * + * The seed is an 4 longword (32 bits) "secret" which can be used to + * uniquify a hash. If the seed is all zero's, then some default seed + * may be used. + * + * A particular hash version specifies whether or not the seed is + * represented, and whether or not the returned hash is 32 bits or 64 + * bits. 32 bit hashes will return 0 for the minor hash. + */ +errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash) +{ + __u32 hash; + __u32 minor_hash = 0; + const char *p; + int i; + __u32 in[8], buf[4]; + int unsigned_flag = 0; + + /* Initialize the default seed for the hash checksum functions */ + buf[0] = 0x67452301; + buf[1] = 0xefcdab89; + buf[2] = 0x98badcfe; + buf[3] = 0x10325476; + + /* Check to see if the seed is all zero's */ + if (seed) { + for (i=0; i < 4; i++) { + if (seed[i]) + break; + } + if (i < 4) + memcpy(buf, seed, sizeof(buf)); + } + + switch (version) { + case EXT2_HASH_LEGACY_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_LEGACY: + hash = dx_hack_hash(name, len, unsigned_flag); + break; + case EXT2_HASH_HALF_MD4_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_HALF_MD4: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 8, unsigned_flag); + halfMD4Transform(buf, in); + len -= 32; + p += 32; + } + minor_hash = buf[2]; + hash = buf[1]; + break; + case EXT2_HASH_TEA_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_TEA: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 4, unsigned_flag); + TEA_transform(buf, in); + len -= 16; + p += 16; + } + hash = buf[0]; + minor_hash = buf[1]; + break; + default: + *ret_hash = 0; + return EXT2_ET_DIRHASH_UNSUPP; + } + *ret_hash = hash & ~1; + if (ret_minor_hash) + *ret_minor_hash = minor_hash; + return 0; +} diff --git a/lib/libext2fs/source/disc_cache.c b/lib/libext2fs/source/disc_cache.c new file mode 100644 index 0000000..af3d8e2 --- /dev/null +++ b/lib/libext2fs/source/disc_cache.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "disc_cache.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +CACHE* _EXT2_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + CACHE* cache; + unsigned int i; + CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (CACHE*) mem_alloc (sizeof(CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (CACHE_ENTRY*) mem_alloc ( sizeof(CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + mem_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) _EXT2_cache_mem_align (32, sectorsPerPage * cache->sectorSize); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _EXT2_cache_destructor (CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + _EXT2_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + _EXT2_cache_mem_free (cache->cacheEntries[i].cache); + } + mem_free (cache->cacheEntries); + mem_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static CACHE_ENTRY* _EXT2_cache_getPage(CACHE *cache,sec_t sector) +{ + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static CACHE_ENTRY* _EXT2_cache_findPage(CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool _EXT2_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = _EXT2_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool _EXT2_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _EXT2_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool _EXT2_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_EXT2_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool _EXT2_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _EXT2_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _EXT2_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _EXT2_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool _EXT2_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _EXT2_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _EXT2_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = _EXT2_cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _EXT2_cache_flush (CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _EXT2_cache_invalidate (CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + _EXT2_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/lib/libext2fs/source/disc_cache.h b/lib/libext2fs/source/disc_cache.h new file mode 100644 index 0000000..50c0b6d --- /dev/null +++ b/lib/libext2fs/source/disc_cache.h @@ -0,0 +1,118 @@ +/* + CACHE.h + The CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _DISC_CACHE_H +#define _DISC_CACHE_H + +#include +#include +#include +#include +#include + +typedef struct +{ + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} CACHE_ENTRY; + +typedef struct +{ + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the CACHE +If the sector is not in the CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +/* +Write data to a sector in the CACHE +If the sector is not in the CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ + +/* +Write data to a sector in the CACHE, zeroing the sector first +If the sector is not in the CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ + +/* +Read several sectors from the CACHE +*/ +bool _EXT2_cache_readSectors (CACHE* DISC_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the CACHE +*/ +/* +Write a full sector to the CACHE +*/ +bool _EXT2_cache_writeSectors (CACHE* DISC_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the CACHE +*/ +bool _EXT2_cache_flush (CACHE* DISC_CACHE); + +/* +Clear out the contents of the CACHE without writing any dirty sectors first +*/ +void _EXT2_cache_invalidate (CACHE* DISC_CACHE); + +CACHE* _EXT2_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void _EXT2_cache_destructor (CACHE* DISC_CACHE); + +#endif // _CACHE_H + diff --git a/lib/libext2fs/source/dupfs.c b/lib/libext2fs/source/dupfs.c new file mode 100644 index 0000000..a9e2a97 --- /dev/null +++ b/lib/libext2fs/source/dupfs.c @@ -0,0 +1,96 @@ +/* + * dupfs.c --- duplicate a ext2 filesystem handle + * + * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) +{ + ext2_filsys fs; + errcode_t retval; + + EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + *fs = *src; + fs->device_name = 0; + fs->super = 0; + fs->orig_super = 0; + fs->group_desc = 0; + fs->inode_map = 0; + fs->block_map = 0; + fs->badblocks = 0; + fs->dblist = 0; + + io_channel_bumpcount(fs->io); + if (fs->icache) + fs->icache->refcount++; + + retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); + if (retval) + goto errout; + strcpy(fs->device_name, src->device_name); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); + if (retval) + goto errout; + memcpy(fs->super, src->super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto errout; + memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto errout; + memcpy(fs->group_desc, src->group_desc, + (size_t) fs->desc_blocks * fs->blocksize); + + if (src->inode_map) { + retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); + if (retval) + goto errout; + } + if (src->block_map) { + retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); + if (retval) + goto errout; + } + if (src->badblocks) { + retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); + if (retval) + goto errout; + } + if (src->dblist) { + retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); + if (retval) + goto errout; + } + *dest = fs; + return 0; +errout: + ext2fs_free(fs); + return retval; + +} + diff --git a/lib/libext2fs/source/e2image.h b/lib/libext2fs/source/e2image.h new file mode 100644 index 0000000..4de2c8d --- /dev/null +++ b/lib/libext2fs/source/e2image.h @@ -0,0 +1,51 @@ +/* + * e2image.h --- header file describing the ext2 image format + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +struct ext2_image_hdr { + __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ + char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ + char fs_hostname[64];/* Hostname of machine of image */ + char fs_netaddr[32]; /* Network address */ + __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ + __u32 fs_device; /* Device number of image */ + char fs_device_name[64]; /* Device name */ + char fs_uuid[16]; /* UUID of filesystem */ + __u32 fs_blocksize; /* Block size of the filesystem */ + __u32 fs_reserved[8]; + + __u32 image_device; /* Device number of image file */ + __u32 image_inode; /* Inode number of image file */ + __u32 image_time; /* Time of image creation */ + __u32 image_reserved[8]; + + __u32 offset_super; /* Byte offset of the sb and descriptors */ + __u32 offset_inode; /* Byte offset of the inode table */ + __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ + __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ + __u32 offset_reserved[8]; +}; + + + + + + + + + + + + + diff --git a/lib/libext2fs/source/e2p/e2p.h b/lib/libext2fs/source/e2p/e2p.h new file mode 100644 index 0000000..319c9bc --- /dev/null +++ b/lib/libext2fs/source/e2p/e2p.h @@ -0,0 +1,74 @@ +/* + * e2p.h --- header file for the e2p library + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include /* Needed by dirent.h on netbsd */ +#include +#include + +#include "../ext2_fs.h" + +#define E2P_FEATURE_COMPAT 0 +#define E2P_FEATURE_INCOMPAT 1 +#define E2P_FEATURE_RO_INCOMPAT 2 +#define E2P_FEATURE_TYPE_MASK 0x03 + +#define E2P_FEATURE_NEGATE_FLAG 0x80 + +#define E2P_FS_FEATURE 0 +#define E2P_JOURNAL_FEATURE 1 + +/* `options' for print_flags() */ + +#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ + + +int fgetflags (const char * name, unsigned long * flags); +int fgetversion (const char * name, unsigned long * version); +int fsetflags (const char * name, unsigned long flags); +int fsetversion (const char * name, unsigned long version); +int getflags (int fd, unsigned long * flags); +int getversion (int fd, unsigned long * version); +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private); +void list_super(struct ext2_super_block * s); +void list_super2(struct ext2_super_block * s, FILE *f); +void print_fs_errors (FILE * f, unsigned short errors); +void print_flags (FILE * f, unsigned long flags, unsigned options); +void print_fs_state (FILE * f, unsigned short state); +int setflags (int fd, unsigned long flags); +int setversion (int fd, unsigned long version); + +const char *e2p_feature2string(int compat, unsigned int mask); +const char *e2p_jrnl_feature2string(int compat, unsigned int mask); +int e2p_string2feature(char *string, int *compat, unsigned int *mask); +int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask); +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array); +int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, + __u32 *clear_ok_array, int *type_err, + unsigned int *mask_err); + +int e2p_is_null_uuid(void *uu); +void e2p_uuid_to_str(void *uu, char *out); +const char *e2p_uuid2str(void *uu); + +const char *e2p_hash2string(int num); +int e2p_string2hash(char *string); + +const char *e2p_mntopt2string(unsigned int mask); +int e2p_string2mntopt(char *string, unsigned int *mask); +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok); + +unsigned long parse_num_blocks(const char *arg, int log_block_size); +unsigned long long parse_num_blocks2(const char *arg, int log_block_size); + +char *e2p_os2string(int os_type); +int e2p_string2os(char *str); + +unsigned int e2p_percent(int percent, unsigned int base); diff --git a/lib/libext2fs/source/e2p/feature.c b/lib/libext2fs/source/e2p/feature.c new file mode 100644 index 0000000..4806be5 --- /dev/null +++ b/lib/libext2fs/source/e2p/feature.c @@ -0,0 +1,385 @@ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" +#include +#include + +struct feature { + int compat; + unsigned int mask; + const char *string; +}; + +static struct feature feature_list[] = { + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC, + "dir_prealloc" }, + { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL, + "has_journal" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, + "imagic_inodes" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, + "ext_attr" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, + "dir_index" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE, + "resize_inode" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG, + "lazy_bg" }, + + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, + "sparse_super" }, + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE, + "large_file" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HUGE_FILE, + "huge_file" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, + "uninit_bg" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, + "uninit_groups" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_DIR_NLINK, + "dir_nlink" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE, + "extra_isize" }, + + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, + "compression" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE, + "filetype" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER, + "needs_recovery" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV, + "journal_dev" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, + "extent" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, + "extents" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG, + "meta_bg" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_64BIT, + "64bit" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG, + "flex_bg"}, + { 0, 0, 0 }, +}; + +static struct feature jrnl_feature_list[] = { + { E2P_FEATURE_COMPAT, JFS_FEATURE_COMPAT_CHECKSUM, + "journal_checksum" }, + + { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE, + "journal_incompat_revoke" }, + { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT, + "journal_async_commit" }, + { 0, 0, 0 }, +}; + +const char *e2p_feature2string(int compat, unsigned int mask) +{ + struct feature *f; + static char buf[20]; + char fchar; + int fnum; + + for (f = feature_list; f->string; f++) { + if ((compat == f->compat) && + (mask == f->mask)) + return f->string; + } + switch (compat) { + case E2P_FEATURE_COMPAT: + fchar = 'C'; + break; + case E2P_FEATURE_INCOMPAT: + fchar = 'I'; + break; + case E2P_FEATURE_RO_INCOMPAT: + fchar = 'R'; + break; + default: + fchar = '?'; + break; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "FEATURE_%c%d", fchar, fnum); + return buf; +} + +int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) +{ + struct feature *f; + char *eptr; + int num; + + for (f = feature_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *compat_type = f->compat; + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "FEATURE_", 8)) + return 1; + + switch (string[8]) { + case 'c': + case 'C': + *compat_type = E2P_FEATURE_COMPAT; + break; + case 'i': + case 'I': + *compat_type = E2P_FEATURE_INCOMPAT; + break; + case 'r': + case 'R': + *compat_type = E2P_FEATURE_RO_INCOMPAT; + break; + default: + return 1; + } + if (string[9] == 0) + return 1; + num = strtol(string+9, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +const char *e2p_jrnl_feature2string(int compat, unsigned int mask) +{ + struct feature *f; + static char buf[20]; + char fchar; + int fnum; + + for (f = jrnl_feature_list; f->string; f++) { + if ((compat == f->compat) && + (mask == f->mask)) + return f->string; + } + switch (compat) { + case E2P_FEATURE_COMPAT: + fchar = 'C'; + break; + case E2P_FEATURE_INCOMPAT: + fchar = 'I'; + break; + case E2P_FEATURE_RO_INCOMPAT: + fchar = 'R'; + break; + default: + fchar = '?'; + break; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "FEATURE_%c%d", fchar, fnum); + return buf; +} + +int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask) +{ + struct feature *f; + char *eptr; + int num; + + for (f = jrnl_feature_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *compat_type = f->compat; + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "FEATURE_", 8)) + return 1; + + switch (string[8]) { + case 'c': + case 'C': + *compat_type = E2P_FEATURE_COMPAT; + break; + case 'i': + case 'I': + *compat_type = E2P_FEATURE_INCOMPAT; + break; + case 'r': + case 'R': + *compat_type = E2P_FEATURE_RO_INCOMPAT; + break; + default: + return 1; + } + if (string[9] == 0) + return 1; + num = strtol(string+9, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} +static char *skip_over_blanks(char *cp) +{ + while (*cp && isspace((int)*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace((int)*cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a feature set array as requested by the user. The ok_array, + * if set, allows the application to limit what features the user is + * allowed to set or clear using this function. If clear_ok_array is set, + * then use it tell whether or not it is OK to clear a filesystem feature. + */ +int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, + __u32 *clear_ok_array, int *type_err, + unsigned int *mask_err) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + int compat_type; + int rc = 0; + + if (!clear_ok_array) + clear_ok_array = ok_array; + + if (type_err) + *type_err = 0; + if (mask_err) + *mask_err = 0; + + buf = malloc(strlen(str)+1); + if (!buf) + return 1; + strcpy(buf, str); + for (cp = buf; cp && *cp; cp = next ? next+1 : 0) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + + if (*next == 0) + next = 0; + else + *next = 0; + + if ((strcasecmp(cp, "none") == 0) || + (strcasecmp(cp, "clear") == 0)) { + compat_array[0] = 0; + compat_array[1] = 0; + compat_array[2] = 0; + continue; + } + + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2feature(cp, &compat_type, &mask)) { + rc = 1; + break; + } + if (neg) { + if (clear_ok_array && + !(clear_ok_array[compat_type] & mask)) { + rc = 1; + if (type_err) + *type_err = (compat_type | + E2P_FEATURE_NEGATE_FLAG); + if (mask_err) + *mask_err = mask; + break; + } + compat_array[compat_type] &= ~mask; + } else { + if (ok_array && !(ok_array[compat_type] & mask)) { + rc = 1; + if (type_err) + *type_err = compat_type; + if (mask_err) + *mask_err = mask; + break; + } + compat_array[compat_type] |= mask; + } + } + free(buf); + return rc; +} + +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) +{ + return e2p_edit_feature2(str, compat_array, ok_array, 0, 0, 0); +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + int compat, compat2, i; + unsigned int mask, mask2; + const char *str; + struct feature *f; + + for (i = 0; i < 2; i++) { + if (i == 0) { + f = feature_list; + printf("Feature list:\n"); + } else { + printf("\nJournal feature list:\n"); + f = jrnl_feature_list; + } + for (; f->string; f++) { + if (i == 0) { + e2p_string2feature((char *)f->string, &compat, + &mask); + str = e2p_feature2string(compat, mask); + } else { + e2p_jrnl_string2feature((char *)f->string, + &compat, &mask); + str = e2p_jrnl_feature2string(compat, mask); + } + + printf("\tCompat = %d, Mask = %u, %s\n", + compat, mask, f->string); + if (strcmp(f->string, str)) { + if (e2p_string2feature((char *) str, &compat2, + &mask2) || + (compat2 != compat) || + (mask2 != mask)) { + fprintf(stderr, "Failure!\n"); + exit(1); + } + } + } + } + exit(0); +} +#endif diff --git a/lib/libext2fs/source/e2p/fgetflags.c b/lib/libext2fs/source/e2p/fgetflags.c new file mode 100644 index 0000000..282be32 --- /dev/null +++ b/lib/libext2fs/source/e2p/fgetflags.c @@ -0,0 +1,95 @@ +/* + * fgetflags.c - Get a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fgetflags (const char * name, unsigned long * flags) +{ +#if HAVE_STAT_FLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) + struct stat buf; + if (stat (name, &buf) == -1) + return -1; + + *flags = 0; +#ifdef UF_IMMUTABLE + if (buf.st_flags & UF_IMMUTABLE) + *flags |= EXT2_IMMUTABLE_FL; +#endif +#ifdef UF_APPEND + if (buf.st_flags & UF_APPEND) + *flags |= EXT2_APPEND_FL; +#endif +#ifdef UF_NODUMP + if (buf.st_flags & UF_NODUMP) + *flags |= EXT2_NODUMP_FL; +#endif + + return 0; +#else +#if HAVE_EXT2_IOCTLS + int fd, r, f, save_errno = 0; + + if (!lstat(name, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + goto notsupp; + } +#if !APPLE_DARWIN + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + if (r == -1) + save_errno = errno; + *flags = f; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + f = -1; + save_errno = syscall(SYS_fsctl, name, EXT2_IOC_GETFLAGS, &f, 0); + *flags = f; + return (save_errno); +#endif +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/source/e2p/fgetversion.c b/lib/libext2fs/source/e2p/fgetversion.c new file mode 100644 index 0000000..97519d7 --- /dev/null +++ b/lib/libext2fs/source/e2p/fgetversion.c @@ -0,0 +1,69 @@ +/* + * fgetversion.c - Get a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fgetversion (const char * name, unsigned long * version) +{ +#if HAVE_EXT2_IOCTLS +#if !APPLE_DARWIN + int fd, r, ver, save_errno = 0; + + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); + if (r == -1) + save_errno = errno; + *version = ver; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + int ver=-1, err; + err = syscall(SYS_fsctl, name, EXT2_IOC_GETVERSION, &ver, 0); + *version = ver; + return(err); +#endif +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/source/e2p/fsetflags.c b/lib/libext2fs/source/e2p/fsetflags.c new file mode 100644 index 0000000..0f39ee9 --- /dev/null +++ b/lib/libext2fs/source/e2p/fsetflags.c @@ -0,0 +1,100 @@ +/* + * fsetflags.c - Set a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +/* + * Deal with lame glibc's that define this function without actually + * implementing it. Can you say "attractive nuisance", boys and girls? + * I knew you could! + */ +#ifdef __linux__ +#undef HAVE_CHFLAGS +#endif + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fsetflags (const char * name, unsigned long flags) +{ +#if HAVE_CHFLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) + struct stat buf; + unsigned long bsd_flags = 0; + +#ifdef UF_IMMUTABLE + if (flags & EXT2_IMMUTABLE_FL) + bsd_flags |= UF_IMMUTABLE; +#endif +#ifdef UF_APPEND + if (flags & EXT2_APPEND_FL) + bsd_flags |= UF_APPEND; +#endif +#ifdef UF_NODUMP + if (flags & EXT2_NODUMP_FL) + bsd_flags |= UF_NODUMP; +#endif + + return chflags (name, bsd_flags); +#else +#if HAVE_EXT2_IOCTLS + int fd, r, f, save_errno = 0; + + if (!lstat(name, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + goto notsupp; + } +#if !APPLE_DARWIN + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + f = (int) flags; + r = ioctl (fd, EXT2_IOC_SETFLAGS, &f); + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; +#else + f = (int) flags; + return syscall(SYS_fsctl, name, EXT2_IOC_SETFLAGS, &f, 0); +#endif + return r; +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/source/e2p/fsetversion.c b/lib/libext2fs/source/e2p/fsetversion.c new file mode 100644 index 0000000..ee26f91 --- /dev/null +++ b/lib/libext2fs/source/e2p/fsetversion.c @@ -0,0 +1,67 @@ +/* + * fsetversion.c - Set a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fsetversion (const char * name, unsigned long version) +{ +#if HAVE_EXT2_IOCTLS +#if !APPLE_DARWIN + int fd, r, ver, save_errno = 0; + + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + ver = (int) version; + r = ioctl (fd, EXT2_IOC_SETVERSION, &ver); + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + int ver = (int)version; + return syscall(SYS_fsctl, name, EXT2_IOC_SETVERSION, &ver, 0); +#endif +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/source/e2p/getflags.c b/lib/libext2fs/source/e2p/getflags.c new file mode 100644 index 0000000..0cc768a --- /dev/null +++ b/lib/libext2fs/source/e2p/getflags.c @@ -0,0 +1,66 @@ +/* + * getflags.c - Get a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int getflags (int fd, unsigned long * flags) +{ +#if HAVE_STAT_FLAGS + struct stat buf; + if (fstat (fd, &buf) == -1) + return -1; + + *flags = 0; +#ifdef UF_IMMUTABLE + if (buf.st_flags & UF_IMMUTABLE) + *flags |= EXT2_IMMUTABLE_FL; +#endif +#ifdef UF_APPEND + if (buf.st_flags & UF_APPEND) + *flags |= EXT2_APPEND_FL; +#endif +#ifdef UF_NODUMP + if (buf.st_flags & UF_NODUMP) + *flags |= EXT2_NODUMP_FL; +#endif + + return 0; +#else +#if HAVE_EXT2_IOCTLS + int r, f; + + if (!fstat(fd, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) + goto notsupp; + r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + *flags = f; + return r; +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/source/e2p/getversion.c b/lib/libext2fs/source/e2p/getversion.c new file mode 100644 index 0000000..06adf6d --- /dev/null +++ b/lib/libext2fs/source/e2p/getversion.c @@ -0,0 +1,41 @@ +/* + * getversion.c - Get a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int getversion (int fd, unsigned long * version) +{ +#if HAVE_EXT2_IOCTLS + int r, ver; + + r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); + *version = ver; + return 0; +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/source/e2p/hashstr.c b/lib/libext2fs/source/e2p/hashstr.c new file mode 100644 index 0000000..5ee6237 --- /dev/null +++ b/lib/libext2fs/source/e2p/hashstr.c @@ -0,0 +1,71 @@ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct hash { + int num; + const char *string; +}; + +static struct hash hash_list[] = { + { EXT2_HASH_LEGACY, "legacy" }, + { EXT2_HASH_HALF_MD4, "half_md4" }, + { EXT2_HASH_TEA, "tea" }, + { 0, 0 }, +}; + +const char *e2p_hash2string(int num) +{ + struct hash *p; + static char buf[20]; + + for (p = hash_list; p->string; p++) { + if (num == p->num) + return p->string; + } + sprintf(buf, "HASHALG_%d", num); + return buf; +} + +/* + * Returns the hash algorithm, or -1 on error + */ +int e2p_string2hash(char *string) +{ + struct hash *p; + char *eptr; + int num; + + for (p = hash_list; p->string; p++) { + if (!strcasecmp(string, p->string)) { + return p->num; + } + } + if (strncasecmp(string, "HASHALG_", 8)) + return -1; + + if (string[8] == 0) + return -1; + num = strtol(string+8, &eptr, 10); + if (num > 255 || num < 0) + return -1; + if (*eptr) + return -1; + return num; +} + diff --git a/lib/libext2fs/source/e2p/iod.c b/lib/libext2fs/source/e2p/iod.c new file mode 100644 index 0000000..c53aafe --- /dev/null +++ b/lib/libext2fs/source/e2p/iod.c @@ -0,0 +1,75 @@ +/* + * iod.c - Iterate a function on each entry of a directory + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include "e2p.h" +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private) +{ + DIR * dir; + struct dirent *de, *dep; + int max_len = -1, len, ret = 0; + +#if HAVE_PATHCONF && defined(_PC_NAME_MAX) + max_len = pathconf(dir_name, _PC_NAME_MAX); +#endif + if (max_len == -1) { +#ifdef _POSIX_NAME_MAX + max_len = _POSIX_NAME_MAX; +#else +#ifdef NAME_MAX + max_len = NAME_MAX; +#else + max_len = 256; +#endif /* NAME_MAX */ +#endif /* _POSIX_NAME_MAX */ + } + max_len += sizeof(struct dirent); + + de = malloc(max_len+1); + if (!de) + return -1; + memset(de, 0, max_len+1); + + dir = opendir (dir_name); + if (dir == NULL) { + free(de); + return -1; + } + while ((dep = readdir (dir))) { +#ifdef HAVE_RECLEN_DIRENT + len = dep->d_reclen; + if (len > max_len) + len = max_len; +#else + len = sizeof(struct dirent); +#endif + memcpy(de, dep, len); + if ((*func)(dir_name, de, private)) + ret++; + } + free(de); + closedir(dir); + return ret; +} diff --git a/lib/libext2fs/source/e2p/ls.c b/lib/libext2fs/source/e2p/ls.c new file mode 100644 index 0000000..20908a6 --- /dev/null +++ b/lib/libext2fs/source/e2p/ls.c @@ -0,0 +1,403 @@ +/* + * ls.c - List the contents of an ext2fs superblock + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Copyright (C) 1995, 1996, 1997 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "e2p.h" + +static void print_user (unsigned short uid, FILE *f) +{ + struct passwd *pw; + + fprintf(f, "%u ", uid); + pw = getpwuid (uid); + if (pw == NULL) + fprintf(f, "(user unknown)\n"); + else + fprintf(f, "(user %s)\n", pw->pw_name); +} + +static void print_group (unsigned short gid, FILE *f) +{ + struct group *gr; + + fprintf(f, "%u ", gid); + gr = getgrgid (gid); + if (gr == NULL) + fprintf(f, "(group unknown)\n"); + else + fprintf(f, "(group %s)\n", gr->gr_name); +} + +#define MONTH_INT (86400 * 30) +#define WEEK_INT (86400 * 7) +#define DAY_INT (86400) +#define HOUR_INT (60 * 60) +#define MINUTE_INT (60) + +static const char *interval_string(unsigned int secs) +{ + static char buf[256], tmp[80]; + int hr, min, num; + + buf[0] = 0; + + if (secs == 0) + return ""; + + if (secs >= MONTH_INT) { + num = secs / MONTH_INT; + secs -= num*MONTH_INT; + sprintf(buf, "%d month%s", num, (num>1) ? "s" : ""); + } + if (secs >= WEEK_INT) { + num = secs / WEEK_INT; + secs -= num*WEEK_INT; + sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs >= DAY_INT) { + num = secs / DAY_INT; + secs -= num*DAY_INT; + sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs > 0) { + hr = secs / HOUR_INT; + secs -= hr*HOUR_INT; + min = secs / MINUTE_INT; + secs -= min*MINUTE_INT; + sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "", + hr, min, secs); + strcat(buf, tmp); + } + return buf; +} + +static void print_features(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, j, printed=0; + __u32 *mask = &s->s_feature_compat, m; + + fprintf(f, "Filesystem features: "); + for (i=0; i <3; i++,mask++) { + for (j=0,m=1; j < 32; j++, m<<=1) { + if (*mask & m) { + fprintf(f, " %s", e2p_feature2string(i, m)); + printed++; + } + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + +static void print_mntopts(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, printed=0; + __u32 mask = s->s_default_mount_opts, m; + + fprintf(f, "Default mount options: "); + if (mask & EXT3_DEFM_JMODE) { + fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE)); + printed++; + } + for (i=0,m=1; i < 32; i++, m<<=1) { + if (m & EXT3_DEFM_JMODE) + continue; + if (mask & m) { + fprintf(f, " %s", e2p_mntopt2string(m)); + printed++; + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + +static void print_super_flags(struct ext2_super_block * s, FILE *f) +{ + int flags_found = 0; + + if (s->s_flags == 0) + return; + + fputs("Filesystem flags: ", f); + if (s->s_flags & EXT2_FLAGS_SIGNED_HASH) { + fputs("signed_directory_hash ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_UNSIGNED_HASH) { + fputs("unsigned_directory_hash ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_TEST_FILESYS) { + fputs("test_filesystem ", f); + flags_found++; + } + if (flags_found) + fputs("\n", f); + else + fputs("(none)\n", f); +} + +static __u64 e2p_blocks_count(struct ext2_super_block *super) +{ + return super->s_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_blocks_count_hi << 32 : 0); +} + +static __u64 e2p_r_blocks_count(struct ext2_super_block *super) +{ + return super->s_r_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_r_blocks_count_hi << 32 : 0); +} + +static __u64 e2p_free_blocks_count(struct ext2_super_block *super) +{ + return super->s_free_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_free_blocks_hi << 32 : 0); +} + +#ifndef EXT2_INODE_SIZE +#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode) +#endif + +#ifndef EXT2_GOOD_OLD_REV +#define EXT2_GOOD_OLD_REV 0 +#endif + +void list_super2(struct ext2_super_block * sb, FILE *f) +{ + int inode_blocks_per_group; + char buf[80], *str; + time_t tm; + + inode_blocks_per_group = (((sb->s_inodes_per_group * + EXT2_INODE_SIZE(sb)) + + EXT2_BLOCK_SIZE(sb) - 1) / + EXT2_BLOCK_SIZE(sb)); + if (sb->s_volume_name[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); + } else + strcpy(buf, ""); + fprintf(f, "Filesystem volume name: %s\n", buf); + if (sb->s_last_mounted[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); + } else + strcpy(buf, ""); + fprintf(f, "Last mounted on: %s\n", buf); + fprintf(f, "Filesystem UUID: %s\n", e2p_uuid2str(sb->s_uuid)); + fprintf(f, "Filesystem magic number: 0x%04X\n", sb->s_magic); + fprintf(f, "Filesystem revision #: %d", sb->s_rev_level); + if (sb->s_rev_level == EXT2_GOOD_OLD_REV) { + fprintf(f, " (original)\n"); +#ifdef EXT2_DYNAMIC_REV + } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) { + fprintf(f, " (dynamic)\n"); +#endif + } else + fprintf(f, " (unknown)\n"); + print_features(sb, f); + print_super_flags(sb, f); + print_mntopts(sb, f); + if (sb->s_mount_opts[0]) + fprintf(f, "Mount options: %s\n", sb->s_mount_opts); + fprintf(f, "Filesystem state: "); + print_fs_state (f, sb->s_state); + fprintf(f, "\n"); + fprintf(f, "Errors behavior: "); + print_fs_errors(f, sb->s_errors); + fprintf(f, "\n"); + str = e2p_os2string(sb->s_creator_os); + fprintf(f, "Filesystem OS type: %s\n", str); + free(str); + fprintf(f, "Inode count: %u\n", sb->s_inodes_count); + fprintf(f, "Block count: %llu\n", e2p_blocks_count(sb)); + fprintf(f, "Reserved block count: %llu\n", e2p_r_blocks_count(sb)); + fprintf(f, "Free blocks: %llu\n", e2p_free_blocks_count(sb)); + fprintf(f, "Free inodes: %u\n", sb->s_free_inodes_count); + fprintf(f, "First block: %u\n", sb->s_first_data_block); + fprintf(f, "Block size: %u\n", EXT2_BLOCK_SIZE(sb)); + fprintf(f, "Fragment size: %u\n", EXT2_FRAG_SIZE(sb)); + if (sb->s_reserved_gdt_blocks) + fprintf(f, "Reserved GDT blocks: %u\n", + sb->s_reserved_gdt_blocks); + fprintf(f, "Blocks per group: %u\n", sb->s_blocks_per_group); + fprintf(f, "Fragments per group: %u\n", sb->s_frags_per_group); + fprintf(f, "Inodes per group: %u\n", sb->s_inodes_per_group); + fprintf(f, "Inode blocks per group: %u\n", inode_blocks_per_group); + if (sb->s_raid_stride) + fprintf(f, "RAID stride: %u\n", + sb->s_raid_stride); + if (sb->s_raid_stripe_width) + fprintf(f, "RAID stripe width: %u\n", + sb->s_raid_stripe_width); + if (sb->s_first_meta_bg) + fprintf(f, "First meta block group: %u\n", + sb->s_first_meta_bg); + if (sb->s_log_groups_per_flex) + fprintf(f, "Flex block group size: %u\n", + 1 << sb->s_log_groups_per_flex); + if (sb->s_mkfs_time) { + tm = sb->s_mkfs_time; + fprintf(f, "Filesystem created: %s", ctime(&tm)); + } + tm = sb->s_mtime; + fprintf(f, "Last mount time: %s", + sb->s_mtime ? ctime(&tm) : "n/a\n"); + tm = sb->s_wtime; + fprintf(f, "Last write time: %s", ctime(&tm)); + fprintf(f, "Mount count: %u\n", sb->s_mnt_count); + fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count); + tm = sb->s_lastcheck; + fprintf(f, "Last checked: %s", ctime(&tm)); + fprintf(f, "Check interval: %u (%s)\n", sb->s_checkinterval, + interval_string(sb->s_checkinterval)); + if (sb->s_checkinterval) + { + time_t next; + + next = sb->s_lastcheck + sb->s_checkinterval; + fprintf(f, "Next check after: %s", ctime(&next)); + } +#define POW2(x) ((__u64) 1 << (x)) + if (sb->s_kbytes_written) { + fprintf(f, "Lifetime writes: "); + if (sb->s_kbytes_written < POW2(13)) + fprintf(f, "%llu kB\n", sb->s_kbytes_written); + else if (sb->s_kbytes_written < POW2(23)) + fprintf(f, "%llu MB\n", + (sb->s_kbytes_written + POW2(9)) >> 10); + else if (sb->s_kbytes_written < POW2(33)) + fprintf(f, "%llu GB\n", + (sb->s_kbytes_written + POW2(19)) >> 20); + else if (sb->s_kbytes_written < POW2(43)) + fprintf(f, "%llu TB\n", + (sb->s_kbytes_written + POW2(29)) >> 30); + else + fprintf(f, "%llu PB\n", + (sb->s_kbytes_written + POW2(39)) >> 40); + } + fprintf(f, "Reserved blocks uid: "); + print_user(sb->s_def_resuid, f); + fprintf(f, "Reserved blocks gid: "); + print_group(sb->s_def_resgid, f); + if (sb->s_rev_level >= EXT2_DYNAMIC_REV) { + fprintf(f, "First inode: %d\n", sb->s_first_ino); + fprintf(f, "Inode size: %d\n", sb->s_inode_size); + if (sb->s_min_extra_isize) + fprintf(f, "Required extra isize: %d\n", + sb->s_min_extra_isize); + if (sb->s_want_extra_isize) + fprintf(f, "Desired extra isize: %d\n", + sb->s_want_extra_isize); + } + if (!e2p_is_null_uuid(sb->s_journal_uuid)) + fprintf(f, "Journal UUID: %s\n", + e2p_uuid2str(sb->s_journal_uuid)); + if (sb->s_journal_inum) + fprintf(f, "Journal inode: %u\n", + sb->s_journal_inum); + if (sb->s_journal_dev) + fprintf(f, "Journal device: 0x%04x\n", + sb->s_journal_dev); + if (sb->s_last_orphan) + fprintf(f, "First orphan inode: %u\n", + sb->s_last_orphan); + if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || + sb->s_def_hash_version) + fprintf(f, "Default directory hash: %s\n", + e2p_hash2string(sb->s_def_hash_version)); + if (!e2p_is_null_uuid(sb->s_hash_seed)) + fprintf(f, "Directory Hash Seed: %s\n", + e2p_uuid2str(sb->s_hash_seed)); + if (sb->s_jnl_backup_type) { + fprintf(f, "Journal backup: "); + switch (sb->s_jnl_backup_type) { + case 1: + fprintf(f, "inode blocks\n"); + break; + default: + fprintf(f, "type %u\n", sb->s_jnl_backup_type); + } + } + if (sb->s_snapshot_inum) { + fprintf(f, "Snapshot inode: %u\n", + sb->s_snapshot_inum); + fprintf(f, "Snapshot ID: %u\n", + sb->s_snapshot_id); + fprintf(f, "Snapshot reserved blocks: %llu\n", + sb->s_snapshot_r_blocks_count); + } + if (sb->s_snapshot_list) + fprintf(f, "Snapshot list head: %u\n", + sb->s_snapshot_list); + if (sb->s_error_count) + fprintf(f, "FS Error count: %u\n", + sb->s_error_count); + if (sb->s_first_error_time) { + tm = sb->s_first_error_time; + fprintf(f, "First error time: %s", ctime(&tm)); + memset(buf, 0, sizeof(buf)); + strncpy(buf, (char *)sb->s_first_error_func, + sizeof(sb->s_first_error_func)); + fprintf(f, "First error function: %s\n", buf); + fprintf(f, "First error line #: %u\n", + sb->s_first_error_line); + fprintf(f, "First error inode #: %u\n", + sb->s_first_error_ino); + fprintf(f, "First error block #: %llu\n", + sb->s_first_error_block); + } + if (sb->s_last_error_time) { + tm = sb->s_last_error_time; + fprintf(f, "Last error time: %s", ctime(&tm)); + memset(buf, 0, sizeof(buf)); + strncpy(buf, (char *) sb->s_last_error_func, + sizeof(sb->s_last_error_func)); + fprintf(f, "Last error function: %s\n", buf); + fprintf(f, "Last error line #: %u\n", + sb->s_last_error_line); + fprintf(f, "Last error inode #: %u\n", + sb->s_last_error_ino); + fprintf(f, "Last error block #: %llu\n", + sb->s_last_error_block); + } +} + +void list_super (struct ext2_super_block * s) +{ + list_super2(s, stdout); +} + diff --git a/lib/libext2fs/source/e2p/mntopts.c b/lib/libext2fs/source/e2p/mntopts.c new file mode 100644 index 0000000..0903ad5 --- /dev/null +++ b/lib/libext2fs/source/e2p/mntopts.c @@ -0,0 +1,147 @@ +/* + * mountopts.c --- convert between default mount options and strings + * + * Copyright (C) 2002 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct mntopt { + unsigned int mask; + const char *string; +}; + +static struct mntopt mntopt_list[] = { + { EXT2_DEFM_DEBUG, "debug" }, + { EXT2_DEFM_BSDGROUPS, "bsdgroups" }, + { EXT2_DEFM_XATTR_USER, "user_xattr" }, + { EXT2_DEFM_ACL, "acl" }, + { EXT2_DEFM_UID16, "uid16" }, + { EXT3_DEFM_JMODE_DATA, "journal_data" }, + { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" }, + { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" }, + { EXT4_DEFM_NOBARRIER, "nobarrier" }, + { EXT4_DEFM_BLOCK_VALIDITY, "block_validity" }, + { EXT4_DEFM_DISCARD, "discard"}, + { EXT4_DEFM_NODELALLOC, "nodelalloc"}, + { 0, 0 }, +}; + +const char *e2p_mntopt2string(unsigned int mask) +{ + struct mntopt *f; + static char buf[20]; + int fnum; + + for (f = mntopt_list; f->string; f++) { + if (mask == f->mask) + return f->string; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "MNTOPT_%d", fnum); + return buf; +} + +int e2p_string2mntopt(char *string, unsigned int *mask) +{ + struct mntopt *f; + char *eptr; + int num; + + for (f = mntopt_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "MNTOPT_", 8)) + return 1; + + if (string[8] == 0) + return 1; + num = strtol(string+8, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +static char *skip_over_blanks(char *cp) +{ + while (*cp && isspace((int) *cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace((int) *cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a mntopt set array as requested by the user. The ok + * parameter, if non-zero, allows the application to limit what + * mntopts the user is allowed to set or clear using this function. + */ +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + int rc = 0; + + buf = malloc(strlen(str)+1); + if (!buf) + return 1; + strcpy(buf, str); + cp = buf; + while (cp && *cp) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + if (*next == 0) + next = 0; + else + *next = 0; + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2mntopt(cp, &mask)) { + rc = 1; + break; + } + if (ok && !(ok & mask)) { + rc = 1; + break; + } + if (mask & EXT3_DEFM_JMODE) + *mntopts &= ~EXT3_DEFM_JMODE; + if (neg) + *mntopts &= ~mask; + else + *mntopts |= mask; + cp = next ? next+1 : 0; + } + free(buf); + return rc; +} diff --git a/lib/libext2fs/source/e2p/ostype.c b/lib/libext2fs/source/e2p/ostype.c new file mode 100644 index 0000000..978315b --- /dev/null +++ b/lib/libext2fs/source/e2p/ostype.c @@ -0,0 +1,77 @@ +/* + * getostype.c - Get the Filesystem OS type + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" +#include +#include + +static const char *os_tab[] = + { "Linux", + "Hurd", + "Masix", + "FreeBSD", + "Lites", + 0 }; + +/* + * Convert an os_type to a string + */ +char *e2p_os2string(int os_type) +{ + const char *os; + char *ret; + + if (os_type <= EXT2_OS_LITES) + os = os_tab[os_type]; + else + os = "(unknown os)"; + + ret = malloc(strlen(os)+1); + if (ret) + strcpy(ret, os); + return ret; +} + +/* + * Convert an os_type to a string + */ +int e2p_string2os(char *str) +{ + const char **cpp; + int i = 0; + + for (cpp = os_tab; *cpp; cpp++, i++) { + if (!strcasecmp(str, *cpp)) + return i; + } + return -1; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + char *s; + int i, os; + + for (i=0; i <= EXT2_OS_LITES; i++) { + s = e2p_os2string(i); + os = e2p_string2os(s); + printf("%d: %s (%d)\n", i, s, os); + if (i != os) { + fprintf(stderr, "Failure!\n"); + exit(1); + } + } + exit(0); +} +#endif + + diff --git a/lib/libext2fs/source/e2p/parse_num.c b/lib/libext2fs/source/e2p/parse_num.c new file mode 100644 index 0000000..83a329a --- /dev/null +++ b/lib/libext2fs/source/e2p/parse_num.c @@ -0,0 +1,71 @@ +/* + * parse_num.c - Parse the number of blocks + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" + +#include + +unsigned long long parse_num_blocks2(const char *arg, int log_block_size) +{ + char *p; + unsigned long long num; + + num = strtoull(arg, &p, 0); + + if (p[0] && p[1]) + return 0; + + switch (*p) { /* Using fall-through logic */ + case 'T': case 't': + num <<= 10; + case 'G': case 'g': + num <<= 10; + case 'M': case 'm': + num <<= 10; + case 'K': case 'k': + num >>= log_block_size; + break; + case 's': + num >>= (1+log_block_size); + break; + case '\0': + break; + default: + return 0; + } + return num; +} + +unsigned long parse_num_blocks(const char *arg, int log_block_size) +{ + return parse_num_blocks2(arg, log_block_size); +} + +#ifdef DEBUG +#include +#include + +main(int argc, char **argv) +{ + unsigned long num; + int log_block_size = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s arg\n", argv[0]); + exit(1); + } + + num = parse_num_blocks(argv[1], log_block_size); + + printf("Parsed number: %lu\n", num); + exit(0); +} +#endif diff --git a/lib/libext2fs/source/e2p/pe.c b/lib/libext2fs/source/e2p/pe.c new file mode 100644 index 0000000..78c8099 --- /dev/null +++ b/lib/libext2fs/source/e2p/pe.c @@ -0,0 +1,39 @@ +/* + * pe.c - Print a second extended filesystem errors behavior + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 94/01/09 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_errors (FILE * f, unsigned short errors) +{ + switch (errors) + { + case EXT2_ERRORS_CONTINUE: + fprintf (f, "Continue"); + break; + case EXT2_ERRORS_RO: + fprintf (f, "Remount read-only"); + break; + case EXT2_ERRORS_PANIC: + fprintf (f, "Panic"); + break; + default: + fprintf (f, "Unknown (continue)"); + } +} diff --git a/lib/libext2fs/source/e2p/percent.c b/lib/libext2fs/source/e2p/percent.c new file mode 100644 index 0000000..cfa7046 --- /dev/null +++ b/lib/libext2fs/source/e2p/percent.c @@ -0,0 +1,66 @@ +/* + * percent.c - Take percentage of a number + * + * Copyright (C) 2006 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" + +#include + +/* + * We work really hard to calculate this accurately, while avoiding + * an overflow. "Is there a hyphen in anal-retentive?" :-) + */ +unsigned int e2p_percent(int percent, unsigned int base) +{ + unsigned int mask = ~((1 << (sizeof(unsigned int) - 1) * 8) - 1); + + if (!percent) + return 0; + if (100 % percent == 0) + return base / (100 / percent); + if (mask & base) + return (base / 100) * percent; + return base * percent / 100; +} + +#ifdef DEBUG +#include +#include + +main(int argc, char **argv) +{ + unsigned int base; + int percent; + char *p; + int log_block_size = 0; + + if (argc != 3) { + fprintf(stderr, "Usage: %s percent base\n", argv[0]); + exit(1); + } + + percent = strtoul(argv[1], &p, 0); + if (p[0] && p[1]) { + fprintf(stderr, "Bad percent: %s\n", argv[1]); + exit(1); + } + + base = strtoul(argv[2], &p, 0); + if (p[0] && p[1]) { + fprintf(stderr, "Bad base: %s\n", argv[2]); + exit(1); + } + + printf("%d percent of %u is %u.\n", percent, base, + e2p_percent(percent, base)); + + exit(0); +} +#endif diff --git a/lib/libext2fs/source/e2p/pf.c b/lib/libext2fs/source/e2p/pf.c new file mode 100644 index 0000000..f34a5cc --- /dev/null +++ b/lib/libext2fs/source/e2p/pf.c @@ -0,0 +1,78 @@ +/* + * pf.c - Print file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include + +#include "e2p.h" + +struct flags_name { + unsigned long flag; + const char *short_name; + const char *long_name; +}; + +static struct flags_name flags_array[] = { + { EXT2_SECRM_FL, "s", "Secure_Deletion" }, + { EXT2_UNRM_FL, "u" , "Undelete" }, + { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, + { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, + { EXT2_IMMUTABLE_FL, "i", "Immutable" }, + { EXT2_APPEND_FL, "a", "Append_Only" }, + { EXT2_NODUMP_FL, "d", "No_Dump" }, + { EXT2_NOATIME_FL, "A", "No_Atime" }, + { EXT2_COMPR_FL, "c", "Compression_Requested" }, +#ifdef ENABLE_COMPRESSION + { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, + { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, + { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, + { EXT2_ECOMPR_FL, "E", "Compression_Error" }, +#endif + { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, + { EXT2_INDEX_FL, "I", "Indexed_directory" }, + { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, + { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, + { EXT4_EXTENTS_FL, "e", "Extents" }, + { EXT4_HUGE_FILE_FL, "h", "Huge_file" }, + { 0, NULL, NULL } +}; + +void print_flags (FILE * f, unsigned long flags, unsigned options) +{ + int long_opt = (options & PFOPT_LONG); + struct flags_name *fp; + int first = 1; + + for (fp = flags_array; fp->flag != 0; fp++) { + if (flags & fp->flag) { + if (long_opt) { + if (first) + first = 0; + else + fputs(", ", f); + fputs(fp->long_name, f); + } else + fputs(fp->short_name, f); + } else { + if (!long_opt) + fputs("-", f); + } + } + if (long_opt && first) + fputs("---", f); +} + diff --git a/lib/libext2fs/source/e2p/ps.c b/lib/libext2fs/source/e2p/ps.c new file mode 100644 index 0000000..e6ad60a --- /dev/null +++ b/lib/libext2fs/source/e2p/ps.c @@ -0,0 +1,31 @@ +/* + * ps.c - Print filesystem state + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/12/22 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_state (FILE * f, unsigned short state) +{ + if (state & EXT2_VALID_FS) + fprintf (f, " clean"); + else + fprintf (f, " not clean"); + if (state & EXT2_ERROR_FS) + fprintf (f, " with errors"); +} diff --git a/lib/libext2fs/source/e2p/setflags.c b/lib/libext2fs/source/e2p/setflags.c new file mode 100644 index 0000000..776300d --- /dev/null +++ b/lib/libext2fs/source/e2p/setflags.c @@ -0,0 +1,74 @@ +/* + * setflags.c - Set a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +/* + * Deal with lame glibc's that define this function without actually + * implementing it. Can you say "attractive nuisance", boys and girls? + * I knew you could! + */ +#ifdef __linux__ +#undef HAVE_CHFLAGS +#endif + +int setflags (int fd, unsigned long flags) +{ +#if HAVE_CHFLAGS + struct stat buf; + unsigned long bsd_flags = 0; + +#ifdef UF_IMMUTABLE + if (flags & EXT2_IMMUTABLE_FL) + bsd_flags |= UF_IMMUTABLE; +#endif +#ifdef UF_APPEND + if (flags & EXT2_APPEND_FL) + bsd_flags |= UF_APPEND; +#endif +#ifdef UF_NODUMP + if (flags & EXT2_NODUMP_FL) + bsd_flags |= UF_NODUMP; +#endif + + return fchflags (fd, bsd_flags); +#else +#if HAVE_EXT2_IOCTLS + int f; + + if (!fstat(fd, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + errno = EOPNOTSUPP; + return -1; + } + f = (int) flags; + return ioctl (fd, EXT2_IOC_SETFLAGS, &f); +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/source/e2p/setversion.c b/lib/libext2fs/source/e2p/setversion.c new file mode 100644 index 0000000..d7d4128 --- /dev/null +++ b/lib/libext2fs/source/e2p/setversion.c @@ -0,0 +1,40 @@ +/* + * setversion.c - Set a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int setversion (int fd, unsigned long version) +{ +#if HAVE_EXT2_IOCTLS + int ver; + + ver = (int) version; + return ioctl (fd, EXT2_IOC_SETVERSION, &ver); +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/source/e2p/uuid.c b/lib/libext2fs/source/e2p/uuid.c new file mode 100644 index 0000000..310b01d --- /dev/null +++ b/lib/libext2fs/source/e2p/uuid.c @@ -0,0 +1,84 @@ +/* + * uuid.c -- utility routines for manipulating UUID's. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include + +#include "e2p.h" + +struct uuid { + __u32 time_low; + __u16 time_mid; + __u16 time_hi_and_version; + __u16 clock_seq; + __u8 node[6]; +}; + +/* Returns 1 if the uuid is the NULL uuid */ +int e2p_is_null_uuid(void *uu) +{ + __u8 *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + +static void e2p_unpack_uuid(void *in, struct uuid *uu) +{ + __u8 *ptr = in; + __u32 tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +void e2p_uuid_to_str(void *uu, char *out) +{ + struct uuid uuid; + + e2p_unpack_uuid(uu, &uuid); + sprintf(out, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +const char *e2p_uuid2str(void *uu) +{ + static char buf[80]; + + if (e2p_is_null_uuid(uu)) + return ""; + e2p_uuid_to_str(uu, buf); + return buf; +} + diff --git a/lib/libext2fs/source/expanddir.c b/lib/libext2fs/source/expanddir.c new file mode 100644 index 0000000..7673a3b --- /dev/null +++ b/lib/libext2fs/source/expanddir.c @@ -0,0 +1,126 @@ +/* + * expand.c --- expand an ext2fs directory + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct expand_dir_struct { + int done; + int newblocks; + errcode_t err; +}; + +static int expand_dir_proc(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; + blk64_t new_blk; + static blk64_t last_blk = 0; + char *block; + errcode_t retval; + + if (*blocknr) { + last_blk = *blocknr; + return 0; + } + retval = ext2fs_new_block2(fs, last_blk, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt > 0) { + retval = ext2fs_new_dir_block(fs, 0, 0, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + es->done = 1; + retval = ext2fs_write_dir_block(fs, new_blk, block); + } else { + retval = ext2fs_get_mem(fs->blocksize, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + memset(block, 0, fs->blocksize); + retval = io_channel_write_blk64(fs->io, new_blk, 1, block); + } + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + ext2fs_free_mem(&block); + *blocknr = new_blk; + ext2fs_block_alloc_stats2(fs, new_blk, +1); + es->newblocks++; + + if (es->done) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; +} + +errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) +{ + errcode_t retval; + struct expand_dir_struct es; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + es.done = 0; + es.err = 0; + es.newblocks = 0; + + retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND, + 0, expand_dir_proc, &es); + + if (es.err) + return es.err; + if (!es.done) + return EXT2_ET_EXPAND_DIR_ERR; + + /* + * Update the size and block count fields in the inode. + */ + retval = ext2fs_read_inode(fs, dir, &inode); + if (retval) + return retval; + + inode.i_size += fs->blocksize; + ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); + + retval = ext2fs_write_inode(fs, dir, &inode); + if (retval) + return retval; + + return 0; +} diff --git a/lib/libext2fs/source/ext2.c b/lib/libext2fs/source/ext2.c new file mode 100644 index 0000000..7cff7f3 --- /dev/null +++ b/lib/libext2fs/source/ext2.c @@ -0,0 +1,420 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" +#include "gekko_io.h" +#include "mem_allocate.h" +#include "partitions.h" + +bool ext2Mount(const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) +{ + errcode_t retval = -1; + ext2_filsys fs = NULL; + io_channel io_chan = NULL; + gekko_fd *fd = NULL; + ext2_vd * vd = NULL; + + // Sanity check + if (!name || !interface) + { + errno = EINVAL; + return false; + } + + // Allocate the device driver descriptor + fd = (gekko_fd*) mem_alloc(sizeof(gekko_fd)); + if (!fd) + goto cleanup; + + memset(fd, 0, sizeof(gekko_fd)); + + // Setup the device driver descriptor + fd->interface = interface; + fd->startSector = startSector; + fd->sectorSize = 0; + fd->sectorCount = 0; + fd->cachePageCount = cachePageCount; + fd->cachePageSize = cachePageSize; + + fs = mem_alloc(sizeof(struct struct_ext2_filsys)); + if (!fs) + { + ext2_log_trace("no memory for fs\n"); + errno = ENOMEM; + goto cleanup; + } + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + + io_chan = mem_alloc(sizeof(struct struct_io_channel)); + if (!io_chan) + { + ext2_log_trace("no memory for io_chan\n"); + errno = ENOMEM; + goto cleanup; + } + + memset(io_chan, 0, sizeof(struct struct_io_channel)); + + io_chan->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io_chan->manager = gekko_io_manager; + io_chan->name = strdup(name); + if(!io_chan->name) goto cleanup; + io_chan->block_size = 1024; + io_chan->read_error = 0; + io_chan->write_error = 0; + io_chan->refcount = 1; + io_chan->private_data = fd; + io_chan->flags = flags; + + retval = ext2fs_open2(io_chan->name, 0, io_chan->flags, 0, 0, &io_chan, &fs); + if(retval) + { + ext2_log_trace("error mounting %i\n", (int) retval); + goto cleanup; + } + + vd = mem_alloc(sizeof(ext2_vd)); + if(!vd) + { + ext2_log_trace("no memory for vd\n"); + goto cleanup; + } + + // Initialise the volume descriptor + ext2InitVolume(vd); + vd->fs = fs; + vd->io = io_chan; + vd->root = EXT2_ROOT_INO; + + // Add the device to the devoptab table + if (ext2AddDevice(name, vd)) { + ext2DeinitVolume(vd); + goto cleanup; + } + + return true; + +cleanup: + if(fd) + mem_free(fd); + if(io_chan) + mem_free(io_chan); + if(vd) + mem_free(vd); + if(fs) + { + ext2fs_close(fs); + ext2fs_free(fs); + } + + return false; +} + +void ext2Unmount(const char *name) +{ + ext2_vd *vd = NULL; + + // Get the devices volume descriptor + vd = ext2GetVolume(name); + if (!vd) + return; + + // Remove the device from the devoptab table + ext2RemoveDevice(name); + + // Deinitialise the volume descriptor + ext2DeinitVolume(vd); + + // Unmount the volume + ext2fs_close(vd->fs); + ext2fs_free(vd->fs); + + //Free the io manager + mem_free(vd->io->private_data); + mem_free(vd->io); + + // Free the volume descriptor + mem_free(vd); + + return; +} + + +const char *ext2GetVolumeName (const char *name) +{ + if (!name) { + errno = EINVAL; + return NULL; + } + + // Get the devices volume descriptor + ext2_vd *vd = ext2GetVolume(name); + if (!vd) { + errno = ENODEV; + return NULL; + } + + return vd->fs->super->s_volume_name; +} + +bool ext2SetVolumeName (const char *name, const char *volumeName) +{ + // Sanity check + if (!name || !volumeName) { + errno = EINVAL; + return false; + } + + // Get the devices volume descriptor + ext2_vd *vd = ext2GetVolume(name); + if (!vd) { + errno = ENODEV; + return false; + } + + // Lock + ext2Lock(vd); + int i; + for(i = 0; i < 15 && *volumeName != 0; ++i, volumeName++) + vd->fs->super->s_volume_name[i] = *volumeName; + + vd->fs->super->s_volume_name[i] = '\0'; + + ext2fs_mark_super_dirty(vd->fs); + + ext2Sync(vd, NULL); + + // Unlock + ext2Unlock(vd); + + return true; +} + +int ext2FindPartitions (const DISC_INTERFACE *interface, sec_t **out_partitions) +{ + MASTER_BOOT_RECORD mbr; + PARTITION_RECORD *partition = NULL; + int partition_count = 0, ret = -1; + sec_t part_lba = 0; + sec_t * partitions = NULL; + int i; + + union { + u8 buffer[512]; + MASTER_BOOT_RECORD mbr; + EXTENDED_BOOT_RECORD ebr; + } sector; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + + if(!out_partitions) { + errno = EINVAL; + return -1; + } + + // Start the device and check that it is inserted + if (!interface->startup()) { + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + errno = EIO; + return 0; + } + + struct ext2_super_block * super = (struct ext2_super_block *) malloc(SUPERBLOCK_SIZE); //1024 bytes + if(!super) + { + ext2_log_trace("no memory for superblock"); + errno = ENOMEM; + return -1; + } + + partitions = (sec_t *) malloc(sizeof(sec_t)); + if(!partitions) + { + ext2_log_trace("no memory for partitions"); + errno = ENOMEM; + mem_free(super); + return -1; + } + // Read the first sector on the device + if (!interface->readSectors(0, 1, §or.buffer)) { + errno = EIO; + mem_free(partitions); + mem_free(super); + return -1; + } + + // If this is the devices master boot record + if (sector.mbr.signature == MBR_SIGNATURE) + { + memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); + + // Search the partition table for all EXT2/3/4 partitions (max. 4 primary partitions) + for (i = 0; i < 4; i++) + { + partition = &mbr.partitions[i]; + part_lba = ext2fs_le32_to_cpu(mbr.partitions[i].lba_start); + + // Figure out what type of partition this is + switch (partition->type) + { + // Ignore empty partitions + case PARTITION_TYPE_EMPTY: + continue; + + // EXT2/3/4 partition + case PARTITION_TYPE_LINUX: + + // Read and validate the EXT partition + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + + break; + + // DOS 3.3+ or Windows 95 extended partition + case PARTITION_TYPE_DOS33_EXTENDED: + case PARTITION_TYPE_WIN95_EXTENDED: + { + ext2_log_trace("Partition %i: Claims to be Extended\n", i + 1); + + // Walk the extended partition chain, finding all EXT partitions within it + sec_t ebr_lba = part_lba; + sec_t next_erb_lba = 0; + do { + // Read and validate the extended boot record + if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) + { + if (sector.ebr.signature == EBR_SIGNATURE) + { + ext2_log_trace("Logical Partition @ %d: %s type 0x%x\n", ebr_lba + next_erb_lba, + sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + sector.ebr.partition.type); + + // Get the start sector of the current partition + // and the next extended boot record in the chain + part_lba = ebr_lba + next_erb_lba + ext2fs_le32_to_cpu(sector.ebr.partition.lba_start); + next_erb_lba = ext2fs_le32_to_cpu(sector.ebr.next_ebr.lba_start); + + // Check if this partition has a valid EXT boot record + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + } + else + next_erb_lba = 0; + } + + } while (next_erb_lba); + + break; + + } + + // Unknown or unsupported partition type + default: + { + // Check if this partition has a valid EXT boot record anyway, + // it might be misrepresented due to a lazy partition editor + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + break; + } + } + } + + // Else it is assumed this device has no master boot record + } + else + { + ext2_log_trace("No Master Boot Record was found!\n"); + + // As a last-ditched effort, search the first 64 sectors of the device for stray EXT partitions + for (i = 1; i < 64; i++) + { + if (interface->readSectors(i+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = i; + } + } + } + + } + + // Return the found partitions (if any) + if (partition_count > 0) + { + *out_partitions = partitions; + ret = partition_count; + } + +cleanup: + + if(partitions && partition_count == 0) + mem_free(partitions); + if(super) + mem_free(super); + + return ret; +} + diff --git a/lib/libext2fs/source/ext2_err.h b/lib/libext2fs/source/ext2_err.h new file mode 100644 index 0000000..4f12744 --- /dev/null +++ b/lib/libext2fs/source/ext2_err.h @@ -0,0 +1,152 @@ +// +// Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. +// +// This file may be redistributed under the terms of the GNU Public +// License. +#ifndef EXT2_ERR_H_ +#define EXT2_ERR_H_ + +#define EXT2_ET_OK 0 +#define EXT2_ET_BASE -1 +#define EXT2_ET_MAGIC_EXT2FS_FILSYS -2 +#define EXT2_ET_MAGIC_BADBLOCKS_LIST -3 +#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE -4 +#define EXT2_ET_MAGIC_INODE_SCAN -5 +#define EXT2_ET_MAGIC_IO_CHANNEL -6 +#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL -7 +#define EXT2_ET_MAGIC_IO_MANAGER -8 +#define EXT2_ET_MAGIC_BLOCK_BITMAP -9 +#define EXT2_ET_MAGIC_INODE_BITMAP -10 +#define EXT2_ET_MAGIC_GENERIC_BITMAP -11 +#define EXT2_ET_MAGIC_TEST_IO_CHANNEL -12 +#define EXT2_ET_MAGIC_DBLIST -13 +#define EXT2_ET_MAGIC_ICOUNT -14 +#define EXT2_ET_MAGIC_PQ_IO_CHANNEL -15 +#define EXT2_ET_MAGIC_EXT2_FILE -16 +#define EXT2_ET_MAGIC_E2IMAGE -17 +#define EXT2_ET_MAGIC_INODE_IO_CHANNEL -18 +#define EXT2_ET_MAGIC_EXTENT_HANDLE -19 +#define EXT2_ET_BAD_MAGIC -20 +#define EXT2_ET_REV_TOO_HIGH -21 +#define EXT2_ET_RO_FILSYS -22 +#define EXT2_ET_GDESC_READ -23 +#define EXT2_ET_GDESC_WRITE -24 +#define EXT2_ET_GDESC_BAD_BLOCK_MAP -25 +#define EXT2_ET_GDESC_BAD_INODE_MAP -26 +#define EXT2_ET_GDESC_BAD_INODE_TABLE -27 +#define EXT2_ET_INODE_BITMAP_WRITE -28 +#define EXT2_ET_INODE_BITMAP_READ -29 +#define EXT2_ET_BLOCK_BITMAP_WRITE -30 +#define EXT2_ET_BLOCK_BITMAP_READ -31 +#define EXT2_ET_INODE_TABLE_WRITE -32 +#define EXT2_ET_INODE_TABLE_READ -33 +#define EXT2_ET_NEXT_INODE_READ -34 +#define EXT2_ET_UNEXPECTED_BLOCK_SIZE -35 +#define EXT2_ET_DIR_CORRUPTED -36 +#define EXT2_ET_SHORT_READ -37 +#define EXT2_ET_SHORT_WRITE -38 +#define EXT2_ET_DIR_NO_SPACE -39 +#define EXT2_ET_NO_INODE_BITMAP -40 +#define EXT2_ET_NO_BLOCK_BITMAP -41 +#define EXT2_ET_BAD_INODE_NUM -42 +#define EXT2_ET_BAD_BLOCK_NUM -45 +#define EXT2_ET_EXPAND_DIR_ERR -46 +#define EXT2_ET_TOOSMALL -47 +#define EXT2_ET_BAD_BLOCK_MARK -48 +#define EXT2_ET_BAD_BLOCK_UNMARK -49 +#define EXT2_ET_BAD_BLOCK_TEST -50 +#define EXT2_ET_BAD_INODE_MARK -51 +#define EXT2_ET_BAD_INODE_UNMARK -52 +#define EXT2_ET_BAD_INODE_TEST -53 +#define EXT2_ET_FUDGE_BLOCK_BITMAP_END -54 +#define EXT2_ET_FUDGE_INODE_BITMAP_END -55 +#define EXT2_ET_BAD_IND_BLOCK -56 +#define EXT2_ET_BAD_DIND_BLOCK -57 +#define EXT2_ET_BAD_TIND_BLOCK -58 +#define EXT2_ET_NEQ_BLOCK_BITMAP -59 +#define EXT2_ET_NEQ_INODE_BITMAP -60 +#define EXT2_ET_BAD_DEVICE_NAME -61 +#define EXT2_ET_MISSING_INODE_TABLE -62 +#define EXT2_ET_CORRUPT_SUPERBLOCK -63 +#define EXT2_ET_BAD_GENERIC_MARK -64 +#define EXT2_ET_BAD_GENERIC_UNMARK -65 +#define EXT2_ET_BAD_GENERIC_TEST -66 +#define EXT2_ET_SYMLINK_LOOP -67 +#define EXT2_ET_CALLBACK_NOTHANDLED -68 +#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE -69 +#define EXT2_ET_UNSUPP_FEATURE -70 +#define EXT2_ET_RO_UNSUPP_FEATURE -71 +#define EXT2_ET_LLSEEK_FAILED -72 +#define EXT2_ET_NO_MEMORY -73 +#define EXT2_ET_INVALID_ARGUMENT -74 +#define EXT2_ET_BLOCK_ALLOC_FAIL -75 +#define EXT2_ET_INODE_ALLOC_FAIL -76 +#define EXT2_ET_NO_DIRECTORY -77 +#define EXT2_ET_TOO_MANY_REFS -78 +#define EXT2_ET_FILE_NOT_FOUND -79 +#define EXT2_ET_FILE_RO -80 +#define EXT2_ET_DB_NOT_FOUND -81 +#define EXT2_ET_DIR_EXISTS -82 +#define EXT2_ET_UNIMPLEMENTED -83 +#define EXT2_ET_CANCEL_REQUESTED -84 +#define EXT2_ET_FILE_TOO_BIG -85 +#define EXT2_ET_JOURNAL_NOT_BLOCK -86 +#define EXT2_ET_NO_JOURNAL_SB -87 +#define EXT2_ET_JOURNAL_TOO_SMALL -88 +#define EXT2_ET_JOURNAL_UNSUPP_VERSION -89 +#define EXT2_ET_LOAD_EXT_JOURNAL -90 +#define EXT2_ET_NO_JOURNAL -91 +#define EXT2_ET_DIRHASH_UNSUPP -92 +#define EXT2_ET_BAD_EA_BLOCK_NUM -93 +#define EXT2_ET_TOO_MANY_INODES -94 +#define EXT2_ET_NOT_IMAGE_FILE -95 +#define EXT2_ET_RES_GDT_BLOCKS -96 +#define EXT2_ET_RESIZE_INODE_CORRUPT -97 +#define EXT2_ET_SET_BMAP_NO_IND -98 +#define EXT2_ET_TDB_SUCCESS -99 +#define EXT2_ET_TDB_ERR_CORRUPT -100 +#define EXT2_ET_TDB_ERR_IO -101 +#define EXT2_ET_TDB_ERR_LOCK -102 +#define EXT2_ET_TDB_ERR_OOM -103 +#define EXT2_ET_TDB_ERR_EXISTS -104 +#define EXT2_ET_TDB_ERR_NOLOCK -105 +#define EXT2_ET_TDB_ERR_EINVAL -106 +#define EXT2_ET_TDB_ERR_NOEXIST -107 +#define EXT2_ET_TDB_ERR_RDONLY -108 +#define EXT2_ET_DBLIST_EMPTY -109 +#define EXT2_ET_RO_BLOCK_ITERATE -110 +#define EXT2_ET_MAGIC_EXTENT_PATH -111 +#define EXT2_ET_MAGIC_RESERVED_10 -112 +#define EXT2_ET_MAGIC_RESERVED_11 -113 +#define EXT2_ET_MAGIC_RESERVED_12 -114 +#define EXT2_ET_MAGIC_RESERVED_13 -115 +#define EXT2_ET_MAGIC_RESERVED_14 -116 +#define EXT2_ET_MAGIC_RESERVED_15 -117 +#define EXT2_ET_MAGIC_RESERVED_16 -118 +#define EXT2_ET_MAGIC_RESERVED_17 -119 +#define EXT2_ET_MAGIC_RESERVED_18 -120 +#define EXT2_ET_MAGIC_RESERVED_19 -121 +#define EXT2_ET_EXTENT_HEADER_BAD -122 +#define EXT2_ET_EXTENT_INDEX_BAD -123 +#define EXT2_ET_EXTENT_LEAF_BAD -124 +#define EXT2_ET_EXTENT_NO_SPACE -125 +#define EXT2_ET_INODE_NOT_EXTENT -126 +#define EXT2_ET_EXTENT_NO_NEXT -127 +#define EXT2_ET_EXTENT_NO_PREV -128 +#define EXT2_ET_EXTENT_NO_UP -129 +#define EXT2_ET_EXTENT_NO_DOWN -130 +#define EXT2_ET_NO_CURRENT_NODE -131 +#define EXT2_ET_OP_NOT_SUPPORTED -132 +#define EXT2_ET_CANT_INSERT_EXTENT -133 +#define EXT2_ET_CANT_SPLIT_EXTENT -134 +#define EXT2_ET_EXTENT_NOT_FOUND -135 +#define EXT2_ET_EXTENT_NOT_SUPPORTED -136 +#define EXT2_ET_EXTENT_INVALID_LENGTH -137 +#define EXT2_ET_IO_CHANNEL_NO_SUPPORT_64 -138 +#define EXT2_NO_MTAB_FILE -139 +#define EXT2_ET_MAGIC_GENERIC_BITMAP64 -140 +#define EXT2_ET_MAGIC_BLOCK_BITMAP64 -141 +#define EXT2_ET_MAGIC_INODE_BITMAP64 -142 +#define EXT2_ET_CANT_USE_LEGACY_BITMAPS -143 + +#endif diff --git a/lib/libext2fs/source/ext2_ext_attr.h b/lib/libext2fs/source/ext2_ext_attr.h new file mode 100644 index 0000000..ed548d1 --- /dev/null +++ b/lib/libext2fs/source/ext2_ext_attr.h @@ -0,0 +1,71 @@ +/* + File: linux/ext2_ext_attr.h + + On-disk format of extended attributes for the ext2 filesystem. + + (C) 2000 Andreas Gruenbacher, +*/ + +#ifndef _EXT2_EXT_ATTR_H +#define _EXT2_EXT_ATTR_H +/* Magic value in attribute blocks */ +#define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 +#define EXT2_EXT_ATTR_MAGIC 0xEA020000 + +/* Maximum number of references to one attribute block */ +#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 + +struct ext2_ext_attr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext2_ext_attr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* attribute name index */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ +#if 0 + char e_name[0]; /* attribute name */ +#endif +}; + +#define EXT2_EXT_ATTR_PAD_BITS 2 +#define EXT2_EXT_ATTR_PAD ((unsigned) 1<e_name_len)) ) +#define EXT2_EXT_ATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) +#define EXT2_EXT_ATTR_NAME(entry) \ + (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) +#define EXT2_XATTR_LEN(name_len) \ + (((name_len) + EXT2_EXT_ATTR_ROUND + \ + sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_XATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT2_FS_EXT_ATTR +extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); +extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int); +extern void ext2_ext_attr_free_inode(struct inode *inode); +extern void ext2_ext_attr_put_super(struct super_block *sb); +extern int ext2_ext_attr_init(void); +extern void ext2_ext_attr_done(void); +# else +# define ext2_get_ext_attr NULL +# define ext2_set_ext_attr NULL +# endif +#endif /* __KERNEL__ */ +#endif /* _EXT2_EXT_ATTR_H */ diff --git a/lib/libext2fs/source/ext2_frag.c b/lib/libext2fs/source/ext2_frag.c new file mode 100644 index 0000000..e05a4bc --- /dev/null +++ b/lib/libext2fs/source/ext2_frag.c @@ -0,0 +1,60 @@ +#include "ext2_internal.h" +#include "ext2_frag.h" + +typedef struct _PrivData +{ + _ext2_frag_append_t append_fragment; + void * callback_data; +} PrivDataST; + +static int block_iter_callback(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_block, int ref_offset, void *privateData) +{ + PrivDataST *priv = (PrivDataST *) privateData; + blk64_t block; + block = *blocknr; + + return priv->append_fragment(priv->callback_data, blockcnt*fs->io->block_size/512, block*fs->io->block_size/512, fs->io->block_size/512); +} + +int _EXT2_get_fragments(const char *in_path, _ext2_frag_append_t append_fragment, void *callback_data) +{ + ext2_inode_t *ni = NULL; + ext2_vd *vd; + + vd = ext2GetVolume(in_path); + + if(!vd) + { + errno = EXDEV; + return -1; + } + + // Get the actual path of the entry + const char * path = ext2RealPath(in_path); + if (!path) { + errno = EINVAL; + return -1; + } + + // Find the entry + ni = ext2OpenEntry(vd, path); + if (!ni) { + errno = ENOENT; + return -1; + } + + PrivDataST priv; + priv.callback_data = callback_data; + priv.append_fragment = append_fragment; + + int ret = ext2fs_block_iterate3(vd->fs, ni->ino, BLOCK_FLAG_DATA_ONLY, NULL, block_iter_callback, &priv); + + if(ret == 0) + ret = priv.append_fragment(callback_data, EXT2_I_SIZE(&ni->ni) >> 9, 0, 0); + + ext2UpdateTimes(vd, ni, EXT2_UPDATE_ATIME); + + ext2CloseEntry(vd, ni); + + return ret; +} diff --git a/lib/libext2fs/source/ext2_frag.h b/lib/libext2fs/source/ext2_frag.h new file mode 100644 index 0000000..5073277 --- /dev/null +++ b/lib/libext2fs/source/ext2_frag.h @@ -0,0 +1,16 @@ +#ifndef EXT2_FRAG_H_ +#define EXT2_FRAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*_ext2_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); + +int _EXT2_get_fragments(const char *in_path, _ext2_frag_append_t append_fragment, void *callback_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libext2fs/source/ext2_fs.h b/lib/libext2fs/source/ext2_fs.h new file mode 100644 index 0000000..8a58dd1 --- /dev/null +++ b/lib/libext2fs/source/ext2_fs.h @@ -0,0 +1,799 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _LINUX_EXT2_FS_H +#define _LINUX_EXT2_FS_H + +#include "ext2_types.h" /* Changed from linux/types.h */ + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ +#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 65000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#ifdef __KERNEL__ +#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) +#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) +#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) +#else +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#endif +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) + +/* + * Macro-instructions used to manage allocation clusters + */ +#define EXT2_MIN_CLUSTER_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_MAX_CLUSTER_LOG_SIZE 29 /* 512MB */ +#define EXT2_MIN_CLUSTER_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_CLUSTER_SIZE (1 << EXT2_MAX_CLUSTER_LOG_SIZE) +#define EXT2_CLUSTER_SIZE(s) (EXT2_MIN_BLOCK_SIZE << \ + (s)->s_log_cluster_size) +#define EXT2_CLUSTER_SIZE_BITS(s) ((s)->s_log_cluster_size + 10) + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; + __u32 bg_reserved[2]; + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __u32 bg_reserved[2]; /* Likely block/inode bitmap checksum */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __u32 bg_inode_table_hi; /* Inodes table block MSB */ + __u16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __u16 bg_used_dirs_count_hi; /* Directories count MSB */ + __u16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __u32 bg_reserved2[3]; +}; + +#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ +#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ +#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + __u32 reserved_zero; + __u8 hash_version; /* 0 now, 1 at release */ + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 +#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ +#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ +#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + __u32 hash; + __u32 block; +}; + +struct ext2_dx_countlimit { + __u16 limit; + __u16 count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_MIN_DESC_SIZE 32 +#define EXT2_MIN_DESC_SIZE_64BIT 64 +#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_DESC_SIZE(s) \ + ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \ + (s)->s_desc_size : EXT2_MIN_DESC_SIZE) + +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) +#ifdef __KERNEL__ +#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) +#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) +#else +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ +#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ +#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ + +/* + * ioctl commands + */ + +/* Used for online resize */ +struct ext2_new_group_input { + __u32 group; /* Group number for this data */ + __u32 block_bitmap; /* Absolute block number of block bitmap */ + __u32 inode_bitmap; /* Absolute block number of inode bitmap */ + __u32 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; /* Number of reserved GDT blocks in group */ +}; + +struct ext4_new_group_input { + __u32 group; /* Group number for this data */ + __u64 block_bitmap; /* Absolute block number of block bitmap */ + __u64 inode_bitmap; /* Absolute block number of inode bitmap */ + __u64 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +#ifdef __GNU__ /* Needed for the Hurd */ +#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) +#endif + +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) +#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) +#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) +#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) +#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode Change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ + __u16 i_extra_isize; + __u16 i_pad1; + __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ + __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __u32 i_crtime; /* File creation time */ + __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ + __u32 i_version_hi; /* high 32 bits for 64-bit version */ +}; + +#define i_dir_acl i_size_high + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#define i_reserved2 osd2.linux2.l_i_reserved2 +#else +#if defined(__GNU__) + +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author + +#endif /* __GNU__ */ +#endif /* defined(__KERNEL__) || defined(__linux__) */ + +#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) +#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) +#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x)) +#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x)) + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ +#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ + +/* + * Misc. filesystem flags + */ +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ +#define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ +#define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ +#define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +#if (__GNUC__ >= 4) +#define ext4_offsetof(TYPE,MEMBER) __builtin_offsetof(TYPE,MEMBER) +#else +#define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __u32 s_log_cluster_size; /* Allocation cluster size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_clusters_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ + __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ + __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ + __u32 s_blocks_count_hi; /* Blocks count high 32bits */ + __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ + __u32 s_free_blocks_hi; /* Free blocks count */ + __u16 s_min_extra_isize; /* All inodes have at least # bytes */ + __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __u32 s_flags; /* Miscellaneous flags */ + __u16 s_raid_stride; /* RAID stride */ + __u16 s_mmp_interval; /* # seconds to wait in MMP checking */ + __u64 s_mmp_block; /* Block for multi-mount protection */ + __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_reserved_char_pad; + __u16 s_reserved_pad; /* Padding to next 32bits */ + __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __u32 s_snapshot_inum; /* Inode number of active snapshot */ + __u32 s_snapshot_id; /* sequential ID of active snapshot */ + __u64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __u32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ +#define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count) + __u32 s_error_count; /* number of fs errors */ + __u32 s_first_error_time; /* first time an error happened */ + __u32 s_first_error_ino; /* inode involved in first error */ + __u64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __u32 s_first_error_line; /* line number where error happened */ + __u32 s_last_error_time; /* most recent time of an error */ + __u32 s_last_error_ino; /* inode involved in last error */ + __u32 s_last_error_line; /* line number where error happened */ + __u64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __u32 s_usr_quota_inum; /* inode number of user quota file */ + __u32 s_grp_quota_inum; /* inode number of group quota file */ + __u32 s_overhead_blocks; /* overhead blocks/clusters in fs */ + __u32 s_reserved[109]; /* Padding to the end of the block */ +}; + +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OBSO_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 +#define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +/* + * This structure will be used for multiple mount protection. It will be + * written into the block number saved in the s_mmp_block field in the + * superblock. + */ +#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */ +#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */ +#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */ + +struct mmp_struct { + __u32 mmp_magic; + __u32 mmp_seq; + __u64 mmp_time; + char mmp_nodename[64]; + char mmp_bdevname[32]; + __u16 mmp_interval; + __u16 mmp_pad1; + __u32 mmp_pad2; +}; + +/* + * Interval in number of seconds to update the MMP sequence number. + */ +#define EXT2_MMP_DEF_INTERVAL 5 + +#endif /* _LINUX_EXT2_FS_H */ diff --git a/lib/libext2fs/source/ext2_internal.c b/lib/libext2fs/source/ext2_internal.c new file mode 100644 index 0000000..d75133a --- /dev/null +++ b/lib/libext2fs/source/ext2_internal.c @@ -0,0 +1,1076 @@ +/** + * ext2_internal.c - Internal support routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "ext2_internal.h" +#include "ext2dir.h" +#include "ext2file.h" +#include "gekko_io.h" + +// EXT2 device driver devoptab +static const devoptab_t devops_ext2 = +{ + NULL, /* Device name */ + sizeof(ext2_file_state), + ext2_open_r, + ext2_close_r, + ext2_write_r, + ext2_read_r, + ext2_seek_r, + ext2_fstat_r, + ext2_stat_r, + ext2_link_r, + ext2_unlink_r, + ext2_chdir_r, + ext2_rename_r, + ext2_mkdir_r, + sizeof(ext2_dir_state), + ext2_diropen_r, + ext2_dirreset_r, + ext2_dirnext_r, + ext2_dirclose_r, + ext2_statvfs_r, + ext2_ftruncate_r, + ext2_fsync_r, + NULL /* Device data */ +}; + + +const devoptab_t *ext2GetDevOpTab() +{ + return &devops_ext2; +} + +int ext2AddDevice (const char *name, void *deviceData) +{ + const devoptab_t *devoptab_ext2 = ext2GetDevOpTab(); + devoptab_t *dev = NULL; + char *devname = NULL; + int i; + + // Sanity check + if (!name || !deviceData || !devoptab_ext2) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) mem_alloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return -1; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // Setup the devoptab + memcpy(dev, devoptab_ext2, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = deviceData; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 0; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0] && i != 0) { + devoptab_list[i] = dev; + return 0; + } + } + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +void ext2RemoveDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + mem_free((devoptab_t*)devoptab); + break; + } + } + } + + return; +} + +const devoptab_t *ext2GetDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return devoptab; + } + } + } + + return NULL; +} + +ext2_vd *ext2GetVolume (const char *path) +{ + // Get the volume descriptor from the paths associated devoptab (if found) + const devoptab_t *devoptab_ext2 = ext2GetDevOpTab(); + const devoptab_t *devoptab = ext2GetDevice(path); + if (devoptab && devoptab_ext2 && (devoptab->open_r == devoptab_ext2->open_r)) + return (ext2_vd*)devoptab->deviceData; + + return NULL; +} + +int ext2InitVolume (ext2_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Reset the volumes data + memset(vd, 0, sizeof(ext2_vd)); + + // Initialise the volume lock + LWP_MutexInit(&vd->lock, false); + + return 0; +} + +void ext2DeinitVolume (ext2_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ext2Lock(vd); + + // Close any directories which are still open (lazy programmers!) + ext2_dir_state *nextDir = vd->firstOpenDir; + while (nextDir) { + ext2CloseDir(nextDir); + nextDir = nextDir->nextOpenDir; + } + + // Close any files which are still open (lazy programmers!) + ext2_file_state *nextFile = vd->firstOpenFile; + while (nextFile) { + ext2CloseFile(nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + // Force the underlying device to sync + ext2Sync(vd, NULL); + + // Unlock + ext2Unlock(vd); + + // Deinitialise the volume lock + LWP_MutexDestroy(vd->lock); +} + +static ext2_ino_t ext2PathToInode(ext2_vd *vd, const char * path) +{ + //Sanity check + if(!vd || !path) + return 0; + + char filename[EXT2_NAME_LEN]; + errcode_t errorcode = 0; + ext2_ino_t ino = 0, parent = vd->cwd_ni && *path != '/' && *path != '\0' ? vd->cwd_ni->ino : vd->root; + const char * ptr = path; + int i; + + while(*ptr == '/') ++ptr; + + if(*ptr == '\0') + return parent; + + while(*ptr != '\0') + { + for(i = 0; *ptr != '\0' && *ptr != '/' && (i < EXT2_NAME_LEN-1); ++ptr, ++i) + filename[i] = *ptr; + + filename[i] = '\0'; + + errorcode = ext2fs_namei(vd->fs, vd->root, parent, filename, &ino); + if(errorcode != EXT2_ET_OK) + return 0; + + parent = ino; + + while(*ptr == '/') ++ptr; + + } + + + return ino; +} + +ext2_inode_t *ext2OpenEntry (ext2_vd *vd, const char *path) +{ + errcode_t errorcode = 0; + ext2_inode_t * ni = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // Get the actual path of the entry + path = ext2RealPath(path); + if (!path) + { + errno = EINVAL; + return NULL; + } + + ni = mem_alloc(sizeof(ext2_inode_t)); + if(!ni) + { + errno = ENOMEM; + return NULL; + } + + memset(ni, 0, sizeof(ext2_inode_t)); + + // Find the entry, taking into account our current directory (if any) + ni->ino = ext2PathToInode(vd, path); + if(ni->ino == 0) + { + mem_free(ni); + return NULL; + } + + errorcode = ext2fs_read_inode(vd->fs, ni->ino, &ni->ni); + if(errorcode) + { + mem_free(ni); + return NULL; + } + + return ni; +} + +void ext2CloseEntry(ext2_vd *vd, ext2_inode_t * ni) +{ + // Sanity check + if (!vd || !ni) { + errno = ENODEV; + return; + } + + // Lock + ext2Lock(vd); + + // Sync the entry (if it is dirty) + if(ni && ni->dirty) + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + + // Close the entry + if(ni) + mem_free(ni); + + // Unlock + ext2Unlock(vd); + + return; +} + +static ext2_ino_t ext2CreateSymlink(ext2_vd *vd, const char *path, const char * targetdir, const char * name, mode_t type) +{ + ext2_inode_t *target_ni = NULL; + ext2_ino_t newentry = 0; + ext2_ino_t ino = 0; + + // Check if it does exist + target_ni = ext2OpenEntry(vd, targetdir); + if (!target_ni) + goto cleanup; + + int err = ext2fs_new_inode(vd->fs, target_ni->ino, type, 0, &ino); + if (err) + goto cleanup; + + do + { + err = ext2fs_link(vd->fs, target_ni->ino, name, ino, EXT2_FT_SYMLINK); + if (err == EXT2_ET_DIR_NO_SPACE) + { + err = ext2fs_expand_dir(vd->fs, target_ni->ino); + if (err) + goto cleanup; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + ext2fs_inode_alloc_stats2(vd->fs, ino, +1, 0); + + struct ext2_inode inode; + memset(&inode, 0, sizeof(inode)); + inode.i_mode = type; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); + inode.i_links_count = 1; + inode.i_size = strlen(path); //initial size of file + inode.i_uid = target_ni->ni.i_uid; + inode.i_gid = target_ni->ni.i_gid; + + if (strlen(path) <= sizeof(inode.i_block)) + { + /* fast symlink */ + strncpy((char *)&(inode.i_block[0]),path,sizeof(inode.i_blocks)); + } + else + { + /* slow symlink */ + char * buffer = mem_alloc(vd->fs->blocksize); + if (buffer) + { + blk_t blk; + strncpy(buffer, path, vd->fs->blocksize); + err = ext2fs_new_block(vd->fs, 0, 0, &blk); + if (!err) + { + inode.i_block[0] = blk; + inode.i_blocks = vd->fs->blocksize / BYTES_PER_SECTOR; + vd->fs->io->manager->write_blk(vd->fs->io, blk, 1, buffer); + ext2fs_block_alloc_stats(vd->fs, blk, +1); + } + mem_free(buffer); + } + } + + if(ext2fs_write_new_inode(vd->fs, ino, &inode) != 0) + newentry = ino; + +cleanup: + + if(target_ni) + ext2CloseEntry(vd, target_ni); + + return newentry; +} + +static ext2_ino_t ext2CreateMkDir(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name) +{ + ext2_ino_t newentry = 0; + ext2_ino_t existing; + + if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0) + return 0; + + errcode_t err = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newentry); + if(err != EXT2_ET_OK) + return 0; + + do + { + err = ext2fs_mkdir(vd->fs, parent->ino, newentry, name); + if(err == EXT2_ET_DIR_NO_SPACE) + { + if(ext2fs_expand_dir(vd->fs, parent->ino) != 0) + return 0; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + if(err != EXT2_ET_OK) + return 0; + + struct ext2_inode inode; + if(ext2fs_read_inode(vd->fs, newentry, &inode) == EXT2_ET_OK) + { + inode.i_mode = type; + inode.i_uid = parent->ni.i_uid; + inode.i_gid = parent->ni.i_gid; + ext2fs_write_new_inode(vd->fs, newentry, &inode); + } + + return newentry; +} + + +static ext2_ino_t ext2CreateFile(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name) +{ + errcode_t retval = -1; + ext2_ino_t newfile = 0; + ext2_ino_t existing; + + if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0) + return 0; + + retval = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newfile); + if (retval) + return 0; + + do + { + retval = ext2fs_link(vd->fs, parent->ino, name, newfile, EXT2_FT_REG_FILE); + if (retval == EXT2_ET_DIR_NO_SPACE) + { + if (ext2fs_expand_dir(vd->fs, parent->ino) != 0) + return 0; + } + } + while(retval == EXT2_ET_DIR_NO_SPACE); + + if (retval) + return 0; + + ext2fs_inode_alloc_stats2(vd->fs, newfile, +1, 0); + + struct ext2_inode inode; + memset(&inode, 0, sizeof(inode)); + inode.i_mode = type; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(0); + inode.i_links_count = 1; + inode.i_size = 0; + inode.i_uid = parent->ni.i_uid; + inode.i_gid = parent->ni.i_gid; + + if (ext2fs_write_new_inode(vd->fs, newfile, &inode) != 0) + return 0; + + return newfile; +} + +ext2_inode_t *ext2Create(ext2_vd *vd, const char *path, mode_t type, const char *target) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *targetdir = NULL; + char *name = NULL; + ext2_ino_t newentry = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return NULL; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return NULL; + + // You cannot link between devices + if(target) { + if(vd != ext2GetVolume(target)) { + errno = EXDEV; + return NULL; + } + // Check if existing + dir_ni = ext2OpenEntry(vd, target); + if (dir_ni) { + goto cleanup; + } + ext2CloseEntry(vd, dir_ni); + dir_ni = NULL; + targetdir = strdup(target); + if (!targetdir) { + errno = EINVAL; + goto cleanup; + } + } + + // Get the actual paths of the entry + path = ext2RealPath(path); + target = ext2RealPath(target); + if (!path) { + errno = EINVAL; + return NULL; + } + + // Lock + ext2Lock(vd); + + // Clean me + // NOTE: this looks horrible right now and need a cleanup + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + + char * tmp_path = (targetdir && (type == S_IFLNK)) ? targetdir : dir; + if (strrchr(tmp_path, '/') != NULL) + { + char * ptr = strrchr(tmp_path, '/'); + name = strdup(ptr+1); + *ptr = '\0'; + } + else + name = strdup(tmp_path); + + // Open the entries parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + goto cleanup; + } + + // If not yet read, read the inode and block bitmap + if(!vd->fs->inode_map || !vd->fs->block_map) + ext2fs_read_bitmaps(vd->fs); + + // Symbolic link + if(type == S_IFLNK) + { + if (!target) { + errno = EINVAL; + goto cleanup; + } + + newentry = ext2CreateSymlink(vd, path, targetdir, name, type); + } + // Directory + else if(type == S_IFDIR) + { + newentry = ext2CreateMkDir(vd, dir_ni, LINUX_S_IFDIR | (0755 & ~vd->fs->umask), name); + } + // File + else if(type == S_IFREG) + { + newentry = ext2CreateFile(vd, dir_ni, LINUX_S_IFREG | (0755 & ~vd->fs->umask), name); + } + + // If the entry was created + if (newentry != 0) + { + // Sync the entry to disc + ext2Sync(vd, NULL); + + ni = ext2OpenEntry(vd, target ? target : path); + } + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(name) + mem_free(name); + + if(dir) + mem_free(dir); + + if(targetdir) + mem_free(targetdir); + + // Unlock + ext2Unlock(vd); + + return ni; +} + +/* + * Given a mode, return the ext2 file type + */ +static int ext2_file_type(unsigned int mode) +{ + if (LINUX_S_ISREG(mode)) + return EXT2_FT_REG_FILE; + + if (LINUX_S_ISDIR(mode)) + return EXT2_FT_DIR; + + if (LINUX_S_ISCHR(mode)) + return EXT2_FT_CHRDEV; + + if (LINUX_S_ISBLK(mode)) + return EXT2_FT_BLKDEV; + + if (LINUX_S_ISLNK(mode)) + return EXT2_FT_SYMLINK; + + if (LINUX_S_ISFIFO(mode)) + return EXT2_FT_FIFO; + + if (LINUX_S_ISSOCK(mode)) + return EXT2_FT_SOCK; + + return 0; +} + +int ext2Link(ext2_vd *vd, const char *old_path, const char *new_path) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + errcode_t err = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // You cannot link between devices + if(vd != ext2GetVolume(new_path)) { + errno = EXDEV; + return -1; + } + + // Get the actual paths of the entry + old_path = ext2RealPath(old_path); + new_path = ext2RealPath(new_path); + if (!old_path || !new_path) { + errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(vd); + + //check for existing in new path + ni = ext2OpenEntry(vd, new_path); + if (ni) { + ext2CloseEntry(vd, ni); + ni = NULL; + errno = EINVAL; + return -1; + } + + dir = strdup(new_path); + if (!dir) { + errno = EINVAL; + err = -1; + goto cleanup; + } + char * ptr = strrchr(dir, '/'); + if (ptr) + { + name = strdup(ptr+1); + *ptr = 0; + } + else + name = strdup(dir); + + // Find the entry + ni = ext2OpenEntry(vd, old_path); + if (!ni) { + errno = ENOENT; + err = -1; + goto cleanup; + } + + // Open the entries new parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + err = -1; + goto cleanup; + } + + do + { + // Link the entry to its new parent + err = ext2fs_link(vd->fs, dir_ni->ino, name, ni->ino, ext2_file_type(ni->ni.i_mode)); + if (err == EXT2_ET_DIR_NO_SPACE) + { + if (ext2fs_expand_dir(vd->fs, dir_ni->ino) != 0) + goto cleanup; + } + else if(err != 0) + { + errno = ENOMEM; + goto cleanup; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + ni->ni.i_links_count++; + + // Update entry times + ext2UpdateTimes(vd, ni, EXT2_UPDATE_MCTIME); + + // Sync the entry to disc + ext2Sync(vd, ni); + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(ni) + ext2CloseEntry(vd, ni); + + if(dir) + mem_free(dir); + + if(name) + mem_free(name); + + // Unlock + ext2Unlock(vd); + + return err; +} + +typedef struct _rd_struct +{ + ext2_ino_t parent; + int empty; +} rd_struct; + +static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) +{ + blk_t block; + + block = *blocknr; + ext2fs_block_alloc_stats(fs, block, -1); + *blocknr = 0; + return 0; +} + +static int unlink_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), + void *private_data) +{ + rd_struct *rds = (rd_struct *) private_data; + + if (dirent->inode == 0) + return 0; + if (((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) + return 0; + if (((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && + (dirent->name[1] == '.')) { + rds->parent = dirent->inode; + return 0; + } + + rds->empty = 0; + return 0; +} + +int ext2Unlink (ext2_vd *vd, const char *path) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + errcode_t err = -1; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // Get the actual path of the entry + path = ext2RealPath(path); + if (!path) { + errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(vd); + + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + char * ptr = strrchr(dir, '/'); + if (ptr) + { + name = strdup(ptr+1); + *ptr = 0; + } + else + name = dir; + + // Find the entry + ni = ext2OpenEntry(vd, path); + if (!ni) { + errno = ENOENT; + goto cleanup; + } + + // Open the entries parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + goto cleanup; + } + + // Directory + if(LINUX_S_ISDIR(ni->ni.i_mode)) + { + rd_struct rds; + rds.parent = 0; + rds.empty = 1; + + if (ext2fs_dir_iterate2(vd->fs, ni->ino, 0, 0, unlink_proc, &rds) != 0) + goto cleanup; + + if(!rds.empty) + goto cleanup; + + if (rds.parent) + { + struct ext2_inode inode; + if (ext2fs_read_inode(vd->fs, rds.parent, &inode) == 0) + { + if(inode.i_links_count > 1) + inode.i_links_count--; + ext2fs_write_inode(vd->fs, rds.parent, &inode); + } + } + + // set link count 0 + ni->ni.i_links_count = 0; + } + // File + else + { + ni->ni.i_links_count--; + } + + if(ni->ni.i_links_count <= 0) + { + ni->ni.i_size = 0; + ni->ni.i_size_high = 0; + ni->ni.i_links_count = 0; + ni->ni.i_dtime = (u32) time(0); + } + + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + + // Unlink the entry from its parent + if(ext2fs_unlink(vd->fs, dir_ni->ino, name, 0, 0) != 0) + goto cleanup; + + if (ext2fs_inode_has_valid_blocks(&ni->ni)) + { + ext2fs_block_iterate(vd->fs, ni->ino, 0, NULL, release_blocks_proc, NULL); + ext2fs_inode_alloc_stats2(vd->fs, ni->ino, -1, LINUX_S_ISDIR(ni->ni.i_mode)); + } + + if(ni->ni.i_links_count == 0) + { + // It's odd that i have to do this on my own and the lib is not doing that for me + blk64_t truncate_block = ((vd->fs->blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(vd->fs->super)) + 1; + ext2fs_punch(vd->fs, ni->ino, &ni->ni, 0, truncate_block, ~0ULL); + } + + // Sync the entry to disc + ext2Sync(vd, NULL); + + err = 0; + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(ni) + ext2CloseEntry(vd, ni); + + if(name) + mem_free(name); + + if(dir) + mem_free(dir); + + // Unlock + ext2Unlock(vd); + + return err; +} + + +int ext2Sync(ext2_vd *vd, ext2_inode_t *ni) +{ + errcode_t res = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // Lock + ext2Lock(vd); + + if(ni && ni->dirty) + { + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + ni->dirty = false; + } + + // Sync the entry + res = ext2fs_flush(vd->fs); + + // Force the underlying device to sync + vd->io->manager->flush(vd->io); + + // Unlock + ext2Unlock(vd); + + return res; + +} + +int ext2Stat (ext2_vd *vd, ext2_inode_t *ni_main, struct stat *st) +{ + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + struct ext2_inode * ni = ni_main ? &ni_main->ni : 0; + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Lock + ext2Lock(vd); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + if(LINUX_S_ISDIR(ni->i_mode)) + { + st->st_nlink = 1; + st->st_size = ni->i_size; + } + else + { + st->st_nlink = ni->i_links_count; + st->st_size = EXT2_I_SIZE(ni); + } + + st->st_mode = ni->i_mode; + st->st_blocks = ni->i_blocks; + st->st_blksize = vd->fs->blocksize; + + // Fill in the generic entry stats + st->st_dev = (dev_t) ((long) vd->fs); + st->st_uid = ni->i_uid | (((u32) ni->osd2.linux2.l_i_uid_high) << 16); + st->st_gid = ni->i_gid | (((u32) ni->osd2.linux2.l_i_gid_high) << 16); + st->st_ino = ni_main->ino; + st->st_atime = ni->i_atime; + st->st_ctime = ni->i_ctime; + st->st_mtime = ni->i_mtime; + + // Update entry times + ext2UpdateTimes(vd, ni_main, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(vd); + + return res; +} + +void ext2UpdateTimes(ext2_vd *vd, ext2_inode_t *ni, ext2_time_update_flags mask) +{ + // Sanity check + if(!ni || !mask) + return; + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return; + + u32 now = (u32) time(0); + + if(mask & EXT2_UPDATE_ATIME) + ni->ni.i_atime = now; + if(mask & EXT2_UPDATE_MTIME) + ni->ni.i_mtime = now; + if(mask & EXT2_UPDATE_CTIME) + ni->ni.i_ctime = now; + + ni->dirty = true; +} + +const char *ext2RealPath (const char *path) +{ + // Sanity check + if (!path) + return NULL; + + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':')+1; + } + if (strchr(path, ':') != NULL) { + return NULL; + } + + return path; +} diff --git a/lib/libext2fs/source/ext2_internal.h b/lib/libext2fs/source/ext2_internal.h new file mode 100644 index 0000000..24dd4a5 --- /dev/null +++ b/lib/libext2fs/source/ext2_internal.h @@ -0,0 +1,102 @@ +/** + * ext2_internal.h + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef EXT2_INTERNAL_H_ +#define EXT2_INTERNAL_H_ + +#include +#include +#include +#include "ext2fs.h" +#include "ext2_fs.h" +#include "mem_allocate.h" + +#ifdef DEBUG_GEKKO +#define ext2_log_trace printf +#else +#define ext2_log_trace(...) +#endif + +typedef struct _ext2_inode_t +{ + struct ext2_inode ni; + ext2_ino_t ino; + bool dirty; +} ext2_inode_t; + +/** + * ext2_vd - EXT2 volume descriptor + */ +typedef struct _ext2_vd +{ + io_channel io; /* EXT device handle */ + ext2_filsys fs; /* EXT volume handle */ + mutex_t lock; /* Volume lock mutex */ + ext2_inode_t *cwd_ni; /* Current directory */ + struct _ext2_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */ + struct _ext2_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */ + u16 openDirCount; /* The total number of directories currently open in this volume */ + u16 openFileCount; /* The total number of files currently open in this volume */ + ext2_ino_t root; /* Root node */ +} ext2_vd; + +typedef enum { + EXT2_UPDATE_ATIME = 0x01, + EXT2_UPDATE_MTIME = 0x02, + EXT2_UPDATE_CTIME = 0x04, + EXT2_UPDATE_AMTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_MTIME, + EXT2_UPDATE_ACTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_CTIME, + EXT2_UPDATE_MCTIME = EXT2_UPDATE_MTIME | EXT2_UPDATE_CTIME, + EXT2_UPDATE_AMCTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_MTIME | EXT2_UPDATE_CTIME, +} ext2_time_update_flags; + +/* Lock volume */ +static inline void ext2Lock (ext2_vd *vd) +{ + LWP_MutexLock(vd->lock); +} + +/* Unlock volume */ +static inline void ext2Unlock (ext2_vd *vd) +{ + LWP_MutexUnlock(vd->lock); +} + +const char *ext2RealPath (const char *path); +int ext2InitVolume (ext2_vd *vd); +void ext2DeinitVolume (ext2_vd *vd); +ext2_vd *ext2GetVolume (const char *path); + +int ext2AddDevice (const char *name, void *deviceData); +void ext2RemoveDevice (const char *path); +const devoptab_t *ext2GetDevice (const char *path); + +ext2_inode_t *ext2OpenEntry (ext2_vd *vd, const char *path); +void ext2CloseEntry (ext2_vd *vd, ext2_inode_t * ni); +int ext2Stat (ext2_vd *vd, ext2_inode_t * ni, struct stat *st); +int ext2Sync (ext2_vd *vd, ext2_inode_t * ni); + +ext2_inode_t *ext2Create (ext2_vd *vd, const char *path, mode_t type, const char *target); +int ext2Link (ext2_vd *vd, const char *old_path, const char *new_path); +int ext2Unlink (ext2_vd *vd, const char *path); + +void ext2UpdateTimes(ext2_vd *vd, ext2_inode_t *ni, ext2_time_update_flags mask); + +#endif diff --git a/lib/libext2fs/source/ext2_io.h b/lib/libext2fs/source/ext2_io.h new file mode 100644 index 0000000..9f84d99 --- /dev/null +++ b/lib/libext2fs/source/ext2_io.h @@ -0,0 +1,144 @@ +/* + * io.h --- the I/O manager abstraction + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _EXT2FS_EXT2_IO_H +#define _EXT2FS_EXT2_IO_H + +#include "ext2fs.h" + +/* + * ext2_loff_t is defined here since unix_io.c needs it. + */ +typedef long long ext2_loff_t; + +/* llseek.c */ +ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); + +typedef struct struct_io_manager *io_manager; +typedef struct struct_io_channel *io_channel; +typedef struct struct_io_stats *io_stats; + +#define CHANNEL_FLAGS_WRITETHROUGH 0x01 +#define CHANNEL_FLAGS_DISCARD_ZEROES 0x02 + +#define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES) + +struct struct_io_channel { + errcode_t magic; + io_manager manager; + char *name; + int block_size; + errcode_t (*read_error)(io_channel channel, + unsigned long block, + int count, + void *data, + size_t size, + int actual_bytes_read, + errcode_t error); + errcode_t (*write_error)(io_channel channel, + unsigned long block, + int count, + const void *data, + size_t size, + int actual_bytes_written, + errcode_t error); + int refcount; + int flags; + long reserved[14]; + void *private_data; + void *app_data; +}; + +struct struct_io_stats { + int num_fields; + int reserved; + unsigned long long bytes_read; + unsigned long long bytes_written; +}; + +struct struct_io_manager { + errcode_t magic; + const char *name; + errcode_t (*open)(const char *name, int flags, io_channel *channel); + errcode_t (*close)(io_channel channel); + errcode_t (*set_blksize)(io_channel channel, int blksize); + errcode_t (*read_blk)(io_channel channel, unsigned long block, + int count, void *data); + errcode_t (*write_blk)(io_channel channel, unsigned long block, + int count, const void *data); + errcode_t (*flush)(io_channel channel); + errcode_t (*write_byte)(io_channel channel, unsigned long offset, + int count, const void *data); + errcode_t (*set_option)(io_channel channel, const char *option, + const char *arg); + errcode_t (*get_stats)(io_channel channel, io_stats *io_stats); + errcode_t (*read_blk64)(io_channel channel, unsigned long long block, + int count, void *data); + errcode_t (*write_blk64)(io_channel channel, unsigned long long block, + int count, const void *data); + errcode_t (*discard)(io_channel channel, unsigned long long block, + unsigned long long count); + long reserved[16]; +}; + +#define IO_FLAG_RW 0x0001 +#define IO_FLAG_EXCLUSIVE 0x0002 +#define IO_FLAG_DIRECT_IO 0x0004 + +/* + * Convenience functions.... + */ +#define io_channel_close(c) ((c)->manager->close((c))) +#define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) +#define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) +#define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) +#define io_channel_flush(c) ((c)->manager->flush((c))) +#define io_channel_bumpcount(c) ((c)->refcount++) + +/* io_manager.c */ +extern errcode_t io_channel_set_options(io_channel channel, + const char *options); +extern errcode_t io_channel_write_byte(io_channel channel, + unsigned long offset, + int count, const void *data); +extern errcode_t io_channel_read_blk64(io_channel channel, + unsigned long long block, + int count, void *data); +extern errcode_t io_channel_write_blk64(io_channel channel, + unsigned long long block, + int count, const void *data); +extern errcode_t io_channel_discard(io_channel channel, + unsigned long long block, + unsigned long long count); + +/* unix_io.c */ +extern io_manager unix_io_manager; + +/* undo_io.c */ +extern io_manager undo_io_manager; +extern errcode_t set_undo_io_backing_manager(io_manager manager); +extern errcode_t set_undo_io_backup_file(char *file_name); + +/* test_io.c */ +extern io_manager test_io_manager, test_io_backing_manager; +extern void (*test_io_cb_read_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_write_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_read_blk64) + (unsigned long long block, int count, errcode_t err); +extern void (*test_io_cb_write_blk64) + (unsigned long long block, int count, errcode_t err); +extern void (*test_io_cb_set_blksize) + (int blksize, errcode_t err); + +#endif /* _EXT2FS_EXT2_IO_H */ + diff --git a/lib/libext2fs/source/ext2_types.h b/lib/libext2fs/source/ext2_types.h new file mode 100644 index 0000000..5f5f7a5 --- /dev/null +++ b/lib/libext2fs/source/ext2_types.h @@ -0,0 +1,18 @@ +/* + * If linux/types.h is already been included, assume it has defined + * everything we need. (cross fingers) Other header files may have + * also defined the types that we need. + */ +#ifndef _EXT2_TYPES_H +#define _EXT2_TYPES_H + +typedef unsigned char __u8; +typedef signed char __s8; +typedef unsigned short __u16; +typedef short __s16; +typedef unsigned int __u32; +typedef int __s32; +typedef unsigned long long __u64; +typedef signed long long __s64; + +#endif /* _EXT2_TYPES_H */ diff --git a/lib/libext2fs/source/ext2dir.c b/lib/libext2fs/source/ext2dir.c new file mode 100644 index 0000000..ee7ce3c --- /dev/null +++ b/lib/libext2fs/source/ext2dir.c @@ -0,0 +1,657 @@ +/** + * ext2_dir.c - devoptab directory routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ext2_internal.h" +#include "ext2dir.h" +#include + +#define STATE(x) ((ext2_dir_state*)(x)->dirStruct) + +void ext2CloseDir (ext2_dir_state *dir) +{ + // Sanity check + if (!dir || !dir->vd) + return; + + // Free the directory entries (if any) + while (dir->first) { + ext2_dir_entry *next = dir->first->next; + mem_free(dir->first->name); + mem_free(dir->first); + dir->first = next; + } + + // Close the directory (if open) + if (dir->ni) + ext2CloseEntry(dir->vd, dir->ni); + + // Reset the directory state + dir->ni = NULL; + dir->first = NULL; + dir->current = NULL; + + return; +} + +int ext2_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + // Short circuit cases were we don't actually have to do anything + if (!st || !path) + return 0; + + ext2_log_trace("path %s, st %p\n", path, st); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFDIR; + return 0; + } + + // Lock + ext2Lock(vd); + + // Find the entry + ni = ext2OpenEntry(vd, path); + if (!ni) { + r->_errno = errno; + ext2Unlock(vd); + return -1; + } + + // Get the entry stats + int ret = ext2Stat(vd, ni, st); + if (ret) + r->_errno = errno; + + // Close the entry + ext2CloseEntry(vd, ni); + + ext2Unlock(vd); + + return 0; +} + +int ext2_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + ext2_log_trace("existing %s, newLink %s\n", existing, newLink); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(existing); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Create a symbolic link between the two paths + ni = ext2Create(vd, existing, S_IFLNK, newLink); + if (!ni) { + ext2Unlock(vd); + r->_errno = errno; + return -1; + } + + // Close the symbolic link + ext2CloseEntry(vd, ni); + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_unlink_r (struct _reent *r, const char *name) +{ + ext2_log_trace("name %s\n", name); + + ext2_vd *vd = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Unlink the entry + int ret = ext2Unlink(vd, name); + if (ret) + r->_errno = errno; + + return ret; +} + +int ext2_chdir_r (struct _reent *r, const char *name) +{ + ext2_log_trace("name %s\n", name); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Find the directory + ni = ext2OpenEntry(vd, name); + if (!ni) { + ext2Unlock(vd); + r->_errno = ENOENT; + return -1; + } + + // Ensure that this directory is indeed a directory + if (!LINUX_S_ISDIR(ni->ni.i_mode)) { + ext2CloseEntry(vd, ni); + ext2Unlock(vd); + r->_errno = ENOTDIR; + return -1; + } + + // Close the old current directory (if any) + if (vd->cwd_ni) + ext2CloseEntry(vd, vd->cwd_ni); + + // Set the new current directory + vd->cwd_ni = ni; + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + ext2_log_trace("oldName %s, newName %s\n", oldName, newName); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(oldName); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // You cannot rename between devices + if(vd != ext2GetVolume(newName)) { + ext2Unlock(vd); + r->_errno = EXDEV; + return -1; + } + + // Check that there is no existing entry with the new name + ni = ext2OpenEntry(vd, newName); + if (ni) { + ext2CloseEntry(vd, ni); + ext2Unlock(vd); + r->_errno = EEXIST; + return -1; + } + + // Link the old entry with the new one + if (ext2Link(vd, oldName, newName)) { + ext2Unlock(vd); + return -1; + } + + // Unlink the old entry + if (ext2Unlink(vd, oldName)) { + if (ext2Unlink(vd, newName)) { + ext2Unlock(vd); + return -1; + } + ext2Unlock(vd); + return -1; + } + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_mkdir_r (struct _reent *r, const char *path, int mode) +{ + ext2_log_trace("path %s, mode %i\n", path, mode); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Create the directory + ni = ext2Create(vd, path, S_IFDIR, NULL); + if (!ni) { + ext2Unlock(vd); + r->_errno = errno; + return -1; + } + + // Close the directory + ext2CloseEntry(vd, ni); + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + ext2_log_trace("path %s, buf %p\n", path, buf); + + ext2_vd *vd = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Lock + ext2Lock(vd); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + // File system block size + switch(vd->fs->super->s_log_block_size) + { + case 1: + buf->f_bsize = 2048; + break; + case 2: + buf->f_bsize = 4096; + break; + case 3: + buf->f_bsize = 8192; + break; + default: + case 0: + buf->f_bsize = 1024; + break; + } + + // Fundamental file system block size + buf->f_frsize = buf->f_bsize; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = vd->fs->super->s_blocks_count | (((u64) vd->fs->super->s_blocks_count_hi) << 32); + + // Free blocks available for all and for non-privileged processes + buf->f_bfree = vd->fs->super->s_free_blocks_count | (((u64) vd->fs->super->s_free_blocks_hi) << 32); + + // Number of inodes at this point in time + buf->f_files = vd->fs->super->s_inodes_count; + + // Free inodes available for all and for non-privileged processes + buf->f_ffree = vd->fs->super->s_free_inodes_count; + + // File system id + buf->f_fsid = vd->fs->super->s_magic; + + // Bit mask of f_flag values. + buf->f_flag = vd->fs->super->s_flags; + + // Maximum length of filenames + buf->f_namemax = EXT2_NAME_LEN; + + // Unlock + ext2Unlock(vd); + + return 0; +} + +/** + * PRIVATE: Callback for directory walking + */ +static int DirIterateCallback(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *dirState) +{ + // Sanity check + if(!dirent) + { + errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(((DIR_ITER *) dirState)); + + // Sanity check + if (!dir || !dir->vd) { + errno = EINVAL; + return -1; + } + + //skip ".." on root directory + if(dir->ni->ino == dir->vd->root && strcmp(dirent->name, "..") == 0) + { + return EXT2_ET_OK; + } + + // Allocate a new directory entry + ext2_dir_entry *entry = (ext2_dir_entry *) mem_alloc(sizeof(ext2_dir_entry)); + if (!entry) + { + errno = ENOMEM; + return -1; + } + + memset(entry, 0, sizeof(ext2_dir_entry)); + + int stringlen = dirent->name_len & 0xFF; + + entry->name = mem_alloc(stringlen+1); + if(!entry->name) + { + mem_free(entry); + errno = ENOMEM; + return -1; + } + + // The null termination is not necessarily there in the fs, we gotta do it + int i; + for(i = 0; i < stringlen; ++i) + entry->name[i] = dirent->name[i]; + entry->name[i] = '\0'; + + // Link the entry to the directory + if (!dir->first) { + dir->first = entry; + dir->length = dirent->rec_len; + } else { + ext2_dir_entry *last = dir->first; + while (last->next) last = last->next; + last->next = entry; + } + + return EXT2_ET_OK; +} + +DIR_ITER *ext2_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + ext2_log_trace("dirState %p, path %s\n", dirState, path); + + if(!dirState) + { + r->_errno = EINVAL; + return NULL; + } + + ext2_dir_state* dir = STATE(dirState); + + if(!dir) + { + r->_errno = EINVAL; + return NULL; + } + + // Get the volume descriptor for this path + dir->vd = ext2GetVolume(path); + if (!dir->vd) { + r->_errno = ENODEV; + return NULL; + } + + // Lock + ext2Lock(dir->vd); + + // Find the directory + dir->ni = ext2OpenEntry(dir->vd, path); + if (!dir->ni) { + ext2Unlock(dir->vd); + r->_errno = ENOENT; + return NULL; + } + + // Ensure that this directory is indeed a directory + if (!LINUX_S_ISDIR(dir->ni->ni.i_mode)) { + ext2CloseEntry(dir->vd, dir->ni); + ext2Unlock(dir->vd); + r->_errno = ENOTDIR; + return NULL; + } + + // Read the directory + dir->first = dir->current = NULL; + if (ext2fs_dir_iterate(dir->vd->fs, dir->ni->ino, 0, 0, DirIterateCallback, dirState) != EXT2_ET_OK) { + ext2CloseDir(dir); + ext2Unlock(dir->vd); + r->_errno = errno; + return NULL; + } + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Insert the directory into the double-linked FILO list of open directories + if (dir->vd->firstOpenDir) { + dir->nextOpenDir = dir->vd->firstOpenDir; + dir->vd->firstOpenDir->prevOpenDir = dir; + } else { + dir->nextOpenDir = NULL; + } + dir->prevOpenDir = NULL; + dir->vd->cwd_ni = dir->ni; + dir->vd->firstOpenDir = dir; + dir->vd->openDirCount++; + + // Unlock + ext2Unlock(dir->vd); + + return dirState; +} + +int ext2_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + ext2_log_trace("dirState %p\n", dirState); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} + +int ext2_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + ext2_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + ext2_inode_t *ni = NULL; + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Check that there is a entry waiting to be fetched + if (!dir->current) { + ext2Unlock(dir->vd); + r->_errno = ENOENT; + return -1; + } + + // Fetch the current entry + strcpy(filename, dir->current->name); + if(filestat != NULL) + { + if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) + { + memset(filestat, 0, sizeof(struct stat)); + filestat->st_mode = S_IFDIR; + } + else + { + ni = ext2OpenEntry(dir->vd, dir->current->name); + if (ni) { + ext2Stat(dir->vd, ni, filestat); + ext2CloseEntry(dir->vd, ni); + } + } + } + + // Move to the next entry in the directory + dir->current = dir->current->next; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} + +int ext2_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + ext2_log_trace("dirState %p\n", dirState); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Close the directory + ext2CloseDir(dir); + + // Remove the directory from the double-linked FILO list of open directories + dir->vd->openDirCount--; + if (dir->nextOpenDir) + dir->nextOpenDir->prevOpenDir = dir->prevOpenDir; + if (dir->prevOpenDir) + dir->prevOpenDir->nextOpenDir = dir->nextOpenDir; + else + dir->vd->firstOpenDir = dir->nextOpenDir; + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} diff --git a/lib/libext2fs/source/ext2dir.h b/lib/libext2fs/source/ext2dir.h new file mode 100644 index 0000000..26081bb --- /dev/null +++ b/lib/libext2fs/source/ext2dir.h @@ -0,0 +1,69 @@ +/** + * ext2_dir.h - devoptab directory routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _EXT2DIR_H +#define _EXT2DIR_H + +#include +#include "ext2_internal.h" + +/** + * ext2_dir_entry - Directory entry + */ +typedef struct _ext2_dir_entry { + char *name; + struct _ext2_dir_entry *next; +} ext2_dir_entry; + +/** + * ext2_dir_state - Directory state + */ +typedef struct _ext2_dir_state { + ext2_vd *vd; /* Volume this directory belongs to */ + ext2_inode_t *ni; /* Directory descriptor */ + unsigned short length; /* Directory length */ + ext2_dir_entry *first; /* The first entry in the directory */ + ext2_dir_entry *current; /* The current entry in the directory */ + struct _ext2_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */ + struct _ext2_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */ +} ext2_dir_state; + +/* Directory state routines */ +void ext2CloseDir (ext2_dir_state *file); + +/* Gekko devoptab directory routines for EXT2-based devices */ +extern int ext2_stat_r (struct _reent *r, const char *path, struct stat *st); +extern int ext2_link_r (struct _reent *r, const char *existing, const char *newLink); +extern int ext2_unlink_r (struct _reent *r, const char *name); +extern int ext2_chdir_r (struct _reent *r, const char *name); +extern int ext2_rename_r (struct _reent *r, const char *oldName, const char *newName); +extern int ext2_mkdir_r (struct _reent *r, const char *path, int mode); +extern int ext2_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* Gekko devoptab directory walking routines for EXT2-based devices */ +extern DIR_ITER *ext2_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path); +extern int ext2_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int ext2_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int ext2_dirclose_r (struct _reent *r, DIR_ITER *dirState); + +#endif /* _EXT2DIR_H */ + diff --git a/lib/libext2fs/source/ext2file.c b/lib/libext2fs/source/ext2file.c new file mode 100644 index 0000000..6647466 --- /dev/null +++ b/lib/libext2fs/source/ext2file.c @@ -0,0 +1,413 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "ext2_internal.h" +#include "ext2file.h" + +#define STATE(x) ((ext2_file_state*)x) + +void ext2CloseFile (ext2_file_state *file) +{ + // Sanity check + if (!file || !file->vd) + return; + + ext2fs_file_close(file->fd); + + // Sync the file (and its attributes) to disc + if(file->write) + { + // Read in node changes before writing them + ext2fs_read_inode(file->vd->fs, file->ni->ino, &file->ni->ni); + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ACTIME); + } + + if (file->read) + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ATIME); + + ext2Sync(file->vd, file->ni); + + // Close the file (if open) + if (file->ni) + ext2CloseEntry(file->vd, file->ni); + + // Reset the file state + file->ni = NULL; + file->fd = NULL; + file->flags = 0; + file->read = false; + file->write = false; + file->append = false; + + return; +} + +int ext2_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + ext2_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode); + + ext2_file_state* file = STATE(fileStruct); + + // Get the volume descriptor for this path + file->vd = ext2GetVolume(path); + if (!file->vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Determine which mode the file is opened for + file->flags = flags; + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + } else { + r->_errno = EACCES; + ext2Unlock(file->vd); + return -1; + } + + // Try and find the file and (if found) ensure that it is not a directory + file->ni = ext2OpenEntry(file->vd, path); + if (file->ni && LINUX_S_ISDIR(file->ni->ni.i_mode)) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = EISDIR; + return -1; + } + + // Are we creating this file? + if ((flags & O_CREAT) && !file->ni) + // Create the file + file->ni = ext2Create(file->vd, path, S_IFREG, NULL); + + // Sanity check, the file should be open by now + if (!file->ni) { + ext2Unlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + // Make sure we aren't trying to write to a read-only file + if (!(file->vd->fs->flags & EXT2_FLAG_RW) && file->write) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = EROFS; + return -1; + } + + errcode_t err = ext2fs_file_open2(file->vd->fs, file->ni->ino, &file->ni->ni, + file->write ? EXT2_FLAG_RW : 0, &file->fd); + if(err != 0) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write) { + if (ext2fs_file_set_size2(file->fd, 0) != 0) { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = errno; + return -1; + } + file->ni->ni.i_size = file->ni->ni.i_size_high = 0; + } + + // Set the files current position + ext2fs_file_llseek(file->fd, file->append ? EXT2_I_SIZE(&file->ni->ni) : 0, SEEK_SET, 0); + + ext2_log_trace("file->len %lld\n", EXT2_I_SIZE(&file->ni->ni)); + + // Update file times + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ATIME); + + // Insert the file into the double-linked FILO list of open files + if (file->vd->firstOpenFile) { + file->nextOpenFile = file->vd->firstOpenFile; + file->vd->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + file->vd->firstOpenFile = file; + file->vd->openFileCount++; + + // Sync access time + ext2Sync(file->vd, file->ni); + + // Unlock + ext2Unlock(file->vd); + + return (int)fileStruct; +} + +int ext2_close_r (struct _reent *r, int fd) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Close the file + ext2CloseFile(file); + + // Remove the file from the double-linked FILO list of open files + file->vd->openFileCount--; + if (file->nextOpenFile) + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + if (file->prevOpenFile) + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + else + file->vd->firstOpenFile = file->nextOpenFile; + + // Unlock + ext2Unlock(file->vd); + + return 0; +} + +ssize_t ext2_write_r (struct _reent *r, int fd, const char *ptr, size_t len) +{ + ext2_log_trace("fd %p, ptr %p, len %i\n", (void *) fd, ptr, len); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Check that we are allowed to write to this file + if (!file->write) { + r->_errno = EACCES; + return -1; + } + + // Lock + ext2Lock(file->vd); + + u32 writen = 0; + + // Write to the files data atrribute + errcode_t err = ext2fs_file_write(file->fd, ptr, len, &writen); + if (writen <= 0 || err) { + ext2Unlock(file->vd); + r->_errno = errno; + return (err ? err : -1); + } + + // Unlock + ext2Unlock(file->vd); + + return (writen == 0 ? -1 : writen); +} + +ssize_t ext2_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + ext2_log_trace("fd %p, ptr %p, len %i\n", (void *) fd, ptr, len); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ext2Lock(file->vd); + + // Check that we are allowed to read from this file + if (!file->read) { + ext2Unlock(file->vd); + r->_errno = EACCES; + return -1; + } + + u32 read = 0; + errcode_t err = 0; + + // Read from the files data attribute + err = ext2fs_file_read(file->fd, ptr, len, &read); + if (err || read <= 0 || read > len) { + ext2Unlock(file->vd); + r->_errno = errno; + return err ? err : -1; + } + + // Unlock + ext2Unlock(file->vd); + + return (read == 0) ? -1 : read; +} + +off_t ext2_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + ext2_log_trace("fd %p, pos %lli, dir %i\n", (void *) fd, pos, dir); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + u64 pos_loaded = 0; + + ext2fs_file_llseek(file->fd, pos, dir, &pos_loaded); + + return (off_t) pos_loaded; +} + +int ext2_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Get the file stats + ret = ext2Stat(file->vd, file->ni, st); + if (ret) + r->_errno = errno; + + return ret; +} + +int ext2_ftruncate_r (struct _reent *r, int fd, off_t len) +{ + ext2_log_trace("fd %p, len %Li\n", (void *) fd, len); + + ext2_file_state* file = STATE(fd); + errcode_t err = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ext2Unlock(file->vd); + r->_errno = EACCES; + return -1; + } + + err = ext2fs_file_set_size2(file->fd, len); + + // Sync the file (and its attributes) to disc + if(!err) + ext2Sync(file->vd, file->ni); + + // update times + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_AMTIME); + + // Unlock + ext2Unlock(file->vd); + + return err; +} + +int ext2_fsync_r (struct _reent *r, int fd) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Sync the file (and its attributes) to disc + ret = ext2fs_file_flush(file->fd); + if (ret) + r->_errno = ret; + + // Unlock + ext2Unlock(file->vd); + + return ret; +} diff --git a/lib/libext2fs/source/ext2file.h b/lib/libext2fs/source/ext2file.h new file mode 100644 index 0000000..249ba52 --- /dev/null +++ b/lib/libext2fs/source/ext2file.h @@ -0,0 +1,60 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _EXT2FILE_H +#define _EXT2FILE_H + +#include +#include "ext2_internal.h" + +/** + * ext2_file_state - File state + */ +typedef struct _ext2_file_state { + ext2_vd *vd; /* Volume this file belongs to */ + ext2_inode_t *ni; /* File inode */ + ext2_file_t fd; /* File descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ +// bool compressed; /* True if file data is compressed */ +// bool encrypted; /* True if file data is encryted */ + struct _ext2_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _ext2_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} ext2_file_state; + +/* File state routines */ +void ext2CloseFile (ext2_file_state *file); + +/* Gekko devoptab file routines for EXT2-based devices */ +extern int ext2_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +extern int ext2_close_r (struct _reent *r, int fd); +extern ssize_t ext2_write_r (struct _reent *r, int fd, const char *ptr, size_t len); +extern ssize_t ext2_read_r (struct _reent *r, int fd, char *ptr, size_t len); +extern off_t ext2_seek_r (struct _reent *r, int fd, off_t pos, int dir); +extern int ext2_fstat_r (struct _reent *r, int fd, struct stat *st); +extern int ext2_ftruncate_r (struct _reent *r, int fd, off_t len); +extern int ext2_fsync_r (struct _reent *r, int fd); + +#endif /* _EXT2FILE_H */ + diff --git a/lib/libext2fs/source/ext2fs.h b/lib/libext2fs/source/ext2fs.h new file mode 100644 index 0000000..7016486 --- /dev/null +++ b/lib/libext2fs/source/ext2fs.h @@ -0,0 +1,1572 @@ +/* + * ext2fs.h --- ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _EXT2FS_EXT2FS_H +#define _EXT2FS_EXT2FS_H + +#ifdef __GNUC__ +#define EXT2FS_ATTR(x) __attribute__(x) +#else +#define EXT2FS_ATTR(x) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Non-GNU C compilers won't necessarily understand inline + */ +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define NO_INLINE_FUNCS +#endif + +/* + * Where the master copy of the superblock is located, and how big + * superblocks are supposed to be. We define SUPERBLOCK_SIZE because + * the size of the superblock structure is not necessarily trustworthy + * (some versions have the padding set up so that the superblock is + * 1032 bytes long). + */ +#define SUPERBLOCK_OFFSET 1024 +#define SUPERBLOCK_SIZE 1024 + +/* + * The last ext2fs revision level that this version of the library is + * able to support. + */ +#define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include + +#include "ext2_types.h" +#include "com_err.h" +#include "ext2_fs.h" +#include "ext3_extents.h" + +typedef __u32 ext2_ino_t; +typedef __u32 blk_t; +typedef __u64 blk64_t; +typedef __u32 dgrp_t; +typedef __u32 ext2_off_t; +typedef __u64 ext2_off64_t; +typedef __s64 e2_blkcnt_t; +typedef __u32 ext2_dirhash_t; + +#include "ext2_io.h" +#include "ext2_err.h" +#include "ext2_ext_attr.h" + +/* + * Portability help for Microsoft Visual C++ + */ +#ifdef _MSC_VER +#define EXT2_QSORT_TYPE int __cdecl +#else +#define EXT2_QSORT_TYPE int +#endif + +typedef struct struct_ext2_filsys *ext2_filsys; + +#define EXT2FS_MARK_ERROR 0 +#define EXT2FS_UNMARK_ERROR 1 +#define EXT2FS_TEST_ERROR 2 + +typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; + +#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) + + +/* + * Badblocks list definitions + */ + +typedef struct ext2_struct_u32_list *ext2_badblocks_list; +typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; + +typedef struct ext2_struct_u32_list *ext2_u32_list; +typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; + +/* old */ +typedef struct ext2_struct_u32_list *badblocks_list; +typedef struct ext2_struct_u32_iterate *badblocks_iterate; + +#define BADBLOCKS_FLAG_DIRTY 1 + +/* + * ext2_dblist structure and abstractions (see dblist.c) + */ +struct ext2_db_entry2 { + ext2_ino_t ino; + blk64_t blk; + e2_blkcnt_t blockcnt; +}; + +/* Ye Olde 32-bit version */ +struct ext2_db_entry { + ext2_ino_t ino; + blk_t blk; + int blockcnt; +}; + +typedef struct ext2_struct_dblist *ext2_dblist; + +#define DBLIST_ABORT 1 + +/* + * ext2_fileio definitions + */ + +#define EXT2_FILE_WRITE 0x0001 +#define EXT2_FILE_CREATE 0x0002 + +#define EXT2_FILE_MASK 0x00FF + +#define EXT2_FILE_BUF_DIRTY 0x4000 +#define EXT2_FILE_BUF_VALID 0x2000 + +typedef struct ext2_file *ext2_file_t; + +#define EXT2_SEEK_SET 0 +#define EXT2_SEEK_CUR 1 +#define EXT2_SEEK_END 2 + +/* + * Flags for the ext2_filsys structure and for ext2fs_open() + */ +#define EXT2_FLAG_RW 0x01 +#define EXT2_FLAG_CHANGED 0x02 +#define EXT2_FLAG_DIRTY 0x04 +#define EXT2_FLAG_VALID 0x08 +#define EXT2_FLAG_IB_DIRTY 0x10 +#define EXT2_FLAG_BB_DIRTY 0x20 +#define EXT2_FLAG_SWAP_BYTES 0x40 +#define EXT2_FLAG_SWAP_BYTES_READ 0x80 +#define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 +#define EXT2_FLAG_MASTER_SB_ONLY 0x200 +#define EXT2_FLAG_FORCE 0x400 +#define EXT2_FLAG_SUPER_ONLY 0x800 +#define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 +#define EXT2_FLAG_IMAGE_FILE 0x2000 +#define EXT2_FLAG_EXCLUSIVE 0x4000 +#define EXT2_FLAG_SOFTSUPP_FEATURES 0x8000 +#define EXT2_FLAG_NOFREE_ON_ERROR 0x10000 +#define EXT2_FLAG_64BITS 0x20000 +#define EXT2_FLAG_PRINT_PROGRESS 0x40000 +#define EXT2_FLAG_DIRECT_IO 0x80000 + +/* + * Special flag in the ext2 inode i_flag field that means that this is + * a new inode. (So that ext2_write_inode() can clear extra fields.) + */ +#define EXT2_NEW_INODE_FL 0x80000000 + +/* + * Flags for mkjournal + * + * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock + */ +#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 + +struct opaque_ext2_group_desc; + +struct struct_ext2_filsys { + errcode_t magic; + io_channel io; + int flags; + char * device_name; + struct ext2_super_block * super; + unsigned int blocksize; + int clustersize; + dgrp_t group_desc_count; + unsigned long desc_blocks; + struct opaque_ext2_group_desc * group_desc; + int inode_blocks_per_group; + ext2fs_inode_bitmap inode_map; + ext2fs_block_bitmap block_map; + /* XXX FIXME-64: not 64-bit safe, but not used? */ + errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); + errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); + errcode_t (*write_bitmaps)(ext2_filsys fs); + errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + ext2_badblocks_list badblocks; + ext2_dblist dblist; + __u32 stride; /* for mke2fs */ + struct ext2_super_block * orig_super; + struct ext2_image_hdr * image_header; + __u32 umask; + time_t now; + /* + * Reserved for future expansion + */ + __u32 reserved[7]; + + /* + * Reserved for the use of the calling application. + */ + void * priv_data; + + /* + * Inode cache + */ + struct ext2_inode_cache *icache; + io_channel image_io; + + /* + * More callback functions + */ + errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal, + blk64_t *ret); + void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse); +}; + +#include "bitops.h" + +/* + * Return flags for the block iterator functions + */ +#define BLOCK_CHANGED 1 +#define BLOCK_ABORT 2 +#define BLOCK_ERROR 4 + +/* + * Block interate flags + * + * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator + * function should be called on blocks where the block number is zero. + * This is used by ext2fs_expand_dir() to be able to add a new block + * to an inode. It can also be used for programs that want to be able + * to deal with files that contain "holes". + * + * BLOCK_FLAG_DEPTH_TRAVERSE indicates that the iterator function for + * the indirect, doubly indirect, etc. blocks should be called after + * all of the blocks containined in the indirect blocks are processed. + * This is useful if you are going to be deallocating blocks from an + * inode. + * + * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be + * called for data blocks only. + * + * BLOCK_FLAG_READ_ONLY is a promise by the caller that it will not + * modify returned block number. + * + * BLOCK_FLAG_NO_LARGE is for internal use only. It informs + * ext2fs_block_iterate2 that large files won't be accepted. + */ +#define BLOCK_FLAG_APPEND 1 +#define BLOCK_FLAG_HOLE 1 +#define BLOCK_FLAG_DEPTH_TRAVERSE 2 +#define BLOCK_FLAG_DATA_ONLY 4 +#define BLOCK_FLAG_READ_ONLY 8 + +#define BLOCK_FLAG_NO_LARGE 0x1000 + +/* + * Magic "block count" return values for the block iterator function. + */ +#define BLOCK_COUNT_IND (-1) +#define BLOCK_COUNT_DIND (-2) +#define BLOCK_COUNT_TIND (-3) +#define BLOCK_COUNT_TRANSLATOR (-4) + +/* + * Flags for ext2fs_move_blocks + */ +#define EXT2_BMOVE_GET_DBLIST 0x0001 +#define EXT2_BMOVE_DEBUG 0x0002 + +/* + * Generic (non-filesystem layout specific) extents structure + */ + +#define EXT2_EXTENT_FLAGS_LEAF 0x0001 +#define EXT2_EXTENT_FLAGS_UNINIT 0x0002 +#define EXT2_EXTENT_FLAGS_SECOND_VISIT 0x0004 + +struct ext2fs_extent { + blk64_t e_pblk; /* first physical block */ + blk64_t e_lblk; /* first logical block extent covers */ + __u32 e_len; /* number of blocks covered by extent */ + __u32 e_flags; /* extent flags */ +}; + +typedef struct ext2_extent_handle *ext2_extent_handle_t; +typedef struct ext2_extent_path *ext2_extent_path_t; + +/* + * Flags used by ext2fs_extent_get() + */ +#define EXT2_EXTENT_CURRENT 0x0000 +#define EXT2_EXTENT_MOVE_MASK 0x000F +#define EXT2_EXTENT_ROOT 0x0001 +#define EXT2_EXTENT_LAST_LEAF 0x0002 +#define EXT2_EXTENT_FIRST_SIB 0x0003 +#define EXT2_EXTENT_LAST_SIB 0x0004 +#define EXT2_EXTENT_NEXT_SIB 0x0005 +#define EXT2_EXTENT_PREV_SIB 0x0006 +#define EXT2_EXTENT_NEXT_LEAF 0x0007 +#define EXT2_EXTENT_PREV_LEAF 0x0008 +#define EXT2_EXTENT_NEXT 0x0009 +#define EXT2_EXTENT_PREV 0x000A +#define EXT2_EXTENT_UP 0x000B +#define EXT2_EXTENT_DOWN 0x000C +#define EXT2_EXTENT_DOWN_AND_LAST 0x000D + +/* + * Flags used by ext2fs_extent_insert() + */ +#define EXT2_EXTENT_INSERT_AFTER 0x0001 /* insert after handle loc'n */ +#define EXT2_EXTENT_INSERT_NOSPLIT 0x0002 /* insert may not cause split */ + +/* + * Flags used by ext2fs_extent_delete() + */ +#define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extnt gone */ + +/* + * Flags used by ext2fs_extent_set_bmap() + */ +#define EXT2_EXTENT_SET_BMAP_UNINIT 0x0001 + +/* + * Data structure returned by ext2fs_extent_get_info() + */ +struct ext2_extent_info { + int curr_entry; + int curr_level; + int num_entries; + int max_entries; + int max_depth; + int bytes_avail; + blk64_t max_lblk; + blk64_t max_pblk; + __u32 max_len; + __u32 max_uninit_len; +}; + +/* + * Flags for directory block reading and writing functions + */ +#define EXT2_DIRBLOCK_V2_STRUCT 0x0001 + +/* + * Return flags for the directory iterator functions + */ +#define DIRENT_CHANGED 1 +#define DIRENT_ABORT 2 +#define DIRENT_ERROR 3 + +/* + * Directory iterator flags + */ + +#define DIRENT_FLAG_INCLUDE_EMPTY 1 +#define DIRENT_FLAG_INCLUDE_REMOVED 2 + +#define DIRENT_DOT_FILE 1 +#define DIRENT_DOT_DOT_FILE 2 +#define DIRENT_OTHER_FILE 3 +#define DIRENT_DELETED_FILE 4 + +/* + * Inode scan definitions + */ +typedef struct ext2_struct_inode_scan *ext2_inode_scan; + +/* + * ext2fs_scan flags + */ +#define EXT2_SF_CHK_BADBLOCKS 0x0001 +#define EXT2_SF_BAD_INODE_BLK 0x0002 +#define EXT2_SF_BAD_EXTRA_BYTES 0x0004 +#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 +#define EXT2_SF_DO_LAZY 0x0010 + +/* + * ext2fs_check_if_mounted flags + */ +#define EXT2_MF_MOUNTED 1 +#define EXT2_MF_ISROOT 2 +#define EXT2_MF_READONLY 4 +#define EXT2_MF_SWAP 8 +#define EXT2_MF_BUSY 16 + +/* + * Ext2/linux mode flags. We define them here so that we don't need + * to depend on the OS's sys/stat.h, since we may be compiling on a + * non-Linux system. + */ +#define LINUX_S_IFMT 00170000 +#define LINUX_S_IFSOCK 0140000 +#define LINUX_S_IFLNK 0120000 +#define LINUX_S_IFREG 0100000 +#define LINUX_S_IFBLK 0060000 +#define LINUX_S_IFDIR 0040000 +#define LINUX_S_IFCHR 0020000 +#define LINUX_S_IFIFO 0010000 +#define LINUX_S_ISUID 0004000 +#define LINUX_S_ISGID 0002000 +#define LINUX_S_ISVTX 0001000 + +#define LINUX_S_IRWXU 00700 +#define LINUX_S_IRUSR 00400 +#define LINUX_S_IWUSR 00200 +#define LINUX_S_IXUSR 00100 + +#define LINUX_S_IRWXG 00070 +#define LINUX_S_IRGRP 00040 +#define LINUX_S_IWGRP 00020 +#define LINUX_S_IXGRP 00010 + +#define LINUX_S_IRWXO 00007 +#define LINUX_S_IROTH 00004 +#define LINUX_S_IWOTH 00002 +#define LINUX_S_IXOTH 00001 + +#define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) +#define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) +#define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) +#define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) +#define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) +#define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) +#define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) + +/* + * ext2 size of an inode + */ +#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) + +/* + * ext2_icount_t abstraction + */ +#define EXT2_ICOUNT_OPT_INCREMENT 0x01 + +typedef struct ext2_icount *ext2_icount_t; + +/* + * Flags for ext2fs_bmap + */ +#define BMAP_ALLOC 0x0001 +#define BMAP_SET 0x0002 + +/* + * Returned flags from ext2fs_bmap + */ +#define BMAP_RET_UNINIT 0x0001 + +/* + * Flags for imager.c functions + */ +#define IMAGER_FLAG_INODEMAP 1 +#define IMAGER_FLAG_SPARSEWRITE 2 + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + + +/* + * For ext2 compression support + */ +#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) -1) +#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) + +/* + * Features supported by this version of the library + */ +#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ + EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ + EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_RESIZE_INODE|\ + EXT2_FEATURE_COMPAT_DIR_INDEX|\ + EXT2_FEATURE_COMPAT_EXT_ATTR) + +/* This #ifdef is temporary until compression is fully supported */ +#ifdef ENABLE_COMPRESSION +#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL +/* If the below warning bugs you, then have + `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your + environment at configure time. */ + #warning "Compression support is experimental" +#endif +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT2_FEATURE_INCOMPAT_COMPRESSION|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER|\ + EXT3_FEATURE_INCOMPAT_EXTENTS|\ + EXT4_FEATURE_INCOMPAT_FLEX_BG|\ + EXT4_FEATURE_INCOMPAT_64BIT) +#else +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER|\ + EXT3_FEATURE_INCOMPAT_EXTENTS|\ + EXT4_FEATURE_INCOMPAT_FLEX_BG|\ + EXT4_FEATURE_INCOMPAT_64BIT) +#endif +#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ + EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) + +/* + * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed + * to ext2fs_openfs() + */ +#define EXT2_LIB_SOFTSUPP_INCOMPAT (0) +#define EXT2_LIB_SOFTSUPP_RO_COMPAT (EXT4_FEATURE_RO_COMPAT_BIGALLOC) + +/* + * function prototypes + */ + +/* alloc.c */ +extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, + ext2fs_inode_bitmap map, ext2_ino_t *ret); +extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret); +extern errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret); +extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, + blk_t finish, int num, + ext2fs_block_bitmap map, + blk_t *ret); +extern errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, + blk64_t finish, int num, + ext2fs_block_bitmap map, + blk64_t *ret); +extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret); +extern errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, + char *block_buf, blk64_t *ret); +extern void ext2fs_set_alloc_block_callback(ext2_filsys fs, + errcode_t (*func)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret), + errcode_t (**old)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret)); + +/* alloc_sb.c */ +extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap); +extern void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, + void (*func)(ext2_filsys fs, + blk64_t blk, + int inuse), + void (**old)(ext2_filsys fs, + blk64_t blk, + int inuse)); + +/* alloc_stats.c */ +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir); +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); +void ext2fs_block_alloc_stats2(ext2_filsys fs, blk64_t blk, int inuse); + +/* alloc_tables.c */ +extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); +extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap); + +/* badblocks.c */ +extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); +extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); +extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret); +extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); +extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); +extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); +extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); + +extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, + int size); +extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); +extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); +extern errcode_t + ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret); +extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, + blk_t *blk); +extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); +extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest); +extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, + ext2_badblocks_list bb2); +extern int ext2fs_u32_list_count(ext2_u32_list bb); + +/* bb_compat */ +extern errcode_t badblocks_list_create(badblocks_list *ret, int size); +extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); +extern int badblocks_list_test(badblocks_list bb, blk_t blk); +extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret); +extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); +extern void badblocks_list_iterate_end(badblocks_iterate iter); +extern void badblocks_list_free(badblocks_list bb); + +/* bb_inode.c */ +extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, + ext2_badblocks_list bb_list); + +/* bitmaps.c */ +extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); +extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret); +extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret); +extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend); +extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend); +extern errcode_t ext2fs_fudge_block_bitmap_end2(ext2fs_block_bitmap bitmap, + blk64_t end, blk64_t *oend); +extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); +extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); +extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); +extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap); +extern errcode_t ext2fs_resize_inode_bitmap2(__u64 new_end, + __u64 new_real_end, + ext2fs_inode_bitmap bmap); +extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap); +extern errcode_t ext2fs_resize_block_bitmap2(__u64 new_end, + __u64 new_real_end, + ext2fs_block_bitmap bmap); +extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2); +extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2); +extern errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *in); +extern errcode_t ext2fs_set_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *in); +extern errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *out); +extern errcode_t ext2fs_get_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *out); +extern errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *in); +extern errcode_t ext2fs_set_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *in); +extern errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *out); +extern errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *out); + +/* blknum.c */ +extern dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t); +extern blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group); +extern blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group); +extern blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs, + struct ext2_inode *inode); +extern blk64_t ext2fs_inode_i_blocks(ext2_filsys fs, + struct ext2_inode *inode); +extern blk64_t ext2fs_blocks_count(struct ext2_super_block *super); +extern void ext2fs_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +extern blk64_t ext2fs_r_blocks_count(struct ext2_super_block *super); +extern void ext2fs_r_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_r_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +extern blk64_t ext2fs_free_blocks_count(struct ext2_super_block *super); +extern void ext2fs_free_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_free_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +/* Block group descriptor accessor functions */ +extern struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group); +extern blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern blk64_t ext2fs_inode_table_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_inode_table_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern __u32 ext2fs_bg_free_blocks_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_free_blocks_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_free_inodes_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_free_inodes_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_used_dirs_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_used_dirs_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_itable_unused(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_itable_unused_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u16 ext2fs_bg_flags(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_flags_zap(ext2_filsys fs, dgrp_t group); +extern int ext2fs_bg_flags_test(ext2_filsys fs, dgrp_t group, __u16 bg_flag); +extern void ext2fs_bg_flags_set(ext2_filsys fs, dgrp_t group, __u16 bg_flags); +extern void ext2fs_bg_flags_clear(ext2_filsys fs, dgrp_t group, __u16 bg_flags); +extern __u16 ext2fs_bg_checksum(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_checksum_set(ext2_filsys fs, dgrp_t group, __u16 checksum); +extern blk64_t ext2fs_file_acl_block(const struct ext2_inode *inode); +extern void ext2fs_file_acl_block_set(struct ext2_inode *inode, blk64_t blk); + +/* block.c */ +extern errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data); +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); +errcode_t ext2fs_block_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); + +/* bmap.c */ +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); +extern errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk64_t block, + int *ret_flags, blk64_t *phys_blk); + +#if 0 +/* bmove.c */ +extern errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags); +#endif + +/* check_desc.c */ +extern errcode_t ext2fs_check_desc(ext2_filsys fs); + +/* closefs.c */ +extern errcode_t ext2fs_close(ext2_filsys fs); +extern errcode_t ext2fs_flush(ext2_filsys fs); +extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); +extern errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, + dgrp_t group, + blk64_t *ret_super_blk, + blk64_t *ret_old_desc_blk, + blk64_t *ret_new_desc_blk, + blk_t *ret_used_blks); +extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg); +extern void ext2fs_update_dynamic_rev(ext2_filsys fs); + +/* csum.c */ +extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group); +extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group); +extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs); + +/* dblist.c */ + +extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); +extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); +extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt); +extern void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)); +extern void ext2fs_dblist_sort2(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)); +extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_dblist_iterate2(ext2_dblist dblist, + int (*func)(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern errcode_t ext2fs_set_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt); +extern errcode_t ext2fs_copy_dblist(ext2_dblist src, + ext2_dblist *dest); +extern int ext2fs_dblist_count(ext2_dblist dblist); +extern blk64_t ext2fs_dblist_count2(ext2_dblist dblist); +extern errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, + struct ext2_db_entry **entry); +extern errcode_t ext2fs_dblist_get_last2(ext2_dblist dblist, + struct ext2_db_entry2 **entry); +extern errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist); + +/* dblist_dir.c */ +extern errcode_t + ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dirblock.c */ +extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); +extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags); +extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); +extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags); + +/* dirhash.c */ +extern errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash); + + +/* dir_iterate.c */ +extern errcode_t ext2fs_get_rec_len(ext2_filsys fs, + struct ext2_dir_entry *dirent, + unsigned int *rec_len); +extern errcode_t ext2fs_set_rec_len(ext2_filsys fs, + unsigned int len, + struct ext2_dir_entry *dirent); +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dupfs.c */ +extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); + +/* expanddir.c */ +extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); + +/* ext_attr.c */ +extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, + void *data); +extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); +extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, + void *buf); +extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, + void *buf); +extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, + int adjust, __u32 *newcount); +extern errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, + int adjust, __u32 *newcount); + +/* extent.c */ +extern errcode_t ext2fs_extent_header_verify(void *ptr, int size); +extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, + ext2_extent_handle_t *handle); +extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + ext2_extent_handle_t *ret_handle); +extern void ext2fs_extent_free(ext2_extent_handle_t handle); +extern errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, + int flags, struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, + blk64_t logical, blk64_t physical, + int flags); +extern errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags); +extern errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, + struct ext2_extent_info *info); +extern errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, + blk64_t blk); + +/* fileio.c */ +extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret); +extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret); +extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); +struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file); +extern errcode_t ext2fs_file_close(ext2_file_t file); +extern errcode_t ext2fs_file_flush(ext2_file_t file); +extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got); +extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written); +extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos); +extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos); +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); +extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); +extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); +extern errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size); + +/* finddev.c */ +extern char *ext2fs_find_block_device(dev_t device); + +/* flushb.c */ +extern errcode_t ext2fs_sync_device(int fd, int flushb); + +/* freefs.c */ +extern void ext2fs_free(ext2_filsys fs); +extern void ext2fs_free_dblist(ext2_dblist dblist); +extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); +extern void ext2fs_u32_list_free(ext2_u32_list bb); + +/* gen_bitmap.c */ +extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); +extern errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, + __u32 start, __u32 end, + __u32 real_end, + const char *descr, char *init_map, + ext2fs_generic_bitmap *ret); +extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret); +extern errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +extern void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap); +extern errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, + errcode_t magic, + errcode_t neq, + ext2_ino_t end, + ext2_ino_t *oend); +extern void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map); +extern errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, + __u32 new_end, + __u32 new_real_end, + ext2fs_generic_bitmap bmap); +extern errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +extern errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *out); +extern errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *in); + +/* gen_bitmap64.c */ +void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap); +errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char *descr, + ext2fs_generic_bitmap *ret); +errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap); +errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend); +void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap); +errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end); +errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *out); +errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *in); + +/* getsize.c */ +extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks); +extern errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks); + +/* getsectsize.c */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); +errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize); + +/* i_block.c */ +errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks); +errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks); +errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b); + +/* imager.c */ +extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); + +/* ind_block.c */ +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); + +/* initialize.c */ +extern errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs); + +/* icount.c */ +extern void ext2fs_free_icount(ext2_icount_t icount); +extern errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, + int flags, ext2_icount_t *ret); +extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret); +extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret); +extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count); +extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); + +/* inode.c */ +extern errcode_t ext2fs_flush_icache(ext2_filsys fs); +extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, + ext2_ino_t *ino, + struct ext2_inode *inode, + int bufsize); +extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan); +extern void ext2fs_close_inode_scan(ext2_inode_scan scan); +extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode); +extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group); +extern void ext2fs_set_inode_callback + (ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data), + void *done_group_data); +extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags); +extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); +extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); + +/* inode_io.c */ +extern io_manager inode_io_manager; +extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name); +extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name); + +/* ismounted.c */ +extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); +extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen); + +/* punch.c */ +extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, blk64_t start, + blk64_t end); + +/* namei.c */ +extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode); +extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode); + +/* native.c */ +int ext2fs_native_flag(void); + +/* newdir.c */ +extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block); + +/* mkdir.c */ +extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name); + +/* mkjournal.c */ +extern errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, + blk_t *ret_blk, int *ret_count); +extern errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, + blk64_t *ret_blk, int *ret_count); +extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb); +extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, + ext2_filsys journal_dev); +extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, + int flags); +extern int ext2fs_default_journal_size(__u64 blocks); + +/* openfs.c */ +extern errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_channel * chan, + ext2_filsys *ret_fs); +extern errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_channel * chan, + ext2_filsys *ret_fs); +extern blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, + blk64_t group_block, dgrp_t i); +extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, + dgrp_t i); +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); + +/* get_pathname.c */ +extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name); + +/* link.c */ +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); + +/* read_bb.c */ +extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, + ext2_badblocks_list *bb_list); + +/* read_bb_file.c */ +extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)); +extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, + blk_t blk)); + +/* res_gdt.c */ +extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); + +/* swapfs.c */ +extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, + int has_header); +extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, + struct ext2_ext_attr_header *from_hdr); +extern void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, + struct ext2_ext_attr_entry *from_entry); +extern void ext2fs_swap_super(struct ext2_super_block * super); +extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); +extern void ext2fs_swap_group_desc2(ext2_filsys, struct ext2_group_desc *gdp); +extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize); +extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, + struct ext2_inode *f, int hostorder); + +/* valid_blk.c */ +extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); + +/* version.c */ +extern int ext2fs_parse_version_string(const char *ver_string); +extern int ext2fs_get_library_version(const char **ver_string, + const char **date_string); + +/* write_bb_file.c */ +extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags, + FILE *f); + + +/* inline functions */ +extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); +extern errcode_t ext2fs_get_memalign(unsigned long size, + unsigned long align, void *ptr); +extern errcode_t ext2fs_free_mem(void *ptr); +extern errcode_t ext2fs_resize_mem(unsigned long old_size, + unsigned long size, void *ptr); +extern void ext2fs_mark_super_dirty(ext2_filsys fs); +extern void ext2fs_mark_changed(ext2_filsys fs); +extern int ext2fs_test_changed(ext2_filsys fs); +extern void ext2fs_mark_valid(ext2_filsys fs); +extern void ext2fs_unmark_valid(ext2_filsys fs); +extern int ext2fs_test_valid(ext2_filsys fs); +extern void ext2fs_mark_ib_dirty(ext2_filsys fs); +extern void ext2fs_mark_bb_dirty(ext2_filsys fs); +extern int ext2fs_test_ib_dirty(ext2_filsys fs); +extern int ext2fs_test_bb_dirty(ext2_filsys fs); +extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); +extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); +extern blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group); +extern blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group); +extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode); +extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b); +extern __u64 ext2fs_div64_ceil(__u64 a, __u64 b); + +/* + * The actual inlined functions definitions themselves... + * + * If NO_INLINE_FUNCS is defined, then we won't try to do inline + * functions at all! + */ +#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef INCLUDE_INLINE_FUNCS +#define _INLINE_ extern +#else +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif +#endif + +#ifndef EXT2_CUSTOM_MEMORY_ROUTINES +#include +#include "mem_allocate.h" +/* + * Allocate memory + */ +_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr) +{ + void *pp; + + pp = mem_alloc(size); + if (!pp) + return EXT2_ET_NO_MEMORY; + memcpy(ptr, &pp, sizeof (pp)); + return 0; +} + +_INLINE_ errcode_t ext2fs_get_memalign(unsigned long size, + unsigned long align, void *ptr) +{ + void *pp; + + #ifdef HWRVL + pp = mem_align(32, size); + #else + pp = mem_alloc(size); + #endif + if (!pp) + return EXT2_ET_NO_MEMORY; + + memcpy(ptr, &pp, sizeof (pp)); + return 0; +} + +_INLINE_ errcode_t ext2fs_get_array(unsigned long count, unsigned long size, void *ptr) +{ + if (count && (-1UL)/countflags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark a filesystem as changed + */ +_INLINE_ void ext2fs_mark_changed(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem has changed + */ +_INLINE_ int ext2fs_test_changed(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_CHANGED); +} + +/* + * Mark a filesystem as valid + */ +_INLINE_ void ext2fs_mark_valid(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_VALID; +} + +/* + * Mark a filesystem as NOT valid + */ +_INLINE_ void ext2fs_unmark_valid(ext2_filsys fs) +{ + fs->flags &= ~EXT2_FLAG_VALID; +} + +/* + * Check to see if a filesystem is valid + */ +_INLINE_ int ext2fs_test_valid(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_VALID); +} + +/* + * Mark the inode bitmap as dirty + */ +_INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark the block bitmap as dirty + */ +_INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem's inode bitmap is dirty + */ +_INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_IB_DIRTY); +} + +/* + * Check to see if a filesystem's block bitmap is dirty + */ +_INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_BB_DIRTY); +} + +/* + * Return the group # of a block + */ +_INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) +{ + return ext2fs_group_of_blk2(fs, blk); +} +/* + * Return the group # of an inode number + */ +_INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) +{ + return (ino - 1) / fs->super->s_inodes_per_group; +} + +/* + * Return the first block (inclusive) in a group + */ +_INLINE_ blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group) +{ + return ext2fs_group_first_block2(fs, group); +} + +/* + * Return the last block (inclusive) in a group + */ +_INLINE_ blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group) +{ + return ext2fs_group_last_block2(fs, group); +} + +_INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode) +{ + return ext2fs_inode_data_blocks2(fs, inode); +} + +/* + * This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b) + */ +_INLINE_ unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b) +{ + if (!a) + return 0; + return ((a - 1) / b) + 1; +} + +_INLINE_ __u64 ext2fs_div64_ceil(__u64 a, __u64 b) +{ + if (!a) + return 0; + return ((a - 1) / b) + 1; +} + +#undef _INLINE_ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _EXT2FS_EXT2FS_H */ diff --git a/lib/libext2fs/source/ext2fsP.h b/lib/libext2fs/source/ext2fsP.h new file mode 100644 index 0000000..ab9ee76 --- /dev/null +++ b/lib/libext2fs/source/ext2fsP.h @@ -0,0 +1,143 @@ +/* + * ext2fsP.h --- private header file for ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "ext2fs.h" + +/* + * Badblocks list + */ +struct ext2_struct_u32_list { + int magic; + int num; + int size; + __u32 *list; + int badblocks_flags; +}; + +struct ext2_struct_u32_iterate { + int magic; + ext2_u32_list bb; + int ptr; +}; + + +/* + * Directory block iterator definition + */ +struct ext2_struct_dblist { + int magic; + ext2_filsys fs; + unsigned long long size; + unsigned long long count; + int sorted; + struct ext2_db_entry2 * list; +}; + +/* + * For directory iterators + */ +struct dir_context { + ext2_ino_t dir; + int flags; + char *buf; + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *priv_data; + errcode_t errcode; +}; + +/* + * Inode cache structure + */ +struct ext2_inode_cache { + void * buffer; + blk_t buffer_blk; + int cache_last; + int cache_size; + int refcount; + struct ext2_inode_cache_ent *cache; +}; + +struct ext2_inode_cache_ent { + ext2_ino_t ino; + struct ext2_inode inode; +}; + +/* Function prototypes */ + +extern int ext2fs_process_dir_block(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block, + int ref_offset, + void *priv_data); + +/* Generic numeric progress meter */ + +struct ext2fs_numeric_progress_struct { + __u64 max; + int log_max; + int skip_progress; +}; + +extern void ext2fs_numeric_progress_init(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *label, __u64 max); +extern void ext2fs_numeric_progress_update(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + __u64 val); +extern void ext2fs_numeric_progress_close(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *message); + +/* + * 64-bit bitmap support + */ + +#define EXT2FS_BMAP64_BITARRAY 1 + +extern errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char * description, + ext2fs_generic_bitmap *bmap); + +extern void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap); + +extern errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); + +extern errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end); +extern errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend); +extern int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int num, + void *in); +extern errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int num, + void *out); +extern int ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func); + +extern int ext2fs_mem_is_zero(const char *mem, size_t len); diff --git a/lib/libext2fs/source/ext3_extents.h b/lib/libext2fs/source/ext3_extents.h new file mode 100644 index 0000000..88fabc9 --- /dev/null +++ b/lib/libext2fs/source/ext3_extents.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003,2004 Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _LINUX_EXT3_EXTENTS +#define _LINUX_EXT3_EXTENTS + +/* + * ext3_inode has i_block array (total 60 bytes) + * first 4 bytes are used to store: + * - tree depth (0 mean there is no tree yet. all extents in the inode) + * - number of alive extents in the inode + */ + +/* + * this is extent on-disk structure + * it's used at the bottom of the tree + */ +struct ext3_extent { + __u32 ee_block; /* first logical block extent covers */ + __u16 ee_len; /* number of blocks covered by extent */ + __u16 ee_start_hi; /* high 16 bits of physical block */ + __u32 ee_start; /* low 32 bigs of physical block */ +}; + +/* + * this is index on-disk structure + * it's used at all the levels, but the bottom + */ +struct ext3_extent_idx { + __u32 ei_block; /* index covers logical blocks from 'block' */ + __u32 ei_leaf; /* pointer to the physical block of the next * + * level. leaf or next index could bet here */ + __u16 ei_leaf_hi; /* high 16 bits of physical block */ + __u16 ei_unused; +}; + +/* + * each block (leaves and indexes), even inode-stored has header + */ +struct ext3_extent_header { + __u16 eh_magic; /* probably will support different formats */ + __u16 eh_entries; /* number of valid entries */ + __u16 eh_max; /* capacity of store in entries */ + __u16 eh_depth; /* has tree real underlaying blocks? */ + __u32 eh_generation; /* generation of the tree */ +}; + +#define EXT3_EXT_MAGIC 0xf30a + +/* + * array of ext3_ext_path contains path to some extent + * creation/lookup routines use it for traversal/splitting/etc + * truncate uses it to simulate recursive walking + */ +struct ext3_ext_path { + __u32 p_block; + __u16 p_depth; + struct ext3_extent *p_ext; + struct ext3_extent_idx *p_idx; + struct ext3_extent_header *p_hdr; + struct buffer_head *p_bh; +}; + +/* + * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an + * initialized extent. This is 2^15 and not (2^16 - 1), since we use the + * MSB of ee_len field in the extent datastructure to signify if this + * particular extent is an initialized extent or an uninitialized (i.e. + * preallocated). + * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an + * uninitialized extent. + * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an + * uninitialized one. In other words, if MSB of ee_len is set, it is an + * uninitialized extent with only one special scenario when ee_len = 0x8000. + * In this case we can not have an uninitialized extent of zero length and + * thus we make it as a special case of initialized extent with 0x8000 length. + * This way we get better extent-to-group alignment for initialized extents. + * Hence, the maximum number of blocks we can have in an *initialized* + * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767). + */ +#define EXT_INIT_MAX_LEN (1UL << 15) +#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1) + +#define EXT_FIRST_EXTENT(__hdr__) \ + ((struct ext3_extent *) (((char *) (__hdr__)) + \ + sizeof(struct ext3_extent_header))) +#define EXT_FIRST_INDEX(__hdr__) \ + ((struct ext3_extent_idx *) (((char *) (__hdr__)) + \ + sizeof(struct ext3_extent_header))) +#define EXT_HAS_FREE_INDEX(__path__) \ + ((__path__)->p_hdr->eh_entries < (__path__)->p_hdr->eh_max) +#define EXT_LAST_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_entries - 1) +#define EXT_LAST_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_entries - 1) +#define EXT_MAX_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_max - 1) +#define EXT_MAX_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_max - 1) + +#endif /* _LINUX_EXT3_EXTENTS */ + diff --git a/lib/libext2fs/source/ext_attr.c b/lib/libext2fs/source/ext_attr.c new file mode 100644 index 0000000..52664eb --- /dev/null +++ b/lib/libext2fs/source/ext_attr.c @@ -0,0 +1,155 @@ +/* + * ext_attr.c --- extended attribute blocks + * + * Copyright (C) 2001 Andreas Gruenbacher, + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * ext2_xattr_hash_entry() + * + * Compute the hash of an extended attribute. + */ +__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) +{ + __u32 hash = 0; + char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry); + int n; + + for (n = 0; n < entry->e_name_len; n++) { + hash = (hash << NAME_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ + *name++; + } + + /* The hash needs to be calculated on the data in little-endian. */ + if (entry->e_value_block == 0 && entry->e_value_size != 0) { + __u32 *value = (__u32 *)data; + for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >> + EXT2_EXT_ATTR_PAD_BITS; n; n--) { + hash = (hash << VALUE_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ + ext2fs_le32_to_cpu(*value++); + } + } + + return hash; +} + +#undef NAME_HASH_SHIFT +#undef VALUE_HASH_SHIFT + +errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +{ + errcode_t retval; + + retval = io_channel_read_blk64(fs->io, block, 1, buf); + if (retval) + return retval; +#ifdef WORDS_BIGENDIAN + ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); +#endif + return 0; +} + +errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) +{ + return ext2fs_read_ext_attr2(fs, block, buf); +} + +errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +{ + errcode_t retval; + char *write_buf; + char *buf = NULL; + +#ifdef WORDS_BIGENDIAN + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + write_buf = buf; + ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); +#else + write_buf = (char *) inbuf; +#endif + retval = io_channel_write_blk64(fs->io, block, 1, write_buf); + if (buf) + ext2fs_free_mem(&buf); + if (!retval) + ext2fs_mark_changed(fs); + return retval; +} + +errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) +{ + return ext2fs_write_ext_attr2(fs, block, inbuf); +} + +/* + * This function adjusts the reference count of the EA block. + */ +errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + errcode_t retval; + struct ext2_ext_attr_header *header; + char *buf = 0; + + if ((blk >= ext2fs_blocks_count(fs->super)) || + (blk < fs->super->s_first_data_block)) + return EXT2_ET_BAD_EA_BLOCK_NUM; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + retval = ext2fs_read_ext_attr2(fs, blk, block_buf); + if (retval) + goto errout; + + header = (struct ext2_ext_attr_header *) block_buf; + header->h_refcount += adjust; + if (newcount) + *newcount = header->h_refcount; + + retval = ext2fs_write_ext_attr2(fs, blk, block_buf); + if (retval) + goto errout; + +errout: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount); +} diff --git a/lib/libext2fs/source/extent.c b/lib/libext2fs/source/extent.c new file mode 100644 index 0000000..5e07092 --- /dev/null +++ b/lib/libext2fs/source/extent.c @@ -0,0 +1,2007 @@ +/* + * extent.c --- routines to implement extents support + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +/* + * Definitions to be dropped in lib/ext2fs/ext2fs.h + */ + +/* + * Private definitions + */ + +struct extent_path { + char *buf; + int entries; + int max_entries; + int left; + int visit_num; + int flags; + blk64_t end_blk; + void *curr; +}; + + +struct ext2_extent_handle { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode *inode; + struct ext2_inode inodebuf; + int type; + int level; + int max_depth; + struct extent_path *path; +}; + +struct ext2_extent_path { + errcode_t magic; + int leaf_height; + blk64_t lblk; +}; + +/* + * Useful Debugging stuff + */ + +#ifdef DEBUG +static void dbg_show_header(struct ext3_extent_header *eh) +{ + printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n", + ext2fs_le16_to_cpu(eh->eh_magic), + ext2fs_le16_to_cpu(eh->eh_entries), + ext2fs_le16_to_cpu(eh->eh_max), + ext2fs_le16_to_cpu(eh->eh_depth), + ext2fs_le32_to_cpu(eh->eh_generation)); +} + +static void dbg_show_index(struct ext3_extent_idx *ix) +{ + printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n", + ext2fs_le32_to_cpu(ix->ei_block), + ext2fs_le32_to_cpu(ix->ei_leaf), + ext2fs_le16_to_cpu(ix->ei_leaf_hi), + ext2fs_le16_to_cpu(ix->ei_unused)); +} + +static void dbg_show_extent(struct ext3_extent *ex) +{ + printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n", + ext2fs_le32_to_cpu(ex->ee_block), + ext2fs_le32_to_cpu(ex->ee_block) + + ext2fs_le16_to_cpu(ex->ee_len) - 1, + ext2fs_le16_to_cpu(ex->ee_len), + ext2fs_le32_to_cpu(ex->ee_start), + ext2fs_le16_to_cpu(ex->ee_start_hi)); +} + +static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) +{ + if (desc) + printf("%s: ", desc); + printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", + extent->e_lblk, extent->e_lblk + extent->e_len - 1, + extent->e_len, extent->e_pblk); + if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) + fputs("LEAF ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fputs("UNINIT ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + fputs("2ND_VISIT ", stdout); + if (!extent->e_flags) + fputs("(none)", stdout); + fputc('\n', stdout); + +} + +#else +#define dbg_show_header(eh) do { } while (0) +#define dbg_show_index(ix) do { } while (0) +#define dbg_show_extent(ex) do { } while (0) +#define dbg_print_extent(desc, ex) do { } while (0) +#endif + +/* + * Verify the extent header as being sane + */ +errcode_t ext2fs_extent_header_verify(void *ptr, int size) +{ + int eh_max, entry_size; + struct ext3_extent_header *eh = ptr; + + dbg_show_header(eh); + if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) + return EXT2_ET_EXTENT_HEADER_BAD; + if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max)) + return EXT2_ET_EXTENT_HEADER_BAD; + if (eh->eh_depth == 0) + entry_size = sizeof(struct ext3_extent); + else + entry_size = sizeof(struct ext3_extent_idx); + + eh_max = (size - sizeof(*eh)) / entry_size; + /* Allow two extent-sized items at the end of the block, for + * ext4_extent_tail with checksum in the future. */ + if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) || + (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2))) + return EXT2_ET_EXTENT_HEADER_BAD; + + return 0; +} + + +/* + * Begin functions to handle an inode's extent information + */ +extern void ext2fs_extent_free(ext2_extent_handle_t handle) +{ + int i; + + if (!handle) + return; + + if (handle->path) { + for (i=1; i <= handle->max_depth; i++) { + if (handle->path[i].buf) + ext2fs_free_mem(&handle->path[i].buf); + } + ext2fs_free_mem(&handle->path); + } + ext2fs_free_mem(&handle); +} + +extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, + ext2_extent_handle_t *ret_handle) +{ + return ext2fs_extent_open2(fs, ino, NULL, ret_handle); +} + +extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + ext2_extent_handle_t *ret_handle) +{ + struct ext2_extent_handle *handle; + errcode_t retval; + int i; + struct ext3_extent_header *eh; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!inode) + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + + retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); + if (retval) + return retval; + memset(handle, 0, sizeof(struct ext2_extent_handle)); + + handle->ino = ino; + handle->fs = fs; + + if (inode) { + handle->inode = inode; + } else { + handle->inode = &handle->inodebuf; + retval = ext2fs_read_inode(fs, ino, handle->inode); + if (retval) + goto errout; + } + + eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; + + for (i=0; i < EXT2_N_BLOCKS; i++) + if (handle->inode->i_block[i]) + break; + if (i >= EXT2_N_BLOCKS) { + eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); + eh->eh_depth = 0; + eh->eh_entries = 0; + i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / + sizeof(struct ext3_extent); + eh->eh_max = ext2fs_cpu_to_le16(i); + handle->inode->i_flags |= EXT4_EXTENTS_FL; + } + + if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) { + retval = EXT2_ET_INODE_NOT_EXTENT; + goto errout; + } + + retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block)); + if (retval) + goto errout; + + handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); + handle->type = ext2fs_le16_to_cpu(eh->eh_magic); + + retval = ext2fs_get_mem(((handle->max_depth+1) * + sizeof(struct extent_path)), + &handle->path); + memset(handle->path, 0, + (handle->max_depth+1) * sizeof(struct extent_path)); + handle->path[0].buf = (char *) handle->inode->i_block; + + handle->path[0].left = handle->path[0].entries = + ext2fs_le16_to_cpu(eh->eh_entries); + handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); + handle->path[0].curr = 0; + handle->path[0].end_blk = + ((((__u64) handle->inode->i_size_high << 32) + + handle->inode->i_size + (fs->blocksize - 1)) + >> EXT2_BLOCK_SIZE_BITS(fs->super)); + handle->path[0].visit_num = 1; + handle->level = 0; + handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; + + *ret_handle = handle; + return 0; + +errout: + ext2fs_extent_free(handle); + return retval; +} + +/* + * This function is responsible for (optionally) moving through the + * extent tree and then returning the current extent + */ +errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, + int flags, struct ext2fs_extent *extent) +{ + struct extent_path *path, *newpath; + struct ext3_extent_header *eh; + struct ext3_extent_idx *ix = 0; + struct ext3_extent *ex; + errcode_t retval; + blk64_t blk; + blk64_t end_blk; + int orig_op, op; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + orig_op = op = flags & EXT2_EXTENT_MOVE_MASK; + +retry: + path = handle->path + handle->level; + if ((orig_op == EXT2_EXTENT_NEXT) || + (orig_op == EXT2_EXTENT_NEXT_LEAF)) { + if (handle->level < handle->max_depth) { + /* interior node */ + if (path->visit_num == 0) { + path->visit_num++; + op = EXT2_EXTENT_DOWN; + } else if (path->left > 0) + op = EXT2_EXTENT_NEXT_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_NEXT; + } else { + /* leaf node */ + if (path->left > 0) + op = EXT2_EXTENT_NEXT_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_NEXT; + } + if (op != EXT2_EXTENT_NEXT_SIB) { +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN) ? "down" : + ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); +#endif + } + } + + if ((orig_op == EXT2_EXTENT_PREV) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) { + if (handle->level < handle->max_depth) { + /* interior node */ + if (path->visit_num > 0 ) { + /* path->visit_num = 0; */ + op = EXT2_EXTENT_DOWN_AND_LAST; + } else if (path->left < path->entries-1) + op = EXT2_EXTENT_PREV_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_PREV; + } else { + /* leaf node */ + if (path->left < path->entries-1) + op = EXT2_EXTENT_PREV_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_PREV; + } + if (op != EXT2_EXTENT_PREV_SIB) { +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" : + ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); +#endif + } + } + + if (orig_op == EXT2_EXTENT_LAST_LEAF) { + if ((handle->level < handle->max_depth) && + (path->left == 0)) + op = EXT2_EXTENT_DOWN; + else + op = EXT2_EXTENT_LAST_SIB; +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib"); +#endif + } + + switch (op) { + case EXT2_EXTENT_CURRENT: + ix = path->curr; + break; + case EXT2_EXTENT_ROOT: + handle->level = 0; + path = handle->path + handle->level; + case EXT2_EXTENT_FIRST_SIB: + path->left = path->entries; + path->curr = 0; + case EXT2_EXTENT_NEXT_SIB: + if (path->left <= 0) + return EXT2_ET_EXTENT_NO_NEXT; + if (path->curr) { + ix = path->curr; + ix++; + } else { + eh = (struct ext3_extent_header *) path->buf; + ix = EXT_FIRST_INDEX(eh); + } + path->left--; + path->curr = ix; + path->visit_num = 0; + break; + case EXT2_EXTENT_PREV_SIB: + if (!path->curr || + path->left+1 >= path->entries) + return EXT2_ET_EXTENT_NO_PREV; + ix = path->curr; + ix--; + path->curr = ix; + path->left++; + if (handle->level < handle->max_depth) + path->visit_num = 1; + break; + case EXT2_EXTENT_LAST_SIB: + eh = (struct ext3_extent_header *) path->buf; + path->curr = EXT_LAST_EXTENT(eh); + ix = path->curr; + path->left = 0; + path->visit_num = 0; + break; + case EXT2_EXTENT_UP: + if (handle->level <= 0) + return EXT2_ET_EXTENT_NO_UP; + handle->level--; + path--; + ix = path->curr; + if ((orig_op == EXT2_EXTENT_PREV) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) + path->visit_num = 0; + break; + case EXT2_EXTENT_DOWN: + case EXT2_EXTENT_DOWN_AND_LAST: + if (!path->curr ||(handle->level >= handle->max_depth)) + return EXT2_ET_EXTENT_NO_DOWN; + + ix = path->curr; + newpath = path + 1; + if (!newpath->buf) { + retval = ext2fs_get_mem(handle->fs->blocksize, + &newpath->buf); + if (retval) + return retval; + } + blk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) && + (handle->fs->io != handle->fs->image_io)) + memset(newpath->buf, 0, handle->fs->blocksize); + else { + retval = io_channel_read_blk64(handle->fs->io, + blk, 1, newpath->buf); + if (retval) + return retval; + } + handle->level++; + + eh = (struct ext3_extent_header *) newpath->buf; + + retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize); + if (retval) { + handle->level--; + return retval; + } + + newpath->left = newpath->entries = + ext2fs_le16_to_cpu(eh->eh_entries); + newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); + + if (path->left > 0) { + ix++; + newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block); + } else + newpath->end_blk = path->end_blk; + + path = newpath; + if (op == EXT2_EXTENT_DOWN) { + ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh); + path->curr = ix; + path->left = path->entries - 1; + path->visit_num = 0; + } else { + ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh); + path->curr = ix; + path->left = 0; + if (handle->level < handle->max_depth) + path->visit_num = 1; + } +#ifdef DEBUG_GET_EXTENT + printf("Down to level %d/%d, end_blk=%llu\n", + handle->level, handle->max_depth, + path->end_blk); +#endif + break; + default: + return EXT2_ET_OP_NOT_SUPPORTED; + } + + if (!ix) + return EXT2_ET_NO_CURRENT_NODE; + + extent->e_flags = 0; +#ifdef DEBUG_GET_EXTENT + printf("(Left %d)\n", path->left); +#endif + + if (handle->level == handle->max_depth) { + ex = (struct ext3_extent *) ix; + + extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) + + ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); + extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block); + extent->e_len = ext2fs_le16_to_cpu(ex->ee_len); + extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF; + if (extent->e_len > EXT_INIT_MAX_LEN) { + extent->e_len -= EXT_INIT_MAX_LEN; + extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + } else { + extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block); + if (path->left > 0) { + ix++; + end_blk = ext2fs_le32_to_cpu(ix->ei_block); + } else + end_blk = path->end_blk; + + extent->e_len = end_blk - extent->e_lblk; + } + if (path->visit_num) + extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT; + + if (((orig_op == EXT2_EXTENT_NEXT_LEAF) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) && + (handle->level != handle->max_depth)) + goto retry; + + if ((orig_op == EXT2_EXTENT_LAST_LEAF) && + ((handle->level != handle->max_depth) || + (path->left != 0))) + goto retry; + + return 0; +} + +static errcode_t update_path(ext2_extent_handle_t handle) +{ + blk64_t blk; + errcode_t retval; + struct ext3_extent_idx *ix; + + if (handle->level == 0) { + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + } else { + ix = handle->path[handle->level - 1].curr; + blk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + + retval = io_channel_write_blk64(handle->fs->io, + blk, 1, handle->path[handle->level].buf); + } + return retval; +} + +#if 0 +errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle, + ext2_extent_path_t *ret_path) +{ + ext2_extent_path_t save_path; + struct ext2fs_extent extent; + struct ext2_extent_info info; + errcode_t retval; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + return retval; + + retval = ext2fs_extent_get_info(handle, &info); + if (retval) + return retval; + + retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path); + if (retval) + return retval; + memset(save_path, 0, sizeof(struct ext2_extent_path)); + + save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH; + save_path->leaf_height = info.max_depth - info.curr_level - 1; + save_path->lblk = extent.e_lblk; + + *ret_path = save_path; + return 0; +} + +errcode_t ext2fs_extent_free_path(ext2_extent_path_t path) +{ + EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH); + + ext2fs_free_mem(&path); + return 0; +} +#endif + +/* + * Go to the node at leaf_level which contains logical block blk. + * + * leaf_level is height from the leaf node level, i.e. + * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc. + * + * If "blk" has no mapping (hole) then handle is left at last + * extent before blk. + */ +static errcode_t extent_goto(ext2_extent_handle_t handle, + int leaf_level, blk64_t blk) +{ + struct ext2fs_extent extent; + errcode_t retval; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); + if (retval) { + if (retval == EXT2_ET_EXTENT_NO_NEXT) + retval = EXT2_ET_EXTENT_NOT_FOUND; + return retval; + } + + if (leaf_level > handle->max_depth) { +#ifdef DEBUG + printf("leaf level %d greater than tree depth %d\n", + leaf_level, handle->max_depth); +#endif + return EXT2_ET_OP_NOT_SUPPORTED; + } + +#ifdef DEBUG + printf("goto extent ino %u, level %d, %llu\n", handle->ino, + leaf_level, blk); +#endif + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("root", &extent); +#endif + while (1) { + if (handle->max_depth - handle->level == leaf_level) { + /* block is in this &extent */ + if ((blk >= extent.e_lblk) && + (blk < extent.e_lblk + extent.e_len)) + return 0; + if (blk < extent.e_lblk) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_SIB, + &extent); + return EXT2_ET_EXTENT_NOT_FOUND; + } + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_SIB, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + return EXT2_ET_EXTENT_NOT_FOUND; + if (retval) + return retval; + continue; + } + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + goto go_down; + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("next", &extent); +#endif + if (blk == extent.e_lblk) + goto go_down; + if (blk > extent.e_lblk) + continue; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB, + &extent); + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("prev", &extent); +#endif + + go_down: + retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN, + &extent); + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("down", &extent); +#endif + } +} + +errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, + blk64_t blk) +{ + return extent_goto(handle, 0, blk); +} + +/* + * Traverse back up to root fixing parents of current node as needed. + * + * If we changed start of first entry in a node, fix parent index start + * and so on. + * + * Safe to call for any position in node; if not at the first entry, + * will simply return. + */ +static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle) +{ + int retval = 0; + blk64_t start; + struct extent_path *path; + struct ext2fs_extent extent; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + + /* modified node's start block */ + start = extent.e_lblk; + + /* traverse up until index not first, or startblk matches, or top */ + while (handle->level > 0 && + (path->left == path->entries - 1)) { + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + if (retval) + goto done; + if (extent.e_lblk == start) + break; + path = handle->path + handle->level; + extent.e_len += (extent.e_lblk - start); + extent.e_lblk = start; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + update_path(handle); + } + + /* put handle back to where we started */ + retval = ext2fs_extent_goto(handle, start); +done: + return retval; +} + +errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, + int flags EXT2FS_ATTR((unused)), + struct ext2fs_extent *extent) +{ + struct extent_path *path; + struct ext3_extent_idx *ix; + struct ext3_extent *ex; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("extent replace: %u ", handle->ino); + dbg_print_extent(0, extent); +#endif + + if (handle->level == handle->max_depth) { + ex = path->curr; + + ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk); + ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); + ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) { + if (extent->e_len > EXT_UNINIT_MAX_LEN) + return EXT2_ET_EXTENT_INVALID_LENGTH; + ex->ee_len = ext2fs_cpu_to_le16(extent->e_len + + EXT_INIT_MAX_LEN); + } else { + if (extent->e_len > EXT_INIT_MAX_LEN) + return EXT2_ET_EXTENT_INVALID_LENGTH; + ex->ee_len = ext2fs_cpu_to_le16(extent->e_len); + } + } else { + ix = path->curr; + + ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); + ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); + ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk); + ix->ei_unused = 0; + } + update_path(handle); + return 0; +} + +/* + * allocate a new block, move half the current node to it, and update parent + * + * handle will be left pointing at original record. + */ +static errcode_t extent_node_split(ext2_extent_handle_t handle) +{ + errcode_t retval = 0; + blk64_t new_node_pblk; + blk64_t new_node_start; + blk64_t orig_lblk; + blk64_t goal_blk = 0; + int orig_height; + char *block_buf = NULL; + struct ext2fs_extent extent; + struct extent_path *path, *newpath = 0; + struct ext3_extent_header *eh, *neweh; + int tocopy; + int new_root = 0; + struct ext2_extent_info info; + + /* basic sanity */ + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("splitting node at level %d\n", handle->level); +#endif + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + + retval = ext2fs_extent_get_info(handle, &info); + if (retval) + goto done; + + /* save the position we were originally splitting... */ + orig_height = info.max_depth - info.curr_level; + orig_lblk = extent.e_lblk; + + /* Is there room in the parent for a new entry? */ + if (handle->level && + (handle->path[handle->level - 1].entries >= + handle->path[handle->level - 1].max_entries)) { + +#ifdef DEBUG + printf("parent level %d full; splitting it too\n", + handle->level - 1); +#endif + /* split the parent */ + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + if (retval) + goto done; + goal_blk = extent.e_pblk; + + retval = extent_node_split(handle); + if (retval) + goto done; + + /* get handle back to our original split position */ + retval = extent_goto(handle, orig_height, orig_lblk); + if (retval) + goto done; + } + + /* At this point, parent should have room for this split */ + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + /* extent header of the current node we'll split */ + eh = (struct ext3_extent_header *)path->buf; + + /* splitting root level means moving them all out */ + if (handle->level == 0) { + new_root = 1; + tocopy = ext2fs_le16_to_cpu(eh->eh_entries); + retval = ext2fs_get_mem(((handle->max_depth+2) * + sizeof(struct extent_path)), + &newpath); + if (retval) + goto done; + memset(newpath, 0, + ((handle->max_depth+2) * sizeof(struct extent_path))); + } else { + tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2; + } + +#ifdef DEBUG + printf("will copy out %d of %d entries at level %d\n", + tocopy, ext2fs_le16_to_cpu(eh->eh_entries), + handle->level); +#endif + + if (!tocopy) { +#ifdef DEBUG + printf("Nothing to copy to new block!\n"); +#endif + retval = EXT2_ET_CANT_SPLIT_EXTENT; + goto done; + } + + /* first we need a new block, or can do nothing. */ + block_buf = malloc(handle->fs->blocksize); + if (!block_buf) { + retval = ENOMEM; + goto done; + } + + if (!goal_blk) { + dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino); + __u8 log_flex = handle->fs->super->s_log_groups_per_flex; + + if (log_flex) + group = group & ~((1 << (log_flex)) - 1); + goal_blk = (group * handle->fs->super->s_blocks_per_group) + + handle->fs->super->s_first_data_block; + } + retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf, + &new_node_pblk); + if (retval) + goto done; + +#ifdef DEBUG + printf("will copy to new node at block %lu\n", + (unsigned long) new_node_pblk); +#endif + + /* Copy data into new block buffer */ + /* First the header for the new block... */ + neweh = (struct ext3_extent_header *) block_buf; + memcpy(neweh, eh, sizeof(struct ext3_extent_header)); + neweh->eh_entries = ext2fs_cpu_to_le16(tocopy); + neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize - + sizeof(struct ext3_extent_header)) / + sizeof(struct ext3_extent)); + + /* then the entries for the new block... */ + memcpy(EXT_FIRST_INDEX(neweh), + EXT_FIRST_INDEX(eh) + + (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy), + sizeof(struct ext3_extent_idx) * tocopy); + + new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block); + + /* ...and write the new node block out to disk. */ + retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1, + block_buf); + + if (retval) + goto done; + + /* OK! we've created the new node; now adjust the tree */ + + /* current path now has fewer active entries, we copied some out */ + if (handle->level == 0) { + memcpy(newpath, path, + sizeof(struct extent_path) * (handle->max_depth+1)); + handle->path = newpath; + newpath = path; + path = handle->path; + path->entries = 1; + path->left = path->max_entries - 1; + handle->max_depth++; + eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth); + } else { + path->entries -= tocopy; + path->left -= tocopy; + } + + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + /* this writes out the node, incl. the modified header */ + retval = update_path(handle); + if (retval) + goto done; + + /* now go up and insert/replace index for new node we created */ + if (new_root) { + retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent); + if (retval) + goto done; + + extent.e_lblk = new_node_start; + extent.e_pblk = new_node_pblk; + extent.e_len = handle->path[0].end_blk - extent.e_lblk; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else { + __u32 new_node_length; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + /* will insert after this one; it's length is shorter now */ + new_node_length = new_node_start - extent.e_lblk; + extent.e_len -= new_node_length; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + + /* now set up the new extent and insert it */ + extent.e_lblk = new_node_start; + extent.e_pblk = new_node_pblk; + extent.e_len = new_node_length; + retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent); + if (retval) + goto done; + } + + /* get handle back to our original position */ + retval = extent_goto(handle, orig_height, orig_lblk); + if (retval) + goto done; + + /* new node hooked in, so update inode block count (do this here?) */ + handle->inode->i_blocks += handle->fs->blocksize / 512; + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + if (retval) + goto done; + +done: + if (newpath) + ext2fs_free_mem(&newpath); + free(block_buf); + + return retval; +} + +errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent) +{ + struct extent_path *path; + struct ext3_extent_idx *ix; + struct ext3_extent_header *eh; + errcode_t retval; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("extent insert: %u ", handle->ino); + dbg_print_extent(0, extent); +#endif + + path = handle->path + handle->level; + + if (path->entries >= path->max_entries) { + if (flags & EXT2_EXTENT_INSERT_NOSPLIT) { + return EXT2_ET_CANT_INSERT_EXTENT; + } else { +#ifdef DEBUG + printf("node full (level %d) - splitting\n", + handle->level); +#endif + retval = extent_node_split(handle); + if (retval) + return retval; + path = handle->path + handle->level; + } + } + + eh = (struct ext3_extent_header *) path->buf; + if (path->curr) { + ix = path->curr; + if (flags & EXT2_EXTENT_INSERT_AFTER) { + ix++; + path->left--; + } + } else + ix = EXT_FIRST_INDEX(eh); + + path->curr = ix; + + if (path->left >= 0) + memmove(ix + 1, ix, + (path->left+1) * sizeof(struct ext3_extent_idx)); + path->left++; + path->entries++; + + eh = (struct ext3_extent_header *) path->buf; + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + + retval = ext2fs_extent_replace(handle, 0, extent); + if (retval) + goto errout; + + retval = update_path(handle); + if (retval) + goto errout; + + return 0; + +errout: + ext2fs_extent_delete(handle, 0); + return retval; +} + +/* + * Sets the physical block for a logical file block in the extent tree. + * + * May: map unmapped, unmap mapped, or remap mapped blocks. + * + * Mapping an unmapped block adds a single-block extent. + * + * Unmapping first or last block modifies extent in-place + * - But may need to fix parent's starts too in first-block case + * + * Mapping any unmapped block requires adding a (single-block) extent + * and inserting into proper point in tree. + * + * Modifying (unmapping or remapping) a block in the middle + * of an extent requires splitting the extent. + * - Remapping case requires new single-block extent. + * + * Remapping first or last block adds an extent. + * + * We really need extent adding to be smart about merging. + */ + +errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, + blk64_t logical, blk64_t physical, int flags) +{ + errcode_t ec, retval = 0; + int mapped = 1; /* logical is mapped? */ + int orig_height; + int extent_uninit = 0; + int prev_uninit = 0; + int next_uninit = 0; + int new_uninit = 0; + int max_len = EXT_INIT_MAX_LEN; + int has_prev, has_next; + blk64_t orig_lblk; + struct extent_path *path; + struct ext2fs_extent extent, next_extent, prev_extent; + struct ext2fs_extent newextent; + struct ext2_extent_info info; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + +#ifdef DEBUG + printf("set_bmap ino %u log %lld phys %lld flags %d\n", + handle->ino, logical, physical, flags); +#endif + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + + if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) { + new_uninit = 1; + max_len = EXT_UNINIT_MAX_LEN; + } + + /* if (re)mapping, set up new extent to insert */ + if (physical) { + newextent.e_len = 1; + newextent.e_pblk = physical; + newextent.e_lblk = logical; + newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF; + if (new_uninit) + newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + + /* special case if the extent tree is completely empty */ + if ((handle->max_depth == 0) && (path->entries == 0)) { + retval = ext2fs_extent_insert(handle, 0, &newextent); + return retval; + } + + /* save our original location in the extent tree */ + if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &extent))) { + if (retval != EXT2_ET_NO_CURRENT_NODE) + return retval; + memset(&extent, 0, sizeof(extent)); + } + if ((retval = ext2fs_extent_get_info(handle, &info))) + return retval; + orig_height = info.max_depth - info.curr_level; + orig_lblk = extent.e_lblk; + + /* go to the logical spot we want to (re/un)map */ + retval = ext2fs_extent_goto(handle, logical); + if (retval) { + if (retval == EXT2_ET_EXTENT_NOT_FOUND) { + retval = 0; + mapped = 0; + if (!physical) { +#ifdef DEBUG + printf("block %llu already unmapped\n", + logical); +#endif + goto done; + } + } else + goto done; + } + + /* + * This may be the extent *before* the requested logical, + * if it's currently unmapped. + * + * Get the previous and next leaf extents, if they are present. + */ + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + extent_uninit = 1; + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); + if (retval) { + has_next = 0; + if (retval != EXT2_ET_EXTENT_NO_NEXT) + goto done; + } else { + dbg_print_extent("set_bmap: next_extent", + &next_extent); + has_next = 1; + if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + next_uninit = 1; + } + retval = ext2fs_extent_goto(handle, logical); + if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) + goto done; + retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent); + if (retval) { + has_prev = 0; + if (retval != EXT2_ET_EXTENT_NO_PREV) + goto done; + } else { + has_prev = 1; + dbg_print_extent("set_bmap: prev_extent", + &prev_extent); + if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + prev_uninit = 1; + } + retval = ext2fs_extent_goto(handle, logical); + if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) + goto done; + + /* check if already pointing to the requested physical */ + if (mapped && (new_uninit == extent_uninit) && + (extent.e_pblk + (logical - extent.e_lblk) == physical)) { +#ifdef DEBUG + printf("physical block (at %llu) unchanged\n", logical); +#endif + goto done; + } + + if (!mapped) { +#ifdef DEBUG + printf("mapping unmapped logical block %llu\n", logical); +#endif + if ((logical == extent.e_lblk + extent.e_len) && + (physical == extent.e_pblk + extent.e_len) && + (new_uninit == extent_uninit) && + ((int) extent.e_len < max_len-1)) { + extent.e_len++; + retval = ext2fs_extent_replace(handle, 0, &extent); + } else if ((logical == extent.e_lblk - 1) && + (physical == extent.e_pblk - 1) && + (new_uninit == extent_uninit) && + ((int) extent.e_len < max_len - 1)) { + extent.e_len++; + extent.e_lblk--; + extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, &extent); + } else if (has_next && + (logical == next_extent.e_lblk - 1) && + (physical == next_extent.e_pblk - 1) && + (new_uninit == next_uninit) && + ((int) next_extent.e_len < max_len - 1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, + &next_extent); + if (retval) + goto done; + next_extent.e_len++; + next_extent.e_lblk--; + next_extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, &next_extent); + } else if (logical < extent.e_lblk) + retval = ext2fs_extent_insert(handle, 0, &newextent); + else + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + retval = ext2fs_extent_fix_parents(handle); + if (retval) + goto done; + } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) { +#ifdef DEBUG + printf("(re/un)mapping only block in extent\n"); +#endif + if (physical) { + retval = ext2fs_extent_replace(handle, 0, &newextent); + } else { + retval = ext2fs_extent_delete(handle, 0); + if (retval) + goto done; + ec = ext2fs_extent_fix_parents(handle); + if (ec != EXT2_ET_NO_CURRENT_NODE) + retval = ec; + } + + if (retval) + goto done; + } else if (logical == extent.e_lblk + extent.e_len - 1) { +#ifdef DEBUG + printf("(re/un)mapping last block in extent\n"); +#endif + if (physical) { + if (has_next && + (logical == (next_extent.e_lblk - 1)) && + (physical == (next_extent.e_pblk - 1)) && + (new_uninit == next_uninit) && + ((int) next_extent.e_len < max_len - 1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, &next_extent); + if (retval) + goto done; + next_extent.e_len++; + next_extent.e_lblk--; + next_extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, + &next_extent); + if (retval) + goto done; + } else + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + /* Now pointing at inserted extent; move back to prev */ + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, + &extent); + if (retval) + goto done; + } + extent.e_len--; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else if (logical == extent.e_lblk) { +#ifdef DEBUG + printf("(re/un)mapping first block in extent\n"); +#endif + if (physical) { + if (has_prev && + (logical == (prev_extent.e_lblk + + prev_extent.e_len)) && + (physical == (prev_extent.e_pblk + + prev_extent.e_len)) && + (new_uninit == prev_uninit) && + ((int) prev_extent.e_len < max_len-1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, &prev_extent); + if (retval) + goto done; + prev_extent.e_len++; + retval = ext2fs_extent_replace(handle, 0, + &prev_extent); + } else + retval = ext2fs_extent_insert(handle, + 0, &newextent); + if (retval) + goto done; + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, + &extent); + if (retval) + goto done; + } + extent.e_pblk++; + extent.e_lblk++; + extent.e_len--; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else { + __u32 orig_length; + +#ifdef DEBUG + printf("(re/un)mapping in middle of extent\n"); +#endif + /* need to split this extent; later */ + + orig_length = extent.e_len; + + /* shorten pre-split extent */ + extent.e_len = (logical - extent.e_lblk); + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + /* insert our new extent, if any */ + if (physical) { + /* insert new extent after current */ + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + } + /* add post-split extent */ + extent.e_pblk += extent.e_len + 1; + extent.e_lblk += extent.e_len + 1; + extent.e_len = orig_length - extent.e_len - 1; + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &extent); + if (retval) + goto done; + } + +done: + /* get handle back to its position */ + if (orig_height > handle->max_depth) + orig_height = handle->max_depth; /* In case we shortened the tree */ + extent_goto(handle, orig_height, orig_lblk); + return retval; +} + +errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags) +{ + struct extent_path *path; + char *cp; + struct ext3_extent_header *eh; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + { + struct ext2fs_extent extent; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &extent); + if (retval == 0) { + printf("extent delete %u ", handle->ino); + dbg_print_extent(0, &extent); + } + } +#endif + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + cp = path->curr; + + if (path->left) { + memmove(cp, cp + sizeof(struct ext3_extent_idx), + path->left * sizeof(struct ext3_extent_idx)); + path->left--; + } else { + struct ext3_extent_idx *ix = path->curr; + ix--; + path->curr = ix; + } + if (--path->entries == 0) + path->curr = 0; + + /* if non-root node has no entries left, remove it & parent ptr to it */ + if (path->entries == 0 && handle->level) { + if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) { + struct ext2fs_extent extent; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, + &extent); + if (retval) + return retval; + + retval = ext2fs_extent_delete(handle, flags); + handle->inode->i_blocks -= handle->fs->blocksize / 512; + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + ext2fs_block_alloc_stats2(handle->fs, + extent.e_pblk, -1); + } + } else { + eh = (struct ext3_extent_header *) path->buf; + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + if ((path->entries == 0) && (handle->level == 0)) + eh->eh_depth = handle->max_depth = 0; + retval = update_path(handle); + } + return retval; +} + +errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, + struct ext2_extent_info *info) +{ + struct extent_path *path; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + memset(info, 0, sizeof(struct ext2_extent_info)); + + path = handle->path + handle->level; + if (path) { + if (path->curr) + info->curr_entry = ((char *) path->curr - path->buf) / + sizeof(struct ext3_extent_idx); + else + info->curr_entry = 0; + info->num_entries = path->entries; + info->max_entries = path->max_entries; + info->bytes_avail = (path->max_entries - path->entries) * + sizeof(struct ext3_extent); + } + + info->curr_level = handle->level; + info->max_depth = handle->max_depth; + info->max_lblk = ((__u64) 1 << 32) - 1; + info->max_pblk = ((__u64) 1 << 48) - 1; + info->max_len = (1UL << 15); + info->max_uninit_len = (1UL << 15) - 1; + + return 0; +} + +#ifdef DEBUG + +#include "ss/ss.h" + +#include "debugfs.h" + +/* + * Hook in new commands into debugfs + */ +const char *debug_prog_name = "tst_extents"; +extern ss_request_table extent_cmds; +ss_request_table *extra_cmds = &extent_cmds; + +ext2_ino_t current_ino = 0; +ext2_extent_handle_t current_handle; + +int common_extent_args_process(int argc, char *argv[], int min_argc, + int max_argc, const char *cmd, + const char *usage, int flags) +{ + if (common_args_process(argc, argv, min_argc, max_argc, cmd, + usage, flags)) + return 1; + + if (!current_handle) { + com_err(cmd, 0, "Extent handle not open"); + return 1; + } + return 0; +} + +void do_inode(int argc, char *argv[]) +{ + ext2_ino_t inode; + int i; + struct ext3_extent_header *eh; + errcode_t retval; + + if (check_fs_open(argv[0])) + return; + + if (argc == 1) { + if (current_ino) + printf("Current inode is %d\n", current_ino); + else + printf("No current inode\n"); + return; + } + + if (common_inode_args_process(argc, argv, &inode, 0)) { + return; + } + + current_ino = 0; + + retval = ext2fs_extent_open(current_fs, inode, ¤t_handle); + if (retval) { + com_err(argv[1], retval, "while opening extent handle"); + return; + } + + current_ino = inode; + + printf("Loaded inode %d\n", current_ino); + + return; +} + +void generic_goto_node(char *cmd_name, int op) +{ + struct ext2fs_extent extent; + errcode_t retval; + + if (check_fs_open(cmd_name)) + return; + + if (!current_handle) { + com_err(cmd_name, 0, "Extent handle not open"); + return; + } + + retval = ext2fs_extent_get(current_handle, op, &extent); + if (retval) { + com_err(cmd_name, retval, 0); + return; + } + dbg_print_extent(0, &extent); +} + +void do_current_node(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); +} + +void do_root_node(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_ROOT); +} + +void do_last_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_LAST_LEAF); +} + +void do_first_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_FIRST_SIB); +} + +void do_last_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_LAST_SIB); +} + +void do_next_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT_SIB); +} + +void do_prev_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV_SIB); +} + +void do_next_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT_LEAF); +} + +void do_prev_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV_LEAF); +} + +void do_next(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT); +} + +void do_prev(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV); +} + +void do_up(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_UP); +} + +void do_down(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_DOWN); +} + +void do_delete_node(int argc, char *argv[]) +{ + errcode_t retval; + int err; + + if (common_extent_args_process(argc, argv, 1, 1, "delete_node", + "", CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + retval = ext2fs_extent_delete(current_handle, 0); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + if (current_handle->path && current_handle->path[0].curr) + do_current_node(argc, argv); +} + +void do_replace_node(int argc, char *argv[]) +{ + const char *usage = "[--uninit] "; + errcode_t retval; + struct ext2fs_extent extent; + int err; + + if (common_extent_args_process(argc, argv, 3, 5, "replace_node", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + extent.e_flags = 0; + + if (!strcmp(argv[1], "--uninit")) { + argc--; + argv++; + extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + + if (argc != 4) { + fprintf(stderr, "Usage: %s %s\n", argv[0], usage); + return; + } + + extent.e_lblk = parse_ulong(argv[1], argv[0], "logical block", &err); + if (err) + return; + + extent.e_len = parse_ulong(argv[2], argv[0], "logical block", &err); + if (err) + return; + + extent.e_pblk = parse_ulong(argv[3], argv[0], "logical block", &err); + if (err) + return; + + retval = ext2fs_extent_replace(current_handle, 0, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_split_node(int argc, char *argv[]) +{ + errcode_t retval; + struct ext2fs_extent extent; + int err; + + if (common_extent_args_process(argc, argv, 1, 1, "split_node", + "", CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + retval = extent_node_split(current_handle); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_insert_node(int argc, char *argv[]) +{ + const char *usage = "[--after] [--uninit] "; + errcode_t retval; + struct ext2fs_extent extent; + char *cmd; + int err; + int flags = 0; + + if (common_extent_args_process(argc, argv, 3, 6, "insert_node", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + cmd = argv[0]; + + extent.e_flags = 0; + + while (argc > 2) { + if (!strcmp(argv[1], "--after")) { + argc--; + argv++; + flags |= EXT2_EXTENT_INSERT_AFTER; + continue; + } + if (!strcmp(argv[1], "--uninit")) { + argc--; + argv++; + extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + continue; + } + break; + } + + if (argc != 4) { + fprintf(stderr, "usage: %s %s\n", cmd, usage); + return; + } + + extent.e_lblk = parse_ulong(argv[1], cmd, + "logical block", &err); + if (err) + return; + + extent.e_len = parse_ulong(argv[2], cmd, + "length", &err); + if (err) + return; + + extent.e_pblk = parse_ulong(argv[3], cmd, + "pysical block", &err); + if (err) + return; + + retval = ext2fs_extent_insert(current_handle, flags, &extent); + if (retval) { + com_err(cmd, retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_set_bmap(int argc, char **argv) +{ + const char *usage = "[--uninit] "; + errcode_t retval; + blk_t logical; + blk_t physical; + char *cmd = argv[0]; + int flags = 0; + int err; + + if (common_extent_args_process(argc, argv, 3, 5, "set_bmap", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + if (argc > 2 && !strcmp(argv[1], "--uninit")) { + argc--; + argv++; + flags |= EXT2_EXTENT_SET_BMAP_UNINIT; + } + + if (argc != 3) { + fprintf(stderr, "Usage: %s %s\n", cmd, usage); + return; + } + + logical = parse_ulong(argv[1], cmd, + "logical block", &err); + if (err) + return; + + physical = parse_ulong(argv[2], cmd, + "physical block", &err); + if (err) + return; + + retval = ext2fs_extent_set_bmap(current_handle, logical, + (blk64_t) physical, flags); + if (retval) { + com_err(cmd, retval, 0); + return; + } + if (current_handle->path && current_handle->path[0].curr) + do_current_node(argc, argv); +} + +void do_print_all(int argc, char **argv) +{ + const char *usage = "[--leaf-only|--reverse|--reverse-leaf]"; + struct ext2fs_extent extent; + errcode_t retval; + errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT; + int op = EXT2_EXTENT_NEXT; + int first_op = EXT2_EXTENT_ROOT; + + + if (common_extent_args_process(argc, argv, 1, 2, "print_all", + usage, 0)) + return; + + if (argc == 2) { + if (!strcmp(argv[1], "--leaf-only")) + op = EXT2_EXTENT_NEXT_LEAF; + else if (!strcmp(argv[1], "--reverse")) { + op = EXT2_EXTENT_PREV; + first_op = EXT2_EXTENT_LAST_LEAF; + end_err = EXT2_ET_EXTENT_NO_PREV; + } else if (!strcmp(argv[1], "--reverse-leaf")) { + op = EXT2_EXTENT_PREV_LEAF; + first_op = EXT2_EXTENT_LAST_LEAF; + end_err = EXT2_ET_EXTENT_NO_PREV; + } else { + fprintf(stderr, "Usage: %s %s\n", argv[0], usage); + return; + } + } + + retval = ext2fs_extent_get(current_handle, first_op, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + dbg_print_extent(0, &extent); + + while (1) { + retval = ext2fs_extent_get(current_handle, op, &extent); + if (retval == end_err) + break; + + if (retval) { + com_err(argv[0], retval, 0); + return; + } + dbg_print_extent(0, &extent); + } +} + +void do_info(int argc, char **argv) +{ + struct ext2fs_extent extent; + struct ext2_extent_info info; + errcode_t retval; + + if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0)) + return; + + retval = ext2fs_extent_get_info(current_handle, &info); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + + retval = ext2fs_extent_get(current_handle, + EXT2_EXTENT_CURRENT, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + + dbg_print_extent(0, &extent); + + printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n", + info.curr_entry, info.num_entries, info.max_entries, + info.bytes_avail, info.curr_level, info.max_depth); + printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk, + info.max_pblk); + printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len, + info.max_uninit_len); +} + +void do_goto_block(int argc, char **argv) +{ + struct ext2fs_extent extent; + errcode_t retval; + int op = EXT2_EXTENT_NEXT_LEAF; + blk64_t blk; + int level = 0, err; + + if (common_extent_args_process(argc, argv, 2, 3, "goto_block", + "block [level]", 0)) + return; + + if (strtoblk(argv[0], argv[1], &blk)) + return; + + if (argc == 3) { + level = parse_ulong(argv[2], argv[0], "level", &err); + if (err) + return; + } + + retval = extent_goto(current_handle, level, (blk64_t) blk); + + if (retval) { + com_err(argv[0], retval, + "while trying to go to block %llu, level %d", + (unsigned long long) blk, level); + return; + } + + generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); +} +#endif + diff --git a/lib/libext2fs/source/fiemap.h b/lib/libext2fs/source/fiemap.h new file mode 100644 index 0000000..30bf555 --- /dev/null +++ b/lib/libext2fs/source/fiemap.h @@ -0,0 +1,68 @@ +/* + * FS_IOC_FIEMAP ioctl infrastructure. + * + * Some portions copyright (C) 2007 Cluster File Systems, Inc + * + * Authors: Mark Fasheh + * Kalpak Shah + * Andreas Dilger + */ + +#ifndef _LINUX_FIEMAP_H +#define _LINUX_FIEMAP_H + +struct fiemap_extent { + __u64 fe_logical; /* logical offset in bytes for the start of + * the extent from the beginning of the file */ + __u64 fe_physical; /* physical offset in bytes for the start + * of the extent from the beginning of the disk */ + __u64 fe_length; /* length in bytes for this extent */ + __u64 fe_reserved64[2]; + __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ + __u32 fe_reserved[3]; +}; + +struct fiemap { + __u64 fm_start; /* logical offset (inclusive) at + * which to start mapping (in) */ + __u64 fm_length; /* logical length of mapping which + * userspace wants (in) */ + __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ + __u32 fm_mapped_extents;/* number of extents that were mapped (out) */ + __u32 fm_extent_count; /* size of fm_extents array (in) */ + __u32 fm_reserved; + struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ +}; + +#ifndef FS_IOC_FIEMAP +#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) +#endif + +#define FIEMAP_MAX_OFFSET (~0ULL) + +#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ +#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ + +#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) + +#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ +#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ +#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. + * Sets EXTENT_UNKNOWN. */ +#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read + * while fs is unmounted */ +#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. + * Sets EXTENT_NO_BYPASS. */ +#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be + * block aligned. */ +#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but + * no data (i.e. zero). */ +#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively + * support extents. Result + * merged for efficiency. */ + +#endif /* _LINUX_FIEMAP_H */ diff --git a/lib/libext2fs/source/fileio.c b/lib/libext2fs/source/fileio.c new file mode 100644 index 0000000..44c859b --- /dev/null +++ b/lib/libext2fs/source/fileio.c @@ -0,0 +1,412 @@ +/* + * fileio.c --- Simple file I/O routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct ext2_file { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + __u64 pos; + blk64_t blockno; + blk64_t physblock; + char *buf; +}; + +#define BMAP_BUFFER (file->buf + fs->blocksize) + +errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret) +{ + ext2_file_t file; + errcode_t retval; + + /* + * Don't let caller create or open a file for writing if the + * filesystem is read-only. + */ + if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && + !(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); + if (retval) + return retval; + + memset(file, 0, sizeof(struct ext2_file)); + file->magic = EXT2_ET_MAGIC_EXT2_FILE; + file->fs = fs; + file->ino = ino; + file->flags = flags & EXT2_FILE_MASK; + + if (inode) { + memcpy(&file->inode, inode, sizeof(struct ext2_inode)); + } else { + retval = ext2fs_read_inode(fs, ino, &file->inode); + if (retval) + goto fail; + } + + retval = ext2fs_get_array(3, fs->blocksize, &file->buf); + if (retval) + goto fail; + + *ret = file; + return 0; + +fail: + if (file->buf) + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + return retval; +} + +errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret) +{ + return ext2fs_file_open2(fs, ino, NULL, flags, ret); +} + +/* + * This function returns the filesystem handle of a file from the structure + */ +ext2_filsys ext2fs_file_get_fs(ext2_file_t file) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return 0; + return file->fs; +} + +/* + * This function returns the pointer to the inode of a file from the structure + */ +struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return NULL; + return &file->inode; +} + +/* + * This function flushes the dirty block buffer out to disk if + * necessary. + */ +errcode_t ext2fs_file_flush(ext2_file_t file) +{ + errcode_t retval; + ext2_filsys fs; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_BUF_VALID) || + !(file->flags & EXT2_FILE_BUF_DIRTY)) + return 0; + + // Flushing out the new size - Dimok + ext2fs_write_inode(file->fs, file->ino, &file->inode); + + /* + * OK, the physical block hasn't been allocated yet. + * Allocate it. + */ + if (!file->physblock) { + retval = ext2fs_bmap2(fs, file->ino, &file->inode, + BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, + file->blockno, 0, &file->physblock); + if (retval) + return retval; + } + + retval = io_channel_write_blk(fs->io, file->physblock, + 1, file->buf); + if (retval) + return retval; + + file->flags &= ~EXT2_FILE_BUF_DIRTY; + + return retval; +} + +/* + * This function synchronizes the file's block buffer and the current + * file position, possibly invalidating block buffer if necessary + */ +static errcode_t sync_buffer_position(ext2_file_t file) +{ + blk_t b; + errcode_t retval; + + b = file->pos / file->fs->blocksize; + if (b != file->blockno) { + retval = ext2fs_file_flush(file); + if (retval) + return retval; + file->flags &= ~EXT2_FILE_BUF_VALID; + } + file->blockno = b; + return 0; +} + +/* + * This function loads the file's block buffer with valid data from + * the disk as necessary. + * + * If dontfill is true, then skip initializing the buffer since we're + * going to be replacing its entire contents anyway. If set, then the + * function basically only sets file->physblock and EXT2_FILE_BUF_VALID + */ +#define DONTFILL 1 +static errcode_t load_buffer(ext2_file_t file, int dontfill) +{ + ext2_filsys fs = file->fs; + errcode_t retval; + + if (!(file->flags & EXT2_FILE_BUF_VALID)) { + retval = ext2fs_bmap2(fs, file->ino, &file->inode, + BMAP_BUFFER, 0, file->blockno, 0, + &file->physblock); + if (retval) + return retval; + if (!dontfill) { + if (file->physblock) { + retval = io_channel_read_blk(fs->io, + file->physblock, + 1, file->buf); + if (retval) + return retval; + } else + memset(file->buf, 0, fs->blocksize); + } + file->flags |= EXT2_FILE_BUF_VALID; + } + return 0; +} + + +errcode_t ext2fs_file_close(ext2_file_t file) +{ + errcode_t retval; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + retval = ext2fs_file_flush(file); + + if (file->buf) + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + + return retval; +} + + +errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + __u64 left; + char *ptr = (char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + retval = load_buffer(file, 0); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > wanted) + c = wanted; + left = EXT2_I_SIZE(&file->inode) - file->pos ; + if (c > left) + c = left; + + memcpy(ptr, file->buf+start, c); + file->pos += c; + ptr += c; + count += c; + wanted -= c; + } + +fail: + if (got) + *got = count; + return retval; +} + + +errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + const char *ptr = (const char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_WRITE)) + return EXT2_ET_FILE_RO; + + while (nbytes > 0) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > nbytes) + c = nbytes; + + /* + * We only need to do a read-modify-update cycle if + * we're doing a partial write. + */ + retval = load_buffer(file, (c == fs->blocksize)); + if (retval) + goto fail; + + file->flags |= EXT2_FILE_BUF_DIRTY; + memcpy(file->buf+start, ptr, c); + file->pos += c; + ptr += c; + count += c; + nbytes -= c; + } + + // I don't see why changing size is my duty - Dimok + if(EXT2_I_SIZE(&file->inode) < file->pos) + { + file->inode.i_size = file->pos & 0xFFFFFFFF; + file->inode.i_size_high = (file->pos >> 32) & 0xFFFFFFFF; + } + +fail: + if (written) + *written = count; + return retval; +} + +errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos) +{ + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + if (whence == EXT2_SEEK_SET) + file->pos = offset; + else if (whence == EXT2_SEEK_CUR) + file->pos += offset; + else if (whence == EXT2_SEEK_END) + file->pos = EXT2_I_SIZE(&file->inode) + offset; + else + return EXT2_ET_INVALID_ARGUMENT; + + if (ret_pos) + *ret_pos = file->pos; + + return 0; +} + +errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos) +{ + __u64 loffset, ret_loffset = 0; + errcode_t retval; + + loffset = offset; + retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); + if (ret_pos) + *ret_pos = (ext2_off_t) ret_loffset; + return retval; +} + + +/* + * This function returns the size of the file, according to the inode + */ +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return EXT2_ET_MAGIC_EXT2_FILE; + *ret_size = EXT2_I_SIZE(&file->inode); + return 0; +} + +/* + * This function returns the size of the file, according to the inode + */ +ext2_off_t ext2fs_file_get_size(ext2_file_t file) +{ + __u64 size; + + if (ext2fs_file_get_lsize(file, &size)) + return 0; + if ((size >> 32) != 0) + return 0; + return size; +} + +/* + * This function sets the size of the file, truncating it if necessary + * + */ +errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) +{ + ext2_off64_t old_size; + errcode_t retval; + blk64_t old_truncate, truncate_block; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + truncate_block = ((size + file->fs->blocksize - 1) >> + EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + old_size = file->inode.i_size + + (((blk64_t) file->inode.i_size_high) << 32); + old_truncate = ((old_size + file->fs->blocksize - 1) >> + EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + + file->inode.i_size = size & 0xffffffff; + file->inode.i_size_high = (size >> 32); + if (file->ino) { + retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); + if (retval) + return retval; + } + + if (truncate_block <= old_truncate) + return 0; + + return ext2fs_punch(file->fs, file->ino, &file->inode, 0, + truncate_block, ~0ULL); +} + +errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) +{ + return ext2fs_file_set_size2(file, size); +} diff --git a/lib/libext2fs/source/finddev.c b/lib/libext2fs/source/finddev.c new file mode 100644 index 0000000..cc2029f --- /dev/null +++ b/lib/libext2fs/source/finddev.c @@ -0,0 +1,208 @@ +/* + * finddev.c -- this routine attempts to find a particular device in + * /dev + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#include +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_SYS_MKDEV_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct dir_list { + char *name; + struct dir_list *next; +}; + +/* + * This function adds an entry to the directory list + */ +static void add_to_dirlist(const char *name, struct dir_list **list) +{ + struct dir_list *dp; + + dp = malloc(sizeof(struct dir_list)); + if (!dp) + return; + dp->name = malloc(strlen(name)+1); + if (!dp->name) { + free(dp); + return; + } + strcpy(dp->name, name); + dp->next = *list; + *list = dp; +} + +/* + * This function frees a directory list + */ +static void free_dirlist(struct dir_list **list) +{ + struct dir_list *dp, *next; + + for (dp = *list; dp; dp = next) { + next = dp->next; + free(dp->name); + free(dp); + } + *list = 0; +} + +static int scan_dir(char *dirname, dev_t device, struct dir_list **list, + char **ret_path) +{ + DIR *dir; + struct dirent *dp; + char path[1024], *cp; + int dirlen; + struct stat st; + + dirlen = strlen(dirname); + if ((dir = opendir(dirname)) == NULL) + return errno; + dp = readdir(dir); + while (dp) { + if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) + goto skip_to_next; + if (dp->d_name[0] == '.' && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) + goto skip_to_next; + sprintf(path, "%s/%s", dirname, dp->d_name); + if (stat(path, &st) < 0) + goto skip_to_next; + if (S_ISDIR(st.st_mode)) + add_to_dirlist(path, list); + if (S_ISBLK(st.st_mode) && st.st_rdev == device) { + cp = malloc(strlen(path)+1); + if (!cp) { + closedir(dir); + return ENOMEM; + } + strcpy(cp, path); + *ret_path = cp; + goto success; + } + skip_to_next: + dp = readdir(dir); + } +success: + closedir(dir); + return 0; +} + +/* + * This function finds the pathname to a block device with a given + * device number. It returns a pointer to allocated memory to the + * pathname on success, and NULL on failure. + */ +char *ext2fs_find_block_device(dev_t device) +{ + struct dir_list *list = 0, *new_list = 0; + struct dir_list *current; + char *ret_path = 0; + + /* + * Add the starting directories to search... + */ + add_to_dirlist("/devices", &list); + add_to_dirlist("/devfs", &list); + add_to_dirlist("/dev", &list); + + while (list) { + current = list; + list = list->next; +#ifdef DEBUG + printf("Scanning directory %s\n", current->name); +#endif + scan_dir(current->name, device, &new_list, &ret_path); + free(current->name); + free(current); + if (ret_path) + break; + /* + * If we're done checking at this level, descend to + * the next level of subdirectories. (breadth-first) + */ + if (list == 0) { + list = new_list; + new_list = 0; + } + } + free_dirlist(&list); + free_dirlist(&new_list); + return ret_path; +} + + +#ifdef DEBUG +int main(int argc, char** argv) +{ + char *devname, *tmp; + int major, minor; + dev_t device; + const char *errmsg = "Couldn't parse %s: %s\n"; + + if ((argc != 2) && (argc != 3)) { + fprintf(stderr, "Usage: %s device_number\n", argv[0]); + fprintf(stderr, "\t: %s major minor\n", argv[0]); + exit(1); + } + if (argc == 2) { + device = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "device number", argv[1]); + exit(1); + } + } else { + major = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "major number", argv[1]); + exit(1); + } + minor = strtoul(argv[2], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "minor number", argv[2]); + exit(1); + } + device = makedev(major, minor); + printf("Looking for device 0x%04x (%d:%d)\n", device, + major, minor); + } + devname = ext2fs_find_block_device(device); + if (devname) { + printf("Found device! %s\n", devname); + free(devname); + } else { + printf("Couldn't find device.\n"); + } + return 0; +} + +#endif diff --git a/lib/libext2fs/source/flushb.c b/lib/libext2fs/source/flushb.c new file mode 100644 index 0000000..b6f161b --- /dev/null +++ b/lib/libext2fs/source/flushb.c @@ -0,0 +1,74 @@ +/* + * flushb.c --- Hides system-dependent information for both syncing a + * device to disk and to flush any buffers from disk cache. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#include /* This may define BLKFLSBUF */ +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since + * not all portable header file does so for us. This really should be + * fixed in the glibc header files. (Recent glibcs appear to define + * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be + * defined anywhere portable.) Until then.... + */ +#ifdef __linux__ +#ifndef BLKFLSBUF +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#endif +#ifndef FDFLUSH +#define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ +#endif +#endif + +/* + * This function will sync a device/file, and optionally attempt to + * flush the buffer cache. The latter is basically only useful for + * system benchmarks and for torturing systems in burn-in tests. :) + */ +errcode_t ext2fs_sync_device(int fd, int flushb) +{ + /* + * We always sync the device in case we're running on old + * kernels for which we can lose data if we don't. (There + * still is a race condition for those kernels, but this + * reduces it greatly.) + */ + if (fsync (fd) == -1) + return errno; + + if (flushb) { + +#ifdef BLKFLSBUF + if (ioctl (fd, BLKFLSBUF, 0) == 0) + return 0; +#endif +#ifdef FDFLUSH + ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ +#endif + } + return 0; +} diff --git a/lib/libext2fs/source/freefs.c b/lib/libext2fs/source/freefs.c new file mode 100644 index 0000000..5c35bb6 --- /dev/null +++ b/lib/libext2fs/source/freefs.c @@ -0,0 +1,112 @@ +/* + * freefs.c --- free an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); + +void ext2fs_free(ext2_filsys fs) +{ + if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) + return; + if (fs->image_io != fs->io) { + if (fs->image_io) + io_channel_close(fs->image_io); + } + if (fs->io) { + io_channel_close(fs->io); + } + if (fs->device_name) + ext2fs_free_mem(&fs->device_name); + if (fs->super) + ext2fs_free_mem(&fs->super); + if (fs->orig_super) + ext2fs_free_mem(&fs->orig_super); + if (fs->group_desc) + ext2fs_free_mem(&fs->group_desc); + if (fs->block_map) + ext2fs_free_block_bitmap(fs->block_map); + if (fs->inode_map) + ext2fs_free_inode_bitmap(fs->inode_map); + + if (fs->badblocks) + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + + if (fs->dblist) + ext2fs_free_dblist(fs->dblist); + + if (fs->icache) + ext2fs_free_inode_cache(fs->icache); + + fs->magic = 0; + + ext2fs_free_mem(&fs); +} + +/* + * Free the inode cache structure + */ +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) +{ + if (--icache->refcount) + return; + if (icache->buffer) + ext2fs_free_mem(&icache->buffer); + if (icache->cache) + ext2fs_free_mem(&icache->cache); + icache->buffer_blk = 0; + ext2fs_free_mem(&icache); +} + +/* + * This procedure frees a badblocks list. + */ +void ext2fs_u32_list_free(ext2_u32_list bb) +{ + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return; + + if (bb->list) + ext2fs_free_mem(&bb->list); + bb->list = 0; + ext2fs_free_mem(&bb); +} + +void ext2fs_badblocks_list_free(ext2_badblocks_list bb) +{ + ext2fs_u32_list_free((ext2_u32_list) bb); +} + + +/* + * Free a directory block list + */ +void ext2fs_free_dblist(ext2_dblist dblist) +{ + if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) + return; + + if (dblist->list) + ext2fs_free_mem(&dblist->list); + dblist->list = 0; + if (dblist->fs && dblist->fs->dblist == dblist) + dblist->fs->dblist = 0; + dblist->magic = 0; + ext2fs_free_mem(&dblist); +} + diff --git a/lib/libext2fs/source/gekko_io.c b/lib/libext2fs/source/gekko_io.c new file mode 100644 index 0000000..d028c3f --- /dev/null +++ b/lib/libext2fs/source/gekko_io.c @@ -0,0 +1,561 @@ +/** + * gekko_io.c - Gekko style disk io functions. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gekko_io.h" +#include "bitops.h" +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" +#include "disc_cache.h" +#include "mem_allocate.h" + +#define DEV_FD(dev) ((gekko_fd *) dev->private_data) + +/* Prototypes */ +static s64 device_gekko_io_readbytes(io_channel dev, s64 offset, s64 count, void *buf); +static bool device_gekko_io_readsectors(io_channel dev, sec_t sector, sec_t numSectors, void* buffer); +static s64 device_gekko_io_writebytes(io_channel dev, s64 offset, s64 count, const void *buf); +static bool device_gekko_io_writesectors(io_channel dev, sec_t sector, sec_t numSectors, const void* buffer); + +/** + * + */ +static errcode_t device_gekko_io_open(const char *name, int flags, io_channel *dev) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD((*dev)); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Start the device interface and ensure that it is inserted + if (!interface->startup()) { + ext2_log_trace("device failed to start\n"); + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + ext2_log_trace("device media is not inserted\n"); + errno = EIO; + return -1; + } + + struct ext2_super_block * super = (struct ext2_super_block *) mem_alloc(SUPERBLOCK_SIZE); //1024 bytes + if(!super) + { + ext2_log_trace("no memory for superblock"); + errno = ENOMEM; + return -1; + } + + // Check that there is a valid EXT boot sector at the start of the device + if (!interface->readSectors(fd->startSector+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + ext2_log_trace("read failure @ sector %d\n", fd->startSector); + errno = EROFS; + mem_free(super); + return -1; + } + + if(ext2fs_le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) + { + mem_free(super); + errno = EROFS; + return -1; + } + + // Parse the boot sector + fd->sectorSize = BYTES_PER_SECTOR; + fd->offset = 0; + fd->sectorCount = 0; + + switch(ext2fs_le32_to_cpu(super->s_log_block_size)) + { + case 1: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 2048 / (u64) BYTES_PER_SECTOR); + break; + case 2: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 4096 / (u64) BYTES_PER_SECTOR); + break; + case 3: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 8192 / (u64) BYTES_PER_SECTOR); + break; + default: + case 0: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 1024 / (u64) BYTES_PER_SECTOR); + break; + } + + mem_free(super); + + // Create the cache + fd->cache = _EXT2_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); + + return 0; +} + +/** + * Flush data out and close volume + */ +static errcode_t device_gekko_io_close(io_channel dev) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + if(!(dev->flags & EXT2_FLAG_RW)) + return 0; + + // Flush and destroy the cache (if required) + if (fd->cache) { + _EXT2_cache_flush(fd->cache); + _EXT2_cache_destructor(fd->cache); + } + + return 0; +} + +/** + * + */ +static s64 device_gekko_io_readbytes(io_channel dev, s64 offset, s64 count, void *buf) +{ + ext2_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(offset < 0) + { + errno = EROFS; + return -1; + } + + if(!count) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this read + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // Don't read over the partitions limit + if(sec_start+sec_count > fd->startSector+fd->sectorCount) + { + ext2_log_trace("Error: read requested up to sector %lli while partition goes up to %lli\n", (s64) (sec_start+sec_count), (s64) (fd->startSector+fd->sectorCount)); + errno = EROFS; + return -1; + } + + // If this read happens to be on the sector boundaries then do the read straight into the destination buffer + + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Read from the device + ext2_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) + { + ext2_log_trace("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else read into a buffer and copy over only what was requested + } + else + { + + // Allocate a buffer to hold the read data + buffer = (u8*)mem_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + + // Read from the device + ext2_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + ext2_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); + if (!device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { + ext2_log_trace("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + mem_free(buffer); + errno = EIO; + return -1; + } + + // Copy what was requested to the destination buffer + memcpy(buf, buffer + buffer_offset, count); + mem_free(buffer); + + } + + return count; +} + +/** + * + */ +static s64 device_gekko_io_writebytes(io_channel dev, s64 offset, s64 count, const void *buf) +{ + ext2_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + if(!(dev->flags & EXT2_FLAG_RW)) + return -1; + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this write + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // Don't write over the partitions limit + if(sec_start+sec_count > fd->startSector+fd->sectorCount) + { + ext2_log_trace("Error: write requested up to sector %lli while partition goes up to %lli\n", (s64) (sec_start+sec_count), (s64) (fd->startSector+fd->sectorCount)); + errno = EROFS; + return -1; + } + + // If this write happens to be on the sector boundaries then do the write straight to disc + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Write to the device + ext2_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { + ext2_log_trace("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else write from a buffer aligned to the sector boundaries + } + else + { + // Allocate a buffer to hold the write data + buffer = (u8 *) mem_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + // Read the first and last sectors of the buffer from disc (if required) + // NOTE: This is done because the data does not line up with the sector boundaries, + // we just read in the buffer edges where the data overlaps with the rest of the disc + if(buffer_offset != 0) + { + if (!device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ext2_log_trace("read failure @ sector %d\n", sec_start); + mem_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ext2_log_trace("read failure @ sector %d\n", sec_start + sec_count - 1); + mem_free(buffer); + errno = EIO; + return -1; + } + } + + // Copy the data into the write buffer + memcpy(buffer + buffer_offset, buf, count); + + // Write to the device + ext2_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) { + ext2_log_trace("buffered write failure @ sector %d\n", sec_start); + mem_free(buffer); + errno = EIO; + return -1; + } + + // Free the buffer + mem_free(buffer); + } + + return count; +} + + +/** + * Read function wrap for I/O manager + */ +static errcode_t device_gekko_io_read64(io_channel dev, unsigned long long block, int count, void *buf) +{ + gekko_fd *fd = DEV_FD(dev); + s64 size = (count < 0) ? -count : count * dev->block_size; + fd->io_stats.bytes_read += size; + ext2_loff_t location = ((ext2_loff_t) block * dev->block_size) + fd->offset; + + s64 read = device_gekko_io_readbytes(dev, location, size, buf); + if(read != size) + return EXT2_ET_SHORT_READ; + else if(read < 0) + return EXT2_ET_BLOCK_BITMAP_READ; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_io_read(io_channel dev, unsigned long block, int count, void *buf) +{ + return device_gekko_io_read64(dev, block, count, buf); +} + +/** + * Write function wrap for I/O manager + */ +static errcode_t device_gekko_io_write64(io_channel dev, unsigned long long block, int count, const void *buf) +{ + gekko_fd *fd = DEV_FD(dev); + s64 size = (count < 0) ? -count : count * dev->block_size; + fd->io_stats.bytes_written += size; + + ext2_loff_t location = ((ext2_loff_t) block * dev->block_size) + fd->offset; + + s64 writen = device_gekko_io_writebytes(dev, location, size, buf); + if(writen != size) + return EXT2_ET_SHORT_WRITE; + else if(writen < 0) + return EXT2_ET_BLOCK_BITMAP_WRITE; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_io_write(io_channel dev, unsigned long block, int count, const void *buf) +{ + return device_gekko_io_write64(dev, block, count, buf); +} + + +static bool device_gekko_io_readsectors(io_channel dev, sec_t sector, sec_t numSectors, void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + // Read the sectors from disc (or cache, if enabled) + if (fd->cache) + return _EXT2_cache_readSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->readSectors(sector, numSectors, buffer); + + return false; +} + +static bool device_gekko_io_writesectors(io_channel dev, sec_t sector, sec_t numSectors, const void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + + // Write the sectors to disc (or cache, if enabled) + if (fd->cache) + return _EXT2_cache_writeSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->writeSectors(sector, numSectors, buffer); + + return false; +} + +/** + * + */ +static errcode_t device_gekko_io_sync(io_channel dev) +{ + gekko_fd *fd = DEV_FD(dev); + ext2_log_trace("dev %p\n", dev); + + // Check that the device can be written to + if(!(dev->flags & EXT2_FLAG_RW)) + return -1; + + // Flush any sectors in the disc cache (if required) + if (fd->cache) { + if (!_EXT2_cache_flush(fd->cache)) { + errno = EIO; + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + } + + return EXT2_ET_OK; +} + +/** + * + */ +static errcode_t device_gekko_io_stat(io_channel dev, io_stats *stats) +{ + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + gekko_fd *fd = DEV_FD(dev); + + if (stats) + *stats = &fd->io_stats; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_set_blksize(io_channel dev, int blksize) +{ + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + + if (dev->block_size != blksize) + { + dev->block_size = blksize; + + return device_gekko_io_sync(dev); + } + + return EXT2_ET_OK; +} + +/** + * Set options. + */ +static errcode_t device_gekko_set_option(io_channel dev, const char *option, const char *arg) +{ + unsigned long long tmp; + char *end; + + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!strcmp(option, "offset")) { + if (!arg) + return EXT2_ET_INVALID_ARGUMENT; + + tmp = strtoull(arg, &end, 0); + if (*end) + return EXT2_ET_INVALID_ARGUMENT; + fd->offset = tmp; + if (fd->offset < 0) + return EXT2_ET_INVALID_ARGUMENT; + return 0; + } + return EXT2_ET_INVALID_ARGUMENT; +} + +static errcode_t device_gekko_discard(io_channel channel, unsigned long long block, unsigned long long count) +{ + //!TODO as soon as it is implemented in the official lib + return 0; +} + +/** + * Device operations for working with gekko style devices and files. + */ +const struct struct_io_manager struct_gekko_io_manager = +{ + EXT2_ET_MAGIC_IO_MANAGER, + "Wii/GC I/O Manager", + device_gekko_io_open, + device_gekko_io_close, + device_gekko_set_blksize, + device_gekko_io_read, + device_gekko_io_write, + device_gekko_io_sync, + 0, + device_gekko_set_option, + device_gekko_io_stat, + device_gekko_io_read64, + device_gekko_io_write64, + device_gekko_discard, +}; + +io_manager gekko_io_manager = (io_manager) &struct_gekko_io_manager; diff --git a/lib/libext2fs/source/gekko_io.h b/lib/libext2fs/source/gekko_io.h new file mode 100644 index 0000000..16786c7 --- /dev/null +++ b/lib/libext2fs/source/gekko_io.h @@ -0,0 +1,53 @@ +/* +* gekko_io.h - Platform specifics for device io. +* + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok +* +* This program/include file is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published +* by the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program/include file is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _GEKKO_IO_H +#define _GEKKO_IO_H + +#include +#include +#include "disc_cache.h" +#include "ext2fs.h" + +#define BYTES_PER_SECTOR 512 + +/** + * gekko_fd - Gekko device driver descriptor + */ +typedef struct _gekko_fd { + const DISC_INTERFACE* interface; /* Device disc interface */ + sec_t startSector; /* LBA of partition start */ + sec_t sectorCount; /* Total number of sectors in partition */ + u16 sectorSize; /* Device sector size (in bytes) */ + int flags; + int access_time; + ext2_loff_t offset; + struct struct_io_stats io_stats; + CACHE *cache; /* Cache */ + u32 cachePageCount; /* The number of pages in the cache */ + u32 cachePageSize; /* The number of sectors per cache page */ +} gekko_fd; + + +/* Gekko device driver i/o operations */ +extern io_manager gekko_io_manager; + +#endif /* _GEKKO_IO_H */ diff --git a/lib/libext2fs/source/gen_bitmap.c b/lib/libext2fs/source/gen_bitmap.c new file mode 100644 index 0000000..2ef1d82 --- /dev/null +++ b/lib/libext2fs/source/gen_bitmap.c @@ -0,0 +1,560 @@ +/* + * gen_bitmap.c --- Generic (32-bit) bitmap routines + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +struct ext2fs_struct_generic_bitmap { + errcode_t magic; + ext2_filsys fs; + __u32 start, end; + __u32 real_end; + char * description; + char * bitmap; + errcode_t base_error_code; + __u32 reserved[7]; +}; + +#define EXT2FS_IS_32_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP)) + +#define EXT2FS_IS_64_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP64)) + +/* + * Used by previously inlined function, so we have to export this and + * not change the function signature + */ +void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg) +{ +#ifndef OMIT_COM_ERR + if (bitmap->description) + com_err(0, bitmap->base_error_code+code, + "#%lu for %s", arg, bitmap->description); + else + com_err(0, bitmap->base_error_code + code, "#%lu", arg); +#endif +} + +static errcode_t check_magic(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap || !((bitmap->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || + (bitmap->magic == EXT2_ET_MAGIC_INODE_BITMAP) || + (bitmap->magic == EXT2_ET_MAGIC_BLOCK_BITMAP))) + return EXT2_ET_MAGIC_GENERIC_BITMAP; + return 0; +} + +errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, + __u32 start, __u32 end, __u32 real_end, + const char *descr, char *init_map, + ext2fs_generic_bitmap *ret) +{ + ext2fs_generic_bitmap bitmap; + errcode_t retval; + size_t size; + + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &bitmap); + if (retval) + return retval; + + bitmap->magic = magic; + bitmap->fs = fs; + bitmap->start = start; + bitmap->end = end; + bitmap->real_end = real_end; + switch (magic) { + case EXT2_ET_MAGIC_INODE_BITMAP: + bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; + break; + case EXT2_ET_MAGIC_BLOCK_BITMAP: + bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; + break; + default: + bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; + } + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); + if (retval) { + ext2fs_free_mem(&bitmap); + return retval; + } + strcpy(bitmap->description, descr); + } else + bitmap->description = 0; + + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + /* Round up to allow for the BT x86 instruction */ + size = (size + 7) & ~3; + retval = ext2fs_get_mem(size, &bitmap->bitmap); + if (retval) { + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap); + return retval; + } + + if (init_map) + memcpy(bitmap->bitmap, init_map, size); + else + memset(bitmap->bitmap, 0, size); + *ret = bitmap; + return 0; +} + +errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret) +{ + return ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_GENERIC_BITMAP, 0, + start, end, real_end, descr, 0, ret); +} + +errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + return (ext2fs_make_generic_bitmap(src->magic, src->fs, + src->start, src->end, + src->real_end, + src->description, src->bitmap, + dest)); +} + +void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) +{ + if (check_magic(bitmap)) + return; + + bitmap->magic = 0; + if (bitmap->description) { + ext2fs_free_mem(&bitmap->description); + bitmap->description = 0; + } + if (bitmap->bitmap) { + ext2fs_free_mem(&bitmap->bitmap); + bitmap->bitmap = 0; + } + ext2fs_free_mem(&bitmap); +} + +int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_test_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "test_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); + return 0; + } + return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_mark_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "mark_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); + return 0; + } + return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_unmark_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "mark_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); + return 0; + } + return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); +} + +__u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_get_generic_bmap_start(bitmap); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "get_bitmap_start"); + return 0; +#endif + } + + return bitmap->start; +} + +__u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_get_generic_bmap_end(bitmap); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "get_bitmap_end"); + return 0; +#endif + } + return bitmap->end; +} + +void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + ext2fs_clear_generic_bmap(bitmap); + return; + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "clear_generic_bitmap"); + return; +#endif + } + + memset(bitmap->bitmap, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} + +errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, + errcode_t magic, errcode_t neq, + ext2_ino_t end, ext2_ino_t *oend) +{ + EXT2_CHECK_MAGIC(bitmap, magic); + + if (end > bitmap->real_end) + return neq; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, + __u32 new_end, __u32 new_real_end, + ext2fs_generic_bitmap bmap) +{ + errcode_t retval; + size_t size, new_size; + __u32 bitno; + + if (!bmap || (bmap->magic != magic)) + return magic; + + /* + * If we're expanding the bitmap, make sure all of the new + * parts of the bitmap are zero. + */ + if (new_end > bmap->end) { + bitno = bmap->real_end; + if (bitno > new_end) + bitno = new_end; + for (; bitno > bmap->end; bitno--) + ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); + } + if (new_real_end == bmap->real_end) { + bmap->end = new_end; + return 0; + } + + size = ((bmap->real_end - bmap->start) / 8) + 1; + new_size = ((new_real_end - bmap->start) / 8) + 1; + + if (size != new_size) { + retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); + if (retval) + return retval; + } + if (new_size > size) + memset(bmap->bitmap + size, 0, new_size - size); + + bmap->end = new_end; + bmap->real_end = new_real_end; + return 0; +} + +errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2) +{ + blk_t i; + + if (!bm1 || bm1->magic != magic) + return magic; + if (!bm2 || bm2->magic != magic) + return magic; + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end) || + (memcmp(bm1->bitmap, bm2->bitmap, + (size_t) (bm1->end - bm1->start)/8))) + return neq; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_fast_test_block_bitmap(bm1, i) != + ext2fs_fast_test_block_bitmap(bm2, i)) + return neq; + + return 0; +} + +void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map) +{ + __u32 i, j; + + /* Protect loop from wrap-around if map->real_end is maxed */ + for (i=map->end+1, j = i - map->start; + i <= map->real_end && i > map->end; + i++, j++) + ext2fs_set_bit(j, map->bitmap); +} + +errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *out) +{ + if (!bmap || (bmap->magic != magic)) + return magic; + + if ((start < bmap->start) || (start+num-1 > bmap->real_end)) + return EXT2_ET_INVALID_ARGUMENT; + + memcpy(out, bmap->bitmap + (start >> 3), (num+7) >> 3); + return 0; +} + +errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *in) +{ + if (!bmap || (bmap->magic != magic)) + return magic; + + if ((start < bmap->start) || (start+num-1 > bmap->real_end)) + return EXT2_ET_INVALID_ARGUMENT; + + memcpy(bmap->bitmap + (start >> 3), in, (num+7) >> 3); + return 0; +} + +/* + * Compare @mem to zero buffer by 256 bytes. + * Return 1 if @mem is zeroed memory, otherwise return 0. + */ +int ext2fs_mem_is_zero(const char *mem, size_t len) +{ + static const char zero_buf[256]; + + while (len >= sizeof(zero_buf)) { + if (memcmp(mem, zero_buf, sizeof(zero_buf))) + return 0; + len -= sizeof(zero_buf); + mem += sizeof(zero_buf); + } + /* Deal with leftover bytes. */ + if (len) + return !memcmp(mem, zero_buf, len); + return 1; +} + +/* + * Return true if all of the bits in a specified range are clear + */ +static int ext2fs_test_clear_generic_bitmap_range(ext2fs_generic_bitmap bitmap, + unsigned int start, + unsigned int len) +{ + size_t start_byte, len_byte = len >> 3; + unsigned int start_bit, len_bit = len % 8; + int first_bit = 0; + int last_bit = 0; + int mark_count = 0; + int mark_bit = 0; + int i; + const char *ADDR = bitmap->bitmap; + + start -= bitmap->start; + start_byte = start >> 3; + start_bit = start % 8; + + if (start_bit != 0) { + /* + * The compared start block number or start inode number + * is not the first bit in a byte. + */ + mark_count = 8 - start_bit; + if (len < 8 - start_bit) { + mark_count = (int)len; + mark_bit = len + start_bit - 1; + } else + mark_bit = 7; + + for (i = mark_count; i > 0; i--, mark_bit--) + first_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the first byte. + * If there is any marked bit, this function returns 0. + */ + if (first_bit & ADDR[start_byte]) + return 0; + else if (len <= 8 - start_bit) + return 1; + + start_byte++; + len_bit = (len - mark_count) % 8; + len_byte = (len - mark_count) >> 3; + } + + /* + * The compared start block number or start inode number is + * the first bit in a byte. + */ + if (len_bit != 0) { + /* + * The compared end block number or end inode number is + * not the last bit in a byte. + */ + for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--) + last_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the last byte. + * If there is any marked bit, this function returns 0. + */ + if (last_bit & ADDR[start_byte + len_byte]) + return 0; + else if (len_byte == 0) + return 1; + } + + /* Check whether all bytes are 0 */ + return ext2fs_mem_is_zero(ADDR + start_byte, len_byte); +} + +int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); + if ((block < bitmap->start) || (block+num-1 > bitmap->real_end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, + block, bitmap->description); + return 0; + } + return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) + bitmap, block, num); +} + +int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, + ino_t inode, int num) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); + if ((inode < bitmap->start) || (inode+num-1 > bitmap->real_end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST, + inode, bitmap->description); + return 0; + } + return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) + bitmap, inode, num); +} + +void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_fast_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_fast_clear_bit(block + i - bitmap->start, + bitmap->bitmap); +} diff --git a/lib/libext2fs/source/gen_bitmap64.c b/lib/libext2fs/source/gen_bitmap64.c new file mode 100644 index 0000000..9e7b298 --- /dev/null +++ b/lib/libext2fs/source/gen_bitmap64.c @@ -0,0 +1,563 @@ +/* + * gen_bitmap64.c --- routines to read, write, and manipulate the new qinode and + * block bitmaps. + * + * Copyright (C) 2007, 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "bmap64.h" + +/* + * Design of 64-bit bitmaps + * + * In order maintain ABI compatibility with programs that don't + * understand about 64-bit blocks/inodes, + * ext2fs_allocate_inode_bitmap() and ext2fs_allocate_block_bitmap() + * will create old-style bitmaps unless the application passes the + * flag EXT2_FLAG_64BITS to ext2fs_open(). If this flag is + * passed, then we know the application has been recompiled, so we can + * use the new-style bitmaps. If it is not passed, we have to return + * an error if trying to open a filesystem which needs 64-bit bitmaps. + * + * The new bitmaps use a new set of structure magic numbers, so that + * both the old-style and new-style interfaces can identify which + * version of the data structure was used. Both the old-style and + * new-style interfaces will support either type of bitmap, although + * of course 64-bit operation will only be possible when both the + * new-style interface and the new-style bitmap are used. + * + * For example, the new bitmap interfaces will check the structure + * magic numbers and so will be able to detect old-stype bitmap. If + * they see an old-style bitmap, they will pass it to the gen_bitmap.c + * functions for handling. The same will be true for the old + * interfaces as well. + * + * The new-style interfaces will have several different back-end + * implementations, so we can support different encodings that are + * appropriate for different applications. In general the default + * should be whatever makes sense, and what the application/library + * will use. However, e2fsck may need specialized implementations for + * its own uses. For example, when doing parent directory pointer + * loop detections in pass 3, the bitmap will *always* be sparse, so + * e2fsck can request an encoding which is optimized for that. + */ + +static void warn_bitmap(ext2fs_generic_bitmap bitmap, + int code, __u64 arg) +{ +#ifndef OMIT_COM_ERR + if (bitmap->description) + com_err(0, bitmap->base_error_code+code, + "#%llu for %s", arg, bitmap->description); + else + com_err(0, bitmap->base_error_code + code, "#%llu", arg); +#endif +} + + +errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char *descr, + ext2fs_generic_bitmap *ret) +{ + ext2fs_generic_bitmap bitmap; + struct ext2_bitmap_ops *ops; + errcode_t retval; + + switch (type) { + case EXT2FS_BMAP64_BITARRAY: + ops = &ext2fs_blkmap64_bitarray; + break; + default: + return EINVAL; + } + + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &bitmap); + if (retval) + return retval; + + /* XXX factor out, repeated in copy_bmap */ + bitmap->magic = magic; + bitmap->fs = fs; + bitmap->start = start; + bitmap->end = end; + bitmap->real_end = real_end; + bitmap->bitmap_ops = ops; + switch (magic) { + case EXT2_ET_MAGIC_INODE_BITMAP64: + bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; + break; + case EXT2_ET_MAGIC_BLOCK_BITMAP64: + bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; + break; + default: + bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; + } + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); + if (retval) { + ext2fs_free_mem(&bitmap); + return retval; + } + strcpy(bitmap->description, descr); + } else + bitmap->description = 0; + + retval = bitmap->bitmap_ops->new_bmap(fs, bitmap); + if (retval) { + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap); + return retval; + } + + *ret = bitmap; + return 0; +} + +void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + ext2fs_free_generic_bitmap(bmap); + return; + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + bmap->bitmap_ops->free_bmap(bmap); + + if (bmap->description) { + ext2fs_free_mem(&bmap->description); + bmap->description = 0; + } + bmap->magic = 0; + ext2fs_free_mem(&bmap); +} + +errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + char *descr, *new_descr; + ext2fs_generic_bitmap new_bmap; + errcode_t retval; + + if (!src) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(src)) + return ext2fs_copy_generic_bitmap(src, dest); + + if (!EXT2FS_IS_64_BITMAP(src)) + return EINVAL; + + /* Allocate a new bitmap struct */ + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &new_bmap); + if (retval) + return retval; + + /* Copy all the high-level parts over */ + new_bmap->magic = src->magic; + new_bmap->fs = src->fs; + new_bmap->start = src->start; + new_bmap->end = src->end; + new_bmap->real_end = src->real_end; + new_bmap->bitmap_ops = src->bitmap_ops; + new_bmap->base_error_code = src->base_error_code; + + descr = src->description; + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &new_descr); + if (retval) { + ext2fs_free_mem(&new_bmap); + return retval; + } + strcpy(new_descr, descr); + new_bmap->description = new_descr; + } + + retval = src->bitmap_ops->copy_bmap(src, new_bmap); + if (retval) { + ext2fs_free_mem(&new_bmap->description); + ext2fs_free_mem(&new_bmap); + return retval; + } + + *dest = new_bmap; + + return 0; +} + +errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) + return ext2fs_resize_generic_bitmap(bmap->magic, new_end, + new_real_end, bmap); + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->resize_bmap(bmap, new_end, new_real_end); +} + +errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + ext2_ino_t tmp_oend; + int retval; + + retval = ext2fs_fudge_generic_bitmap_end(bitmap, bitmap->magic, + neq, end, &tmp_oend); + if (oend) + *oend = tmp_oend; + return retval; + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + if (end > bitmap->real_end) + return neq; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +__u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) + return ext2fs_get_generic_bitmap_start(bitmap); + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + return bitmap->start; +} + +__u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) + return ext2fs_get_generic_bitmap_end(bitmap); + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + return bitmap->end; +} + +void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap) +{ + if (EXT2FS_IS_32_BITMAP(bitmap)) + ext2fs_clear_generic_bitmap(bitmap); + + bitmap->bitmap_ops->clear_bmap (bitmap); +} + +int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, + EXT2FS_MARK_ERROR, 0xffffffff); + return 0; + } + return ext2fs_mark_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_MARK_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->mark_bmap(bitmap, arg); +} + +int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, + 0xffffffff); + return 0; + } + return ext2fs_unmark_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_UNMARK_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->unmark_bmap(bitmap, arg); +} + +int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, + 0xffffffff); + return 0; + } + return ext2fs_test_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_TEST_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->test_bmap(bitmap, arg); +} + +errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *in) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((start+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bmap, EXT2FS_UNMARK_ERROR, + 0xffffffff); + return EINVAL; + } + return ext2fs_set_generic_bitmap_range(bmap, bmap->magic, + start, num, in); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->set_bmap_range(bmap, start, num, in); +} + +errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *out) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((start+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return EINVAL; + } + return ext2fs_get_generic_bitmap_range(bmap, bmap->magic, + start, num, out); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->get_bmap_range(bmap, start, num, out); +} + +errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2) +{ + blk64_t i; + + if (!bm1 || !bm2) + return EINVAL; + if (bm1->magic != bm2->magic) + return EINVAL; + + /* Now we know both bitmaps have the same magic */ + if (EXT2FS_IS_32_BITMAP(bm1)) + return ext2fs_compare_generic_bitmap(bm1->magic, neq, bm1, bm2); + + if (!EXT2FS_IS_64_BITMAP(bm1)) + return EINVAL; + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end)) + return neq; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_test_generic_bmap(bm1, i) != + ext2fs_test_generic_bmap(bm2, i)) + return neq; + + return 0; +} + +void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap) +{ + __u64 start, num; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + ext2fs_set_generic_bitmap_padding(bmap); + return; + } + + start = bmap->end + 1; + num = bmap->real_end - bmap->end; + bmap->bitmap_ops->mark_bmap_extent(bmap, start, num); + /* XXX ought to warn on error */ +} + +int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return EINVAL; + + if (num == 1) + return !ext2fs_test_generic_bmap((ext2fs_generic_bitmap) + bmap, block); + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return EINVAL; + } + return ext2fs_test_block_bitmap_range( + (ext2fs_generic_bitmap) bmap, block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num); +} + +void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return; + } + ext2fs_mark_block_bitmap_range((ext2fs_generic_bitmap) bmap, + block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + if ((block < bmap->start) || (block+num-1 > bmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bmap->description); + return; + } + + bmap->bitmap_ops->mark_bmap_extent(bmap, block, num); +} + +void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return; + } + ext2fs_unmark_block_bitmap_range((ext2fs_generic_bitmap) bmap, + block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + if ((block < bmap->start) || (block+num-1 > bmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bmap->description); + return; + } + + bmap->bitmap_ops->unmark_bmap_extent(bmap, block, num); +} + +int ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func) +{ +#ifndef OMIT_COM_ERR + if (bitmap && bitmap->description) + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "called %s with 64-bit bitmap for %s", func, + bitmap->description); + else + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "called %s with 64-bit bitmap", func); +#endif + return 0; +} diff --git a/lib/libext2fs/source/get_pathname.c b/lib/libext2fs/source/get_pathname.c new file mode 100644 index 0000000..7ac14db --- /dev/null +++ b/lib/libext2fs/source/get_pathname.c @@ -0,0 +1,160 @@ +/* + * get_pathname.c --- do directry/inode -> name translation + * + * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * + * ext2fs_get_pathname(fs, dir, ino, name) + * + * This function translates takes two inode numbers into a + * string, placing the result in . is the containing + * directory inode, and is the inode number itself. If + * is zero, then ext2fs_get_pathname will return pathname + * of the the directory . + * + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct get_pathname_struct { + ext2_ino_t search_ino; + ext2_ino_t parent; + char *name; + errcode_t errcode; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int get_pathname_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct get_pathname_struct *gp; + errcode_t retval; + + gp = (struct get_pathname_struct *) priv_data; + + if (((dirent->name_len & 0xFF) == 2) && + !strncmp(dirent->name, "..", 2)) + gp->parent = dirent->inode; + if (dirent->inode == gp->search_ino) { + retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, + &gp->name); + if (retval) { + gp->errcode = retval; + return DIRENT_ABORT; + } + strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); + gp->name[dirent->name_len & 0xFF] = '\0'; + return DIRENT_ABORT; + } + return 0; +} + +static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, + ext2_ino_t ino, int maxdepth, + char *buf, char **name) +{ + struct get_pathname_struct gp; + char *parent_name, *ret; + errcode_t retval; + + if (dir == ino) { + retval = ext2fs_get_mem(2, name); + if (retval) + return retval; + strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); + return 0; + } + + if (!dir || (maxdepth < 0)) { + retval = ext2fs_get_mem(4, name); + if (retval) + return retval; + strcpy(*name, "..."); + return 0; + } + + gp.search_ino = ino; + gp.parent = 0; + gp.name = 0; + gp.errcode = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); + if (retval) + goto cleanup; + if (gp.errcode) { + retval = gp.errcode; + goto cleanup; + } + + retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, + buf, &parent_name); + if (retval) + goto cleanup; + if (!ino) { + *name = parent_name; + return 0; + } + + if (gp.name) + retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, + &ret); + else + retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); + if (retval) + goto cleanup; + + ret[0] = 0; + if (parent_name[1]) + strcat(ret, parent_name); + strcat(ret, "/"); + if (gp.name) + strcat(ret, gp.name); + else + strcat(ret, "???"); + *name = ret; + ext2fs_free_mem(&parent_name); + retval = 0; + +cleanup: + if (gp.name) + ext2fs_free_mem(&gp.name); + return retval; +} + +errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + if (dir == ino) + ino = 0; + retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); + ext2fs_free_mem(&buf); + return retval; + +} diff --git a/lib/libext2fs/source/getsectsize.c b/lib/libext2fs/source/getsectsize.c new file mode 100644 index 0000000..64f42a6 --- /dev/null +++ b/lib/libext2fs/source/getsectsize.c @@ -0,0 +1,91 @@ +/* + * getsectsize.c --- get the sector size of a device. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#include +#endif + +#if defined(__linux__) && defined(_IO) +#if !defined(BLKSSZGET) +#define BLKSSZGET _IO(0x12,104)/* get block device sector size */ +#endif +#if !defined(BLKPBSZGET) +#define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ +#endif +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Returns the logical sector size of a device + */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) +{ + int fd; + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef BLKSSZGET + if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { + close(fd); + return 0; + } +#endif + *sectsize = 0; + close(fd); + return 0; +} + +/* + * Returns the physical sector size of a device + */ +errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize) +{ + int fd; + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, sectsize) >= 0) { + close(fd); + return 0; + } +#endif + *sectsize = 0; + close(fd); + return 0; +} diff --git a/lib/libext2fs/source/getsize.c b/lib/libext2fs/source/getsize.c new file mode 100644 index 0000000..56ec4b6 --- /dev/null +++ b/lib/libext2fs/source/getsize.c @@ -0,0 +1,311 @@ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * Windows version of ext2fs_get_device_size by Chris Li, VMware. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#include +#endif +#ifdef HAVE_SYS_DISK_H +#ifdef HAVE_SYS_QUEUE_H +#include /* for LIST_HEAD */ +#endif +#include +#endif +#ifdef __linux__ +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#include + +#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +#endif + +#ifdef APPLE_DARWIN +#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif /* APPLE_DARWIN */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__CYGWIN__) || defined (WIN32) +#include "windows.h" +#include "winioctl.h" + +#if (_WIN32_WINNT >= 0x0500) +#define HAVE_GET_FILE_SIZE_EX 1 +#endif + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + HANDLE dev; + PARTITION_INFORMATION pi; + DISK_GEOMETRY gi; + DWORD retbytes; +#ifdef HAVE_GET_FILE_SIZE_EX + LARGE_INTEGER filesize; +#else + DWORD filesize; +#endif /* HAVE_GET_FILE_SIZE_EX */ + + dev = CreateFile(file, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE , + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (dev == INVALID_HANDLE_VALUE) + return EBADF; + if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, + &pi, sizeof(PARTITION_INFORMATION), + &pi, sizeof(PARTITION_INFORMATION), + &retbytes, NULL)) { + + *retblocks = pi.PartitionLength.QuadPart / blocksize; + + } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, + &gi, sizeof(DISK_GEOMETRY), + &gi, sizeof(DISK_GEOMETRY), + &retbytes, NULL)) { + + *retblocks = gi.BytesPerSector * + gi.SectorsPerTrack * + gi.TracksPerCylinder * + gi.Cylinders.QuadPart / blocksize; + +#ifdef HAVE_GET_FILE_SIZE_EX + } else if (GetFileSizeEx(dev, &filesize)) { + *retblocks = filesize.QuadPart / blocksize; + } +#else + } else { + filesize = GetFileSize(dev, NULL); + if (INVALID_FILE_SIZE != filesize) { + *retblocks = filesize / blocksize; + } + } +#endif /* HAVE_GET_FILE_SIZE_EX */ + + CloseHandle(dev); + return 0; +} + +#else + +static int valid_offset (int fd, ext2_loff_t offset) +{ + char ch; + + if (ext2fs_llseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +/* + * Returns the number of blocks in a partition + */ +errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks) +{ + int fd, rc = 0; +#ifdef __linux__ + struct utsname ut; +#endif + unsigned long long size64; + ext2_loff_t high, low; +#ifdef FDGETPRM + struct floppy_struct this_floppy; +#endif +#ifdef HAVE_SYS_DISKLABEL_H + int part; + struct disklabel lab; + struct partition *pp; + char ch; +#endif /* HAVE_SYS_DISKLABEL_H */ + +#ifdef HAVE_OPEN64 + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { + *retblocks = size64 / (blocksize / 512); + goto out; + } +#endif + +#ifdef BLKGETSIZE64 +#ifdef __linux__ + if ((uname(&ut) == 0) && + ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.'))) + valid_blkgetsize64 = 0; +#endif + if (valid_blkgetsize64 && + ioctl(fd, BLKGETSIZE64, &size64) >= 0) { + *retblocks = size64 / blocksize; + goto out; + } +#endif + +#ifdef BLKGETSIZE + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + *retblocks = size / (blocksize / 512); + goto out; + } +#endif + +#ifdef FDGETPRM + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + *retblocks = this_floppy.size / (blocksize / 512); + goto out; + } +#endif + +#ifdef HAVE_SYS_DISKLABEL_H +#if defined(DIOCGMEDIASIZE) + { + off_t ms; + u_int bs; + if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { + *retblocks = ms / blocksize; + goto out; + } + } +#elif defined(DIOCGDINFO) + /* old disklabel interface */ + part = strlen(file) - 1; + if (part >= 0) { + ch = file[part]; + if (isdigit(ch)) + part = 0; + else if (ch >= 'a' && ch <= 'h') + part = ch - 'a'; + else + part = -1; + } + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) { + *retblocks = pp->p_size / (blocksize / 512); + goto out; + } + } +#endif /* defined(DIOCG*) */ +#endif /* HAVE_SYS_DISKLABEL_H */ + + { +#ifdef HAVE_FSTAT64 + struct stat64 st; + if (fstat64(fd, &st) == 0) +#else + struct stat st; + if (fstat(fd, &st) == 0) +#endif + if (S_ISREG(st.st_mode)) { + *retblocks = st.st_size / blocksize; + goto out; + } + } + + /* + * OK, we couldn't figure it out by using a specialized ioctl, + * which is generally the best way. So do binary search to + * find the size of the partition. + */ + low = 0; + for (high = 1024; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const ext2_loff_t mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + size64 = low + 1; + *retblocks = size64 / blocksize; +out: + close(fd); + return rc; +} + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + errcode_t retval; + blk64_t blocks; + + retval = ext2fs_get_device_size2(file, blocksize, &blocks); + if (retval) + return retval; + if (blocks >= (1ULL << 32)) + return EFBIG; + *retblocks = (blk_t) blocks; + return 0; +} + +#endif /* WIN32 */ + +#ifdef DEBUG +int main(int argc, char **argv) +{ + blk_t blocks; + int retval; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + retval = ext2fs_get_device_size(argv[1], 1024, &blocks); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_get_device_size"); + exit(1); + } + printf("Device %s has %u 1k blocks.\n", argv[1], blocks); + exit(0); +} +#endif diff --git a/lib/libext2fs/source/i_block.c b/lib/libext2fs/source/i_block.c new file mode 100644 index 0000000..39d93ee --- /dev/null +++ b/lib/libext2fs/source/i_block.c @@ -0,0 +1,89 @@ +/* + * i_block.c --- Manage the i_block field for i_blocks + * + * Copyright (C) 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks) +{ + unsigned long long b = inode->i_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; + + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + b += num_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + else if (b > 0xFFFFFFFF) + return EOVERFLOW; + inode->i_blocks = b & 0xFFFFFFFF; + return 0; +} + +errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks) +{ + unsigned long long b = inode->i_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; + + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks *= fs->blocksize / 512; + + if (num_blocks > b) + return EOVERFLOW; + + b -= num_blocks; + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + inode->i_blocks = b & 0xFFFFFFFF; + return 0; +} + +errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b) +{ + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + b *= fs->blocksize / 512; + + inode->i_blocks = b & 0xFFFFFFFF; + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + inode->osd2.linux2.l_i_blocks_hi = b >> 32; + else if (b >> 32) + return EOVERFLOW; + return 0; +} diff --git a/lib/libext2fs/source/icount.c b/lib/libext2fs/source/icount.c new file mode 100644 index 0000000..43cc53e --- /dev/null +++ b/lib/libext2fs/source/icount.c @@ -0,0 +1,862 @@ +/* + * icount.c --- an efficient inode count abstraction + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "tdb.h" + +/* + * The data storage strategy used by icount relies on the observation + * that most inode counts are either zero (for non-allocated inodes), + * one (for most files), and only a few that are two or more + * (directories and files that are linked to more than one directory). + * + * Also, e2fsck tends to load the icount data sequentially. + * + * So, we use an inode bitmap to indicate which inodes have a count of + * one, and then use a sorted list to store the counts for inodes + * which are greater than one. + * + * We also use an optional bitmap to indicate which inodes are already + * in the sorted list, to speed up the use of this abstraction by + * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, + * so this extra bitmap avoids searching the sorted list to see if a + * particular inode is on the sorted list already. + */ + +struct ext2_icount_el { + ext2_ino_t ino; + __u32 count; +}; + +struct ext2_icount { + errcode_t magic; + ext2fs_inode_bitmap single; + ext2fs_inode_bitmap multiple; + ext2_ino_t count; + ext2_ino_t size; + ext2_ino_t num_inodes; + ext2_ino_t cursor; + struct ext2_icount_el *list; + struct ext2_icount_el *last_lookup; + char *tdb_fn; + TDB_CONTEXT *tdb; +}; + +/* + * We now use a 32-bit counter field because it doesn't cost us + * anything extra for the in-memory data structure, due to alignment + * padding. But there's no point changing the interface if most of + * the time we only care if the number is bigger than 65,000 or not. + * So use the following translation function to return a 16-bit count. + */ +#define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x)) + +void ext2fs_free_icount(ext2_icount_t icount) +{ + if (!icount) + return; + + icount->magic = 0; + if (icount->list) + ext2fs_free_mem(&icount->list); + if (icount->single) + ext2fs_free_inode_bitmap(icount->single); + if (icount->multiple) + ext2fs_free_inode_bitmap(icount->multiple); + if (icount->tdb) + tdb_close(icount->tdb); + if (icount->tdb_fn) { + unlink(icount->tdb_fn); + free(icount->tdb_fn); + } + + ext2fs_free_mem(&icount); +} + +static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + + *ret = 0; + + retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); + if (retval) + return retval; + memset(icount, 0, sizeof(struct ext2_icount)); + + retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single); + if (retval) + goto errout; + + if (flags & EXT2_ICOUNT_OPT_INCREMENT) { + retval = ext2fs_allocate_inode_bitmap(fs, 0, + &icount->multiple); + if (retval) + goto errout; + } else + icount->multiple = 0; + + icount->magic = EXT2_ET_MAGIC_ICOUNT; + icount->num_inodes = fs->super->s_inodes_count; + + *ret = icount; + return 0; + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +struct uuid { + __u32 time_low; + __u16 time_mid; + __u16 time_hi_and_version; + __u16 clock_seq; + __u8 node[6]; +}; + +static void unpack_uuid(void *in, struct uuid *uu) +{ + __u8 *ptr = in; + __u32 tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +static void uuid_unparse(void *uu, char *out) +{ + struct uuid uuid; + + unpack_uuid(uu, &uuid); + sprintf(out, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, + int flags, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + char *fn, uuid[40]; + int fd; + + retval = alloc_icount(fs, flags, &icount); + if (retval) + return retval; + + retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn); + if (retval) + goto errout; + uuid_unparse(fs->super->s_uuid, uuid); + sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid); + fd = mkstemp(fn); + + icount->tdb_fn = fn; + icount->tdb = tdb_open(fn, 0, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (icount->tdb) { + close(fd); + *ret = icount; + return 0; + } + + retval = errno; + close(fd); + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + size_t bytes; + ext2_ino_t i; + + if (hint) { + EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); + if (hint->size > size) + size = (size_t) hint->size; + } + + retval = alloc_icount(fs, flags, &icount); + if (retval) + return retval; + + if (size) { + icount->size = size; + } else { + /* + * Figure out how many special case inode counts we will + * have. We know we will need one for each directory; + * we also need to reserve some extra room for file links + */ + retval = ext2fs_get_num_dirs(fs, &icount->size); + if (retval) + goto errout; + icount->size += fs->super->s_inodes_count / 50; + } + + bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); +#if 0 + printf("Icount allocated %u entries, %d bytes.\n", + icount->size, bytes); +#endif + retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el), + &icount->list); + if (retval) + goto errout; + memset(icount->list, 0, bytes); + + icount->count = 0; + icount->cursor = 0; + + /* + * Populate the sorted list with those entries which were + * found in the hint icount (since those are ones which will + * likely need to be in the sorted list this time around). + */ + if (hint) { + for (i=0; i < hint->count; i++) + icount->list[i].ino = hint->list[i].ino; + icount->count = hint->count; + } + + *ret = icount; + return 0; + +errout: + ext2fs_free_icount(icount); + return(retval); +} + +errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret) +{ + return ext2fs_create_icount2(fs, flags, size, 0, ret); +} + +/* + * insert_icount_el() --- Insert a new entry into the sorted list at a + * specified position. + */ +static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int pos) +{ + struct ext2_icount_el *el; + errcode_t retval; + ext2_ino_t new_size = 0; + int num; + + if (icount->last_lookup && icount->last_lookup->ino == ino) + return icount->last_lookup; + + if (icount->count >= icount->size) { + if (icount->count) { + new_size = icount->list[(unsigned)icount->count-1].ino; + new_size = (ext2_ino_t) (icount->count * + ((float) icount->num_inodes / new_size)); + } + if (new_size < (icount->size + 100)) + new_size = icount->size + 100; +#if 0 + printf("Reallocating icount %u entries...\n", new_size); +#endif + retval = ext2fs_resize_mem((size_t) icount->size * + sizeof(struct ext2_icount_el), + (size_t) new_size * + sizeof(struct ext2_icount_el), + &icount->list); + if (retval) + return 0; + icount->size = new_size; + } + num = (int) icount->count - pos; + if (num < 0) + return 0; /* should never happen */ + if (num) { + memmove(&icount->list[pos+1], &icount->list[pos], + sizeof(struct ext2_icount_el) * num); + } + icount->count++; + el = &icount->list[pos]; + el->count = 0; + el->ino = ino; + icount->last_lookup = el; + return el; +} + +/* + * get_icount_el() --- given an inode number, try to find icount + * information in the sorted list. If the create flag is set, + * and we can't find an entry, create one in the sorted list. + */ +static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int create) +{ + float range; + int low, high, mid; + ext2_ino_t lowval, highval; + + if (!icount || !icount->list) + return 0; + + if (create && ((icount->count == 0) || + (ino > icount->list[(unsigned)icount->count-1].ino))) { + return insert_icount_el(icount, ino, (unsigned) icount->count); + } + if (icount->count == 0) + return 0; + + if (icount->cursor >= icount->count) + icount->cursor = 0; + if (ino == icount->list[icount->cursor].ino) + return &icount->list[icount->cursor++]; +#if 0 + printf("Non-cursor get_icount_el: %u\n", ino); +#endif + low = 0; + high = (int) icount->count-1; + while (low <= high) { +#if 0 + mid = (low+high)/2; +#else + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = icount->list[low].ino; + highval = icount->list[high].ino; + + if (ino < lowval) + range = 0; + else if (ino > highval) + range = 1; + else { + range = ((float) (ino - lowval)) / + (highval - lowval); + if (range > 0.9) + range = 0.9; + if (range < 0.1) + range = 0.1; + } + mid = low + ((int) (range * (high-low))); + } +#endif + if (ino == icount->list[mid].ino) { + icount->cursor = mid+1; + return &icount->list[mid]; + } + if (ino < icount->list[mid].ino) + high = mid-1; + else + low = mid+1; + } + /* + * If we need to create a new entry, it should be right at + * low (where high will be left at low-1). + */ + if (create) + return insert_icount_el(icount, ino, low); + return 0; +} + +static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino, + __u32 count) +{ + struct ext2_icount_el *el; + TDB_DATA key, data; + + if (icount->tdb) { + key.dptr = (unsigned char *) &ino; + key.dsize = sizeof(ext2_ino_t); + data.dptr = (unsigned char *) &count; + data.dsize = sizeof(__u32); + if (count) { + if (tdb_store(icount->tdb, key, data, TDB_REPLACE)) + return tdb_error(icount->tdb) + + EXT2_ET_TDB_SUCCESS; + } else { + if (tdb_delete(icount->tdb, key)) + return tdb_error(icount->tdb) + + EXT2_ET_TDB_SUCCESS; + } + return 0; + } + + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + + el->count = count; + return 0; +} + +static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino, + __u32 *count) +{ + struct ext2_icount_el *el; + TDB_DATA key, data; + + if (icount->tdb) { + key.dptr = (unsigned char *) &ino; + key.dsize = sizeof(ext2_ino_t); + + data = tdb_fetch(icount->tdb, key); + if (data.dptr == NULL) { + *count = 0; + return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; + } + + *count = *((__u32 *) data.dptr); + free(data.dptr); + return 0; + } + el = get_icount_el(icount, ino, 0); + if (!el) { + *count = 0; + return ENOENT; + } + + *count = el->count; + return 0; +} + +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) +{ + errcode_t ret = 0; + unsigned int i; + const char *bad = "bad icount"; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (icount->count > icount->size) { + fprintf(out, "%s: count > size\n", bad); + return EXT2_ET_INVALID_ARGUMENT; + } + for (i=1; i < icount->count; i++) { + if (icount->list[i-1].ino >= icount->list[i].ino) { + fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", + bad, i-1, icount->list[i-1].ino, + i, icount->list[i].ino); + ret = EXT2_ET_INVALID_ARGUMENT; + } + } + return ret; +} + +errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) +{ + __u32 val; + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + *ret = 1; + return 0; + } + if (icount->multiple && + !ext2fs_test_inode_bitmap2(icount->multiple, ino)) { + *ret = 0; + return 0; + } + get_inode_count(icount, ino, &val); + *ret = icount_16_xlate(val); + return 0; +} + +errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + __u32 curr_value; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + /* + * If the existing count is 1, then we know there is + * no entry in the list. + */ + if (set_inode_count(icount, ino, 2)) + return EXT2_ET_NO_MEMORY; + curr_value = 2; + ext2fs_unmark_inode_bitmap2(icount->single, ino); + } else if (icount->multiple) { + /* + * The count is either zero or greater than 1; if the + * inode is set in icount->multiple, then there should + * be an entry in the list, so we need to fix it. + */ + if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) { + get_inode_count(icount, ino, &curr_value); + curr_value++; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + } else { + /* + * The count was zero; mark the single bitmap + * and return. + */ + ext2fs_mark_inode_bitmap2(icount->single, ino); + if (ret) + *ret = 1; + return 0; + } + } else { + /* + * The count is either zero or greater than 1; try to + * find an entry in the list to determine which. + */ + get_inode_count(icount, ino, &curr_value); + curr_value++; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + } + if (icount->multiple) + ext2fs_mark_inode_bitmap2(icount->multiple, ino); + if (ret) + *ret = icount_16_xlate(curr_value); + return 0; +} + +errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + __u32 curr_value; + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) { + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + else { + set_inode_count(icount, ino, 0); + } + if (ret) + *ret = 0; + return 0; + } + + if (icount->multiple && + !ext2fs_test_inode_bitmap2(icount->multiple, ino)) + return EXT2_ET_INVALID_ARGUMENT; + + get_inode_count(icount, ino, &curr_value); + if (!curr_value) + return EXT2_ET_INVALID_ARGUMENT; + curr_value--; + if (set_inode_count(icount, ino, curr_value)) + return EXT2_ET_NO_MEMORY; + + if (curr_value == 1) + ext2fs_mark_inode_bitmap2(icount->single, ino); + if ((curr_value == 0) && icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + + if (ret) + *ret = icount_16_xlate(curr_value); + return 0; +} + +errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count) +{ + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (count == 1) { + ext2fs_mark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + return 0; + } + if (count == 0) { + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) { + /* + * If the icount->multiple bitmap is enabled, + * we can just clear both bitmaps and we're done + */ + ext2fs_unmark_inode_bitmap2(icount->multiple, ino); + } else + set_inode_count(icount, ino, 0); + return 0; + } + + if (set_inode_count(icount, ino, count)) + return EXT2_ET_NO_MEMORY; + ext2fs_unmark_inode_bitmap2(icount->single, ino); + if (icount->multiple) + ext2fs_mark_inode_bitmap2(icount->multiple, ino); + return 0; +} + +ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) +{ + if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) + return 0; + + return icount->size; +} + +#ifdef DEBUG + +ext2_filsys test_fs; +ext2_icount_t icount; + +#define EXIT 0x00 +#define FETCH 0x01 +#define STORE 0x02 +#define INCREMENT 0x03 +#define DECREMENT 0x04 + +struct test_program { + int cmd; + ext2_ino_t ino; + __u16 arg; + __u16 expected; +}; + +struct test_program prog[] = { + { STORE, 42, 42, 42 }, + { STORE, 1, 1, 1 }, + { STORE, 2, 2, 2 }, + { STORE, 3, 3, 3 }, + { STORE, 10, 1, 1 }, + { STORE, 42, 0, 0 }, + { INCREMENT, 5, 0, 1 }, + { INCREMENT, 5, 0, 2 }, + { INCREMENT, 5, 0, 3 }, + { INCREMENT, 5, 0, 4 }, + { DECREMENT, 5, 0, 3 }, + { DECREMENT, 5, 0, 2 }, + { DECREMENT, 5, 0, 1 }, + { DECREMENT, 5, 0, 0 }, + { FETCH, 10, 0, 1 }, + { FETCH, 1, 0, 1 }, + { FETCH, 2, 0, 2 }, + { FETCH, 3, 0, 3 }, + { INCREMENT, 1, 0, 2 }, + { DECREMENT, 2, 0, 1 }, + { DECREMENT, 2, 0, 0 }, + { FETCH, 12, 0, 0 }, + { EXIT, 0, 0, 0 } +}; + +struct test_program extended[] = { + { STORE, 1, 1, 1 }, + { STORE, 2, 2, 2 }, + { STORE, 3, 3, 3 }, + { STORE, 4, 4, 4 }, + { STORE, 5, 5, 5 }, + { STORE, 6, 1, 1 }, + { STORE, 7, 2, 2 }, + { STORE, 8, 3, 3 }, + { STORE, 9, 4, 4 }, + { STORE, 10, 5, 5 }, + { STORE, 11, 1, 1 }, + { STORE, 12, 2, 2 }, + { STORE, 13, 3, 3 }, + { STORE, 14, 4, 4 }, + { STORE, 15, 5, 5 }, + { STORE, 16, 1, 1 }, + { STORE, 17, 2, 2 }, + { STORE, 18, 3, 3 }, + { STORE, 19, 4, 4 }, + { STORE, 20, 5, 5 }, + { STORE, 21, 1, 1 }, + { STORE, 22, 2, 2 }, + { STORE, 23, 3, 3 }, + { STORE, 24, 4, 4 }, + { STORE, 25, 5, 5 }, + { STORE, 26, 1, 1 }, + { STORE, 27, 2, 2 }, + { STORE, 28, 3, 3 }, + { STORE, 29, 4, 4 }, + { STORE, 30, 5, 5 }, + { EXIT, 0, 0, 0 } +}; + +/* + * Setup the variables for doing the inode scan test. + */ +static void setup(void) +{ + errcode_t retval; + struct ext2_super_block param; + + initialize_ext2_error_table(); + + memset(¶m, 0, sizeof(param)); + ext2fs_blocks_count_set(¶m, 12000); + + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + test_io_manager, &test_fs); + if (retval) { + com_err("setup", retval, + "while initializing filesystem"); + exit(1); + } + retval = ext2fs_allocate_tables(test_fs); + if (retval) { + com_err("setup", retval, + "while allocating tables for test filesystem"); + exit(1); + } +} + +int run_test(int flags, int size, char *dir, struct test_program *prog) +{ + errcode_t retval; + ext2_icount_t icount; + struct test_program *pc; + __u16 result; + int problem = 0; + + if (dir) { + retval = ext2fs_create_icount_tdb(test_fs, dir, + flags, &icount); + if (retval) { + com_err("run_test", retval, + "while creating icount using tdb"); + exit(1); + } + } else { + retval = ext2fs_create_icount2(test_fs, flags, size, 0, + &icount); + if (retval) { + com_err("run_test", retval, "while creating icount"); + exit(1); + } + } + for (pc = prog; pc->cmd != EXIT; pc++) { + switch (pc->cmd) { + case FETCH: + printf("icount_fetch(%u) = ", pc->ino); + break; + case STORE: + retval = ext2fs_icount_store(icount, pc->ino, pc->arg); + if (retval) { + com_err("run_test", retval, + "while calling icount_store"); + exit(1); + } + printf("icount_store(%u, %u) = ", pc->ino, pc->arg); + break; + case INCREMENT: + retval = ext2fs_icount_increment(icount, pc->ino, 0); + if (retval) { + com_err("run_test", retval, + "while calling icount_increment"); + exit(1); + } + printf("icount_increment(%u) = ", pc->ino); + break; + case DECREMENT: + retval = ext2fs_icount_decrement(icount, pc->ino, 0); + if (retval) { + com_err("run_test", retval, + "while calling icount_decrement"); + exit(1); + } + printf("icount_decrement(%u) = ", pc->ino); + break; + } + retval = ext2fs_icount_fetch(icount, pc->ino, &result); + if (retval) { + com_err("run_test", retval, + "while calling icount_fetch"); + exit(1); + } + printf("%u (%s)\n", result, (result == pc->expected) ? + "OK" : "NOT OK"); + if (result != pc->expected) + problem++; + } + printf("icount size is %u\n", ext2fs_get_icount_size(icount)); + retval = ext2fs_icount_validate(icount, stdout); + if (retval) { + com_err("run_test", retval, "while calling icount_validate"); + exit(1); + } + ext2fs_free_icount(icount); + return problem; +} + + +int main(int argc, char **argv) +{ + int failed = 0; + + setup(); + printf("Standard icount run:\n"); + failed += run_test(0, 0, 0, prog); + printf("\nMultiple bitmap test:\n"); + failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog); + printf("\nResizing icount:\n"); + failed += run_test(0, 3, 0, extended); + printf("\nStandard icount run with tdb:\n"); + failed += run_test(0, 0, ".", prog); + printf("\nMultiple bitmap test with tdb:\n"); + failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog); + if (failed) + printf("FAILED!\n"); + return failed; +} +#endif diff --git a/lib/libext2fs/source/imager.c b/lib/libext2fs/source/imager.c new file mode 100644 index 0000000..5a6d0b9 --- /dev/null +++ b/lib/libext2fs/source/imager.c @@ -0,0 +1,408 @@ +/* + * image.c --- writes out the critical parts of the filesystem as a + * flat file. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef HAVE_TYPE_SSIZE_T +typedef int ssize_t; +#endif + +/* + * This function returns 1 if the specified block is all zeros + */ +static int check_zero_block(char *buf, int blocksize) +{ + char *cp = buf; + int left = blocksize; + + while (left > 0) { + if (*cp++) + return 0; + left--; + } + return 1; +} + +/* + * Write the inode table out as a single block. + */ +#define BUF_BLOCKS 32 + +errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) +{ + unsigned int group, left, c, d; + char *buf, *cp; + blk64_t blk; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize * BUF_BLOCKS); + if (!buf) + return ENOMEM; + + for (group = 0; group < fs->group_desc_count; group++) { + blk = ext2fs_inode_table_loc(fs, (unsigned)group); + if (!blk) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + retval = io_channel_read_blk64(fs->io, blk, c, buf); + if (retval) + goto errout; + cp = buf; + while (c) { + if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { + d = c; + goto skip_sparse; + } + /* Skip zero blocks */ + if (check_zero_block(cp, fs->blocksize)) { + c--; + blk++; + left--; + cp += fs->blocksize; + lseek(fd, fs->blocksize, SEEK_CUR); + continue; + } + /* Find non-zero blocks */ + for (d=1; d < c; d++) { + if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) + break; + } + skip_sparse: + actual = write(fd, cp, fs->blocksize * d); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * d)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + blk += d; + left -= d; + cp += fs->blocksize * d; + c -= d; + } + } + } + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read in the inode table and stuff it into place + */ +errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + unsigned int group, c, left; + char *buf; + blk64_t blk; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize * BUF_BLOCKS); + if (!buf) + return ENOMEM; + + for (group = 0; group < fs->group_desc_count; group++) { + blk = ext2fs_inode_table_loc(fs, (unsigned)group); + if (!blk) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + actual = read(fd, buf, fs->blocksize * c); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * c)) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + retval = io_channel_write_blk64(fs->io, blk, c, buf); + if (retval) + goto errout; + + blk += c; + left -= c; + } + } + retval = ext2fs_flush_icache(fs); + +errout: + free(buf); + return retval; +} + +/* + * Write out superblock and group descriptors + */ +errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf, *cp; + ssize_t actual; + errcode_t retval; + + buf = malloc(fs->blocksize); + if (!buf) + return ENOMEM; + + /* + * Write out the superblock + */ + memset(buf, 0, fs->blocksize); + memcpy(buf, fs->super, SUPERBLOCK_SIZE); + actual = write(fd, buf, fs->blocksize); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) fs->blocksize) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + /* + * Now write out the block group descriptors + */ + cp = (char *) fs->group_desc; + actual = write(fd, cp, fs->blocksize * fs->desc_blocks); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read the superblock and group descriptors and overwrite them. + */ +errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf; + ssize_t actual, size; + errcode_t retval; + + size = fs->blocksize * (fs->group_desc_count + 1); + buf = malloc(size); + if (!buf) + return ENOMEM; + + /* + * Read it all in. + */ + actual = read(fd, buf, size); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != size) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + + /* + * Now copy in the superblock and group descriptors + */ + memcpy(fs->super, buf, SUPERBLOCK_SIZE); + + memcpy(fs->group_desc, buf + fs->blocksize, + fs->blocksize * fs->group_desc_count); + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Write the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) +{ + ext2fs_generic_bitmap bmap; + errcode_t err, retval; + ssize_t actual; + __u32 itr, cnt, size; + int c, total_size; + char buf[1024]; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->inode_map; + err = EXT2_ET_MAGIC_INODE_BITMAP; + itr = 1; + cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->block_map; + err = EXT2_ET_MAGIC_BLOCK_BITMAP; + itr = fs->super->s_first_data_block; + cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + total_size = size * fs->group_desc_count; + + while (cnt > 0) { + size = sizeof(buf); + if (size > (cnt >> 3)) + size = (cnt >> 3); + + retval = ext2fs_get_generic_bmap_range(bmap, itr, + size << 3, buf); + if (retval) + return retval; + + actual = write(fd, buf, size); + if (actual == -1) + return errno; + if (actual != (int) size) + return EXT2_ET_SHORT_READ; + + itr += size << 3; + cnt -= size << 3; + } + + size = total_size % fs->blocksize; + memset(buf, 0, sizeof(buf)); + if (size) { + size = fs->blocksize - size; + while (size) { + c = size; + if (c > (int) sizeof(buf)) + c = sizeof(buf); + actual = write(fd, buf, c); + if (actual == -1) + return errno; + if (actual != c) + return EXT2_ET_SHORT_WRITE; + size -= c; + } + } + return 0; +} + + +/* + * Read the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) +{ + ext2fs_generic_bitmap bmap; + errcode_t err, retval; + __u32 itr, cnt; + char buf[1024]; + unsigned int size; + ssize_t actual; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->inode_map; + err = EXT2_ET_MAGIC_INODE_BITMAP; + itr = 1; + cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + bmap = fs->block_map; + err = EXT2_ET_MAGIC_BLOCK_BITMAP; + itr = fs->super->s_first_data_block; + cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + + while (cnt > 0) { + size = sizeof(buf); + if (size > (cnt >> 3)) + size = (cnt >> 3); + + actual = read(fd, buf, size); + if (actual == -1) + return errno; + if (actual != (int) size) + return EXT2_ET_SHORT_READ; + + retval = ext2fs_set_generic_bmap_range(bmap, itr, + size << 3, buf); + if (retval) + return retval; + + itr += size << 3; + cnt -= size << 3; + } + return 0; +} diff --git a/lib/libext2fs/source/ind_block.c b/lib/libext2fs/source/ind_block.c new file mode 100644 index 0000000..722d3bd --- /dev/null +++ b/lib/libext2fs/source/ind_block.c @@ -0,0 +1,66 @@ +/* + * ind_block.c --- indirect block I/O routines + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ + errcode_t retval; +#ifdef WORDS_BIGENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && + (fs->io != fs->image_io)) + memset(buf, 0, fs->blocksize); + else { + retval = io_channel_read_blk(fs->io, blk, 1, buf); + if (retval) + return retval; + } +#ifdef WORDS_BIGENDIAN + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); +#endif + return 0; +} + +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ +#ifdef WORDS_BIGENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) + return 0; + +#ifdef WORDS_BIGENDIAN + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); +#endif + return io_channel_write_blk(fs->io, blk, 1, buf); +} + + diff --git a/lib/libext2fs/source/initialize.c b/lib/libext2fs/source/initialize.c new file mode 100644 index 0000000..4706773 --- /dev/null +++ b/lib/libext2fs/source/initialize.c @@ -0,0 +1,447 @@ +/* + * initialize.c --- initialize a filesystem handle given superblock + * parameters. Used by mke2fs when initializing a filesystem. + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__linux__) && defined(EXT2_OS_LINUX) +#define CREATOR_OS EXT2_OS_LINUX +#else +#if defined(__GNU__) && defined(EXT2_OS_HURD) +#define CREATOR_OS EXT2_OS_HURD +#else +#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) +#define CREATOR_OS EXT2_OS_FREEBSD +#else +#if defined(LITES) && defined(EXT2_OS_LITES) +#define CREATOR_OS EXT2_OS_LITES +#else +#define CREATOR_OS EXT2_OS_LINUX /* by default */ +#endif /* defined(LITES) && defined(EXT2_OS_LITES) */ +#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ +#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ +#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ + +/* + * Calculate the number of GDT blocks to reserve for online filesystem growth. + * The absolute maximum number of GDT blocks we can reserve is determined by + * the number of block pointers that can fit into a single block. + */ +static unsigned int calc_reserved_gdt_blocks(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + unsigned long bpg = sb->s_blocks_per_group; + unsigned int gdpb = EXT2_DESC_PER_BLOCK(sb); + unsigned long max_blocks = 0xffffffff; + unsigned long rsv_groups; + unsigned int rsv_gdb; + + /* We set it at 1024x the current filesystem size, or + * the upper block count limit (2^32), whichever is lower. + */ + if (ext2fs_blocks_count(sb) < max_blocks / 1024) + max_blocks = ext2fs_blocks_count(sb) * 1024; + /* + * ext2fs_div64_ceil() is unnecessary because max_blocks is + * max _GDT_ blocks, which is limited to 32 bits. + */ + rsv_groups = ext2fs_div_ceil(max_blocks - sb->s_first_data_block, bpg); + rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - fs->desc_blocks; + if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) + rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); +#ifdef RES_GDT_DEBUG + printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n", + max_blocks, rsv_groups, rsv_gdb); +#endif + + return rsv_gdb; +} + +errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs) +{ + ext2_filsys fs; + errcode_t retval; + struct ext2_super_block *super; + unsigned int rem; + unsigned int overhead = 0; + unsigned int ipg; + dgrp_t i; + blk_t numblocks; + int rsv_gdt; + int csum_flag; + int io_flags; + char *buf = 0; + char c; + + if (!param || !ext2fs_blocks_count(param)) + return EXT2_ET_INVALID_ARGUMENT; + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->flags = flags | EXT2_FLAG_RW; + fs->umask = 022; +#ifdef WORDS_BIGENDIAN + fs->flags |= EXT2_FLAG_SWAP_BYTES; +#endif + io_flags = IO_FLAG_RW; + if (flags & EXT2_FLAG_EXCLUSIVE) + io_flags |= IO_FLAG_EXCLUSIVE; + retval = manager->open(name, io_flags, &fs->io); + if (retval) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + + strcpy(fs->device_name, name); + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); + if (retval) + goto cleanup; + fs->super = super; + + memset(super, 0, SUPERBLOCK_SIZE); + +#define set_field(field, default) (super->field = param->field ? \ + param->field : (default)) + + super->s_magic = EXT2_SUPER_MAGIC; + super->s_state = EXT2_VALID_FS; + + set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ + set_field(s_log_cluster_size, 0); + set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); + set_field(s_max_mnt_count, 0); + set_field(s_errors, EXT2_ERRORS_DEFAULT); + set_field(s_feature_compat, 0); + set_field(s_feature_incompat, 0); + set_field(s_feature_ro_compat, 0); + set_field(s_default_mount_opts, 0); + set_field(s_first_meta_bg, 0); + set_field(s_raid_stride, 0); /* default stride size: 0 */ + set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ + set_field(s_log_groups_per_flex, 0); + set_field(s_flags, 0); + if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + + set_field(s_rev_level, EXT2_GOOD_OLD_REV); + if (super->s_rev_level >= EXT2_DYNAMIC_REV) { + set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); + set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); + if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { + int extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + set_field(s_min_extra_isize, extra_isize); + set_field(s_want_extra_isize, extra_isize); + } + } else { + super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + } + + set_field(s_checkinterval, 0); + super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); + + super->s_creator_os = CREATOR_OS; + + fs->blocksize = EXT2_BLOCK_SIZE(super); + fs->clustersize = EXT2_CLUSTER_SIZE(super); + + /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ + set_field(s_blocks_per_group, fs->blocksize * 8); + if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) + super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); + super->s_clusters_per_group = super->s_blocks_per_group; + + ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); + ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); + if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + + /* + * If we're creating an external journal device, we don't need + * to bother with the rest. + */ + if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + ext2fs_mark_super_dirty(fs); + *ret_fs = fs; + return 0; + } + +retry: + fs->group_desc_count = (blk_t) ext2fs_div64_ceil( + ext2fs_blocks_count(super) - super->s_first_data_block, + EXT2_BLOCKS_PER_GROUP(super)); + if (fs->group_desc_count == 0) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } + + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT; + + fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, + EXT2_DESC_PER_BLOCK(super)); + + i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; + + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && + (ext2fs_blocks_count(super) / i) > (1ULL << 32)) + set_field(s_inodes_count, ~0U); + else + set_field(s_inodes_count, ext2fs_blocks_count(super) / i); + + /* + * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so + * that we have enough inodes for the filesystem(!) + */ + if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) + super->s_inodes_count = EXT2_FIRST_INODE(super)+1; + + /* + * There should be at least as many inodes as the user + * requested. Figure out how many inodes per group that + * should be. But make sure that we don't allocate more than + * one bitmap's worth of inodes each group. + */ + ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); + if (ipg > fs->blocksize * 8) { + if (super->s_blocks_per_group >= 256) { + /* Try again with slightly different parameters */ + super->s_blocks_per_group -= 8; + ext2fs_blocks_count_set(super, + ext2fs_blocks_count(param)); + super->s_clusters_per_group = super->s_blocks_per_group; + goto retry; + } else { + retval = EXT2_ET_TOO_MANY_INODES; + goto cleanup; + } + } + + if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) + ipg = EXT2_MAX_INODES_PER_GROUP(super); + +ipg_retry: + super->s_inodes_per_group = ipg; + + /* + * Make sure the number of inodes per group completely fills + * the inode table blocks in the descriptor. If not, add some + * additional inodes/group. Waste not, want not... + */ + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + super->s_inodes_per_group = ((fs->inode_blocks_per_group * + EXT2_BLOCK_SIZE(super)) / + EXT2_INODE_SIZE(super)); + /* + * Finally, make sure the number of inodes per group is a + * multiple of 8. This is needed to simplify the bitmap + * splicing code. + */ + if (super->s_inodes_per_group < 8) + super->s_inodes_per_group = 8; + super->s_inodes_per_group &= ~7; + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + + /* + * adjust inode count to reflect the adjusted inodes_per_group + */ + if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { + ipg--; + goto ipg_retry; + } + super->s_inodes_count = super->s_inodes_per_group * + fs->group_desc_count; + super->s_free_inodes_count = super->s_inodes_count; + + /* + * check the number of reserved group descriptor table blocks + */ + if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) + rsv_gdt = calc_reserved_gdt_blocks(fs); + else + rsv_gdt = 0; + set_field(s_reserved_gdt_blocks, rsv_gdt); + if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { + retval = EXT2_ET_RES_GDT_BLOCKS; + goto cleanup; + } + + /* + * Calculate the maximum number of bookkeeping blocks per + * group. It includes the superblock, the block group + * descriptors, the block bitmap, the inode bitmap, the inode + * table, and the reserved gdt blocks. + */ + overhead = (int) (3 + fs->inode_blocks_per_group + + fs->desc_blocks + super->s_reserved_gdt_blocks); + + /* This can only happen if the user requested too many inodes */ + if (overhead > super->s_blocks_per_group) { + retval = EXT2_ET_TOO_MANY_INODES; + goto cleanup; + } + + /* + * See if the last group is big enough to support the + * necessary data structures. If not, we need to get rid of + * it. We need to recalculate the overhead for the last block + * group, since it might or might not have a superblock + * backup. + */ + overhead = (int) (2 + fs->inode_blocks_per_group); + if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) + overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; + rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % + super->s_blocks_per_group); + if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } + if (rem && (rem < overhead+50)) { + ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - + rem); + + goto retry; + } + + /* + * At this point we know how big the filesystem will be. So + * we can do any and all allocations that depend on the block + * count. + */ + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + goto cleanup; + + strcpy(buf, "block bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + + strcpy(buf, "inode bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + + ext2fs_free_mem(&buf); + + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + + memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); + + /* + * Reserve the superblock and group descriptors for each + * group, and fill in the correct group statistics for group. + * Note that although the block bitmap, inode bitmap, and + * inode table have not been allocated (and in fact won't be + * by this routine), they are accounted for nevertheless. + * + * If FLEX_BG meta-data grouping is used, only account for the + * superblock and group descriptors (the inode tables and + * bitmaps will be accounted for when allocated). + */ + ext2fs_free_blocks_count_set(super, 0); + csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + for (i = 0; i < fs->group_desc_count; i++) { + /* + * Don't set the BLOCK_UNINIT group for the last group + * because the block bitmap needs to be padded. + */ + if (csum_flag) { + if (i != fs->group_desc_count - 1) + ext2fs_bg_flags_set(fs, i, + EXT2_BG_BLOCK_UNINIT); + ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); + numblocks = super->s_inodes_per_group; + if (i == 0) + numblocks -= super->s_first_ino; + ext2fs_bg_itable_unused_set(fs, i, numblocks); + } + numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); + if (fs->super->s_log_groups_per_flex) + numblocks += 2 + fs->inode_blocks_per_group; + + ext2fs_free_blocks_count_set(super, + ext2fs_free_blocks_count(super) + + numblocks); + ext2fs_bg_free_blocks_count_set(fs, i, numblocks); + ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); + ext2fs_bg_used_dirs_count_set(fs, i, 0); + ext2fs_group_desc_csum_set(fs, i); + } + + c = (char) 255; + if (((int) c) == -1) { + super->s_flags |= EXT2_FLAGS_SIGNED_HASH; + } else { + super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; + } + + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); + ext2fs_mark_ib_dirty(fs); + + io_channel_set_blksize(fs->io, fs->blocksize); + + *ret_fs = fs; + return 0; +cleanup: + free(buf); + ext2fs_free(fs); + return retval; +} diff --git a/lib/libext2fs/source/inline.c b/lib/libext2fs/source/inline.c new file mode 100644 index 0000000..f9be368 --- /dev/null +++ b/lib/libext2fs/source/inline.c @@ -0,0 +1,32 @@ +/* + * inline.c --- Includes the inlined functions defined in the header + * files as standalone functions, in case the application program + * is compiled with inlining turned off. + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#define INCLUDE_INLINE_FUNCS +#include "ext2fs.h" + diff --git a/lib/libext2fs/source/inode.c b/lib/libext2fs/source/inode.c new file mode 100644 index 0000000..a762dbc --- /dev/null +++ b/lib/libext2fs/source/inode.c @@ -0,0 +1,834 @@ +/* + * inode.c --- utility routines to read and write inodes + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +struct ext2_struct_inode_scan { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t current_inode; + blk64_t current_block; + dgrp_t current_group; + ext2_ino_t inodes_left; + blk_t blocks_left; + dgrp_t groups_left; + blk_t inode_buffer_blocks; + char * inode_buffer; + int inode_size; + char * ptr; + int bytes_left; + char *temp_buffer; + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data); + void * done_group_data; + int bad_block_ptr; + int scan_flags; + int reserved[6]; +}; + +/* + * This routine flushes the icache, if it exists. + */ +errcode_t ext2fs_flush_icache(ext2_filsys fs) +{ + int i; + + if (!fs->icache) + return 0; + + for (i=0; i < fs->icache->cache_size; i++) + fs->icache->cache[i].ino = 0; + + fs->icache->buffer_blk = 0; + return 0; +} + +static errcode_t create_icache(ext2_filsys fs) +{ + errcode_t retval; + + if (fs->icache) + return 0; + retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); + if (retval) + return retval; + + memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); + retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); + if (retval) { + ext2fs_free_mem(&fs->icache); + return retval; + } + fs->icache->buffer_blk = 0; + fs->icache->cache_last = -1; + fs->icache->cache_size = 4; + fs->icache->refcount = 1; + retval = ext2fs_get_array(fs->icache->cache_size, + sizeof(struct ext2_inode_cache_ent), + &fs->icache->cache); + if (retval) { + ext2fs_free_mem(&fs->icache->buffer); + ext2fs_free_mem(&fs->icache); + return retval; + } + ext2fs_flush_icache(fs); + return 0; +} + +errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan) +{ + ext2_inode_scan scan; + errcode_t retval; + errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * If fs->badblocks isn't set, then set it --- since the inode + * scanning functions require it. + */ + if (fs->badblocks == 0) { + /* + * Temporarly save fs->get_blocks and set it to zero, + * for compatibility with old e2fsck's. + */ + save_get_blocks = fs->get_blocks; + fs->get_blocks = 0; + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval && fs->badblocks) { + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + } + fs->get_blocks = save_get_blocks; + } + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); + if (retval) + return retval; + memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); + + scan->magic = EXT2_ET_MAGIC_INODE_SCAN; + scan->fs = fs; + scan->inode_size = EXT2_INODE_SIZE(fs->super); + scan->bytes_left = 0; + scan->current_group = 0; + scan->groups_left = fs->group_desc_count - 1; + scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); + scan->blocks_left = scan->fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + scan->inodes_left -= + ext2fs_bg_itable_unused(fs, scan->current_group); + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + retval = ext2fs_get_memalign(scan->inode_buffer_blocks * fs->blocksize, + fs->blocksize, &scan->inode_buffer); + scan->done_group = 0; + scan->done_group_data = 0; + scan->bad_block_ptr = 0; + if (retval) { + ext2fs_free_mem(&scan); + return retval; + } + retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); + if (retval) { + ext2fs_free_mem(&scan->inode_buffer); + ext2fs_free_mem(&scan); + return retval; + } + if (scan->fs->badblocks && scan->fs->badblocks->num) + scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + scan->scan_flags |= EXT2_SF_DO_LAZY; + *ret_scan = scan; + return 0; +} + +void ext2fs_close_inode_scan(ext2_inode_scan scan) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + ext2fs_free_mem(&scan->inode_buffer); + scan->inode_buffer = NULL; + ext2fs_free_mem(&scan->temp_buffer); + scan->temp_buffer = NULL; + ext2fs_free_mem(&scan); + return; +} + +void ext2fs_set_inode_callback(ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data), + void *done_group_data) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + scan->done_group = done_group; + scan->done_group_data = done_group_data; +} + +int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags) +{ + int old_flags; + + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return 0; + + old_flags = scan->scan_flags; + scan->scan_flags &= ~clear_flags; + scan->scan_flags |= set_flags; + return old_flags; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * get ready to read in a new blockgroup. + */ +static errcode_t get_next_blockgroup(ext2_inode_scan scan) +{ + ext2_filsys fs = scan->fs; + + scan->current_group++; + scan->groups_left--; + + scan->current_block = ext2fs_inode_table_loc(scan->fs, + scan->current_group); + scan->current_inode = scan->current_group * + EXT2_INODES_PER_GROUP(fs->super); + + scan->bytes_left = 0; + scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); + scan->blocks_left = fs->inode_blocks_per_group; + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + scan->inodes_left -= + ext2fs_bg_itable_unused(fs, scan->current_group); + scan->blocks_left = + (scan->inodes_left + + (fs->blocksize / scan->inode_size - 1)) * + scan->inode_size / fs->blocksize; + } + + return 0; +} + +errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group) +{ + scan->current_group = group - 1; + scan->groups_left = scan->fs->group_desc_count - group; + return get_next_blockgroup(scan); +} + +/* + * This function is called by get_next_blocks() to check for bad + * blocks in the inode table. + * + * This function assumes that badblocks_list->list is sorted in + * increasing order. + */ +static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, + blk_t *num_blocks) +{ + blk_t blk = scan->current_block; + badblocks_list bb = scan->fs->badblocks; + + /* + * If the inode table is missing, then obviously there are no + * bad blocks. :-) + */ + if (blk == 0) + return 0; + + /* + * If the current block is greater than the bad block listed + * in the bad block list, then advance the pointer until this + * is no longer the case. If we run out of bad blocks, then + * we don't need to do any more checking! + */ + while (blk > bb->list[scan->bad_block_ptr]) { + if (++scan->bad_block_ptr >= bb->num) { + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + } + + /* + * If the current block is equal to the bad block listed in + * the bad block list, then handle that one block specially. + * (We could try to handle runs of bad blocks, but that + * only increases CPU efficiency by a small amount, at the + * expense of a huge expense of code complexity, and for an + * uncommon case at that.) + */ + if (blk == bb->list[scan->bad_block_ptr]) { + scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; + *num_blocks = 1; + if (++scan->bad_block_ptr >= bb->num) + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + + /* + * If there is a bad block in the range that we're about to + * read in, adjust the number of blocks to read so that we we + * don't read in the bad block. (Then the next block to read + * will be the bad block, which is handled in the above case.) + */ + if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) + *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); + + return 0; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * read in more blocks from the current blockgroup's inode table. + */ +static errcode_t get_next_blocks(ext2_inode_scan scan) +{ + blk_t num_blocks; + errcode_t retval; + + /* + * Figure out how many blocks to read; we read at most + * inode_buffer_blocks, and perhaps less if there aren't that + * many blocks left to read. + */ + num_blocks = scan->inode_buffer_blocks; + if (num_blocks > scan->blocks_left) + num_blocks = scan->blocks_left; + + /* + * If the past block "read" was a bad block, then mark the + * left-over extra bytes as also being bad. + */ + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { + if (scan->bytes_left) + scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; + scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; + } + + /* + * Do inode bad block processing, if necessary. + */ + if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { + retval = check_for_inode_bad_blocks(scan, &num_blocks); + if (retval) + return retval; + } + + if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || + (scan->current_block == 0)) { + memset(scan->inode_buffer, 0, + (size_t) num_blocks * scan->fs->blocksize); + } else { + retval = io_channel_read_blk64(scan->fs->io, + scan->current_block, + (int) num_blocks, + scan->inode_buffer); + if (retval) + return EXT2_ET_NEXT_INODE_READ; + } + scan->ptr = scan->inode_buffer; + scan->bytes_left = num_blocks * scan->fs->blocksize; + + scan->blocks_left -= num_blocks; + if (scan->current_block) + scan->current_block += num_blocks; + return 0; +} + +#if 0 +/* + * Returns 1 if the entire inode_buffer has a non-zero size and + * contains all zeros. (Not just deleted inodes, since that means + * that part of the inode table was used at one point; we want all + * zeros, which means that the inode table is pristine.) + */ +static inline int is_empty_scan(ext2_inode_scan scan) +{ + int i; + + if (scan->bytes_left == 0) + return 0; + + for (i=0; i < scan->bytes_left; i++) + if (scan->ptr[i]) + return 0; + return 1; +} +#endif + +errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode, int bufsize) +{ + errcode_t retval; + int extra_bytes = 0; + + EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); + + /* + * Do we need to start reading a new block group? + */ + if (scan->inodes_left <= 0) { + force_new_group: + if (scan->done_group) { + retval = (scan->done_group) + (scan->fs, scan, scan->current_group, + scan->done_group_data); + if (retval) + return retval; + } + if (scan->groups_left <= 0) { + *ino = 0; + return 0; + } + retval = get_next_blockgroup(scan); + if (retval) + return retval; + } + /* + * These checks are done outside the above if statement so + * they can be done for block group #0. + */ + if ((scan->scan_flags & EXT2_SF_DO_LAZY) && + (ext2fs_bg_flags_test(scan->fs, scan->current_group, EXT2_BG_INODE_UNINIT) + )) + goto force_new_group; + if (scan->inodes_left == 0) + goto force_new_group; + if (scan->current_block == 0) { + if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { + goto force_new_group; + } else + return EXT2_ET_MISSING_INODE_TABLE; + } + + + /* + * Have we run out of space in the inode buffer? If so, we + * need to read in more blocks. + */ + if (scan->bytes_left < scan->inode_size) { + memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); + extra_bytes = scan->bytes_left; + + retval = get_next_blocks(scan); + if (retval) + return retval; +#if 0 + /* + * XXX test Need check for used inode somehow. + * (Note: this is hard.) + */ + if (is_empty_scan(scan)) + goto force_new_group; +#endif + } + + retval = 0; + if (extra_bytes) { + memcpy(scan->temp_buffer+extra_bytes, scan->ptr, + scan->inode_size - extra_bytes); + scan->ptr += scan->inode_size - extra_bytes; + scan->bytes_left -= scan->inode_size - extra_bytes; + +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->temp_buffer, + 0, bufsize); +#else + *inode = *((struct ext2_inode *) scan->temp_buffer); +#endif + if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; + } else { +#ifdef WORDS_BIGENDIAN + memset(inode, 0, bufsize); + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->ptr, + 0, bufsize); +#else + memcpy(inode, scan->ptr, bufsize); +#endif + scan->ptr += scan->inode_size; + scan->bytes_left -= scan->inode_size; + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + } + + scan->inodes_left--; + scan->current_inode++; + *ino = scan->current_inode; + return retval; +} + +errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode) +{ + return ext2fs_get_next_inode_full(scan, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * Functions to read and write a single inode. + */ +errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + char *ptr; + errcode_t retval; + int clen, i, inodes_per_block, length; + io_channel io; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user has an override function */ + if (fs->read_inode) { + retval = (fs->read_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + /* Create inode cache if not present */ + if (!fs->icache) { + retval = create_icache(fs); + if (retval) + return retval; + } + /* Check to see if it's in the inode cache */ + if (bufsize == sizeof(struct ext2_inode)) { + /* only old good inode can be retrieved from the cache */ + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + *inode = fs->icache->cache[i].inode; + return 0; + } + } + } + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); + block_nr = fs->image_header->offset_inode / fs->blocksize; + block_nr += (ino - 1) / inodes_per_block; + offset = ((ino - 1) % inodes_per_block) * + EXT2_INODE_SIZE(fs->super); + io = fs->image_io; + } else { + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + if (group > fs->group_desc_count) + return EXT2_ET_BAD_INODE_NUM; + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) + return EXT2_ET_MISSING_INODE_TABLE; + block_nr = ext2fs_inode_table_loc(fs, group) + + block; + io = fs->io; + } + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (bufsize < length) + length = bufsize; + + ptr = (char *) inode; + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (block_nr != fs->icache->buffer_blk) { + retval = io_channel_read_blk64(io, block_nr, 1, + fs->icache->buffer); + if (retval) + return retval; + fs->icache->buffer_blk = block_nr; + } + + memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, + clen); + + offset = 0; + length -= clen; + ptr += clen; + block_nr++; + } + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) inode, + 0, bufsize); +#endif + + /* Update the inode cache */ + fs->icache->cache_last = (fs->icache->cache_last + 1) % + fs->icache->cache_size; + fs->icache->cache[fs->icache->cache_last].ino = ino; + fs->icache->cache[fs->icache->cache_last].inode = *inode; + + return 0; +} + +errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode) +{ + return ext2fs_read_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + errcode_t retval = 0; + struct ext2_inode_large temp_inode, *w_inode; + char *ptr; + int clen, i, length; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user provided an override function */ + if (fs->write_inode) { + retval = (fs->write_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + + /* Check to see if the inode cache needs to be updated */ + if (fs->icache) { + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + fs->icache->cache[i].inode = *inode; + break; + } + } + } else { + retval = create_icache(fs); + if (retval) + return retval; + } + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + + length = bufsize; + if (length < EXT2_INODE_SIZE(fs->super)) + length = EXT2_INODE_SIZE(fs->super); + + if (length > (int) sizeof(struct ext2_inode_large)) { + w_inode = malloc(length); + if (!w_inode) + return ENOMEM; + } else + w_inode = &temp_inode; + memset(w_inode, 0, length); + +#ifdef WORDS_BIGENDIAN + ext2fs_swap_inode_full(fs, w_inode, + (struct ext2_inode_large *) inode, + 1, bufsize); +#else + memcpy(w_inode, inode, bufsize); +#endif + + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!ext2fs_inode_table_loc(fs, (unsigned) group)) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block; + + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (length > bufsize) + length = bufsize; + + ptr = (char *) w_inode; + + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (fs->icache->buffer_blk != block_nr) { + retval = io_channel_read_blk64(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + fs->icache->buffer_blk = block_nr; + } + + + memcpy((char *) fs->icache->buffer + (unsigned) offset, + ptr, clen); + + retval = io_channel_write_blk64(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + + offset = 0; + ptr += clen; + length -= clen; + block_nr++; + } + + fs->flags |= EXT2_FLAG_CHANGED; +errout: + if (w_inode && w_inode != &temp_inode) + free(w_inode); + return retval; +} + +errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * This function should be called when writing a new inode. It makes + * sure that extra part of large inodes is initialized properly. + */ +errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + struct ext2_inode *buf; + int size = EXT2_INODE_SIZE(fs->super); + struct ext2_inode_large *large_inode; + errcode_t retval; + __u32 t = fs->now ? fs->now : time(NULL); + + if (!inode->i_ctime) + inode->i_ctime = t; + if (!inode->i_mtime) + inode->i_mtime = t; + if (!inode->i_atime) + inode->i_atime = t; + + if (size == sizeof(struct ext2_inode)) + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); + + buf = malloc(size); + if (!buf) + return ENOMEM; + + memset(buf, 0, size); + *buf = *inode; + + large_inode = (struct ext2_inode_large *) buf; + large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + if (!large_inode->i_crtime) + large_inode->i_crtime = t; + + retval = ext2fs_write_inode_full(fs, ino, buf, size); + free(buf); + return retval; +} + + +errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) +{ + struct ext2_inode inode; + int i; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->get_blocks) { + if (!(*fs->get_blocks)(fs, ino, blocks)) + return 0; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + for (i=0; i < EXT2_N_BLOCKS; i++) + blocks[i] = inode.i_block[i]; + return 0; +} + +errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode inode; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->check_directory) { + retval = (fs->check_directory)(fs, ino); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + if (!LINUX_S_ISDIR(inode.i_mode)) + return EXT2_ET_NO_DIRECTORY; + return 0; +} + diff --git a/lib/libext2fs/source/inode_io.c b/lib/libext2fs/source/inode_io.c new file mode 100644 index 0000000..b3e7ce5 --- /dev/null +++ b/lib/libext2fs/source/inode_io.c @@ -0,0 +1,291 @@ +/* + * inode_io.c --- This is allows an inode in an ext2 filesystem image + * to be accessed via the I/O manager interface. + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + +struct inode_private_data { + int magic; + char name[32]; + ext2_file_t file; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + struct inode_private_data *next; +}; + +#define CHANNEL_HAS_INODE 0x8000 + +static struct inode_private_data *top_intern; +static int ino_unique = 0; + +static errcode_t inode_open(const char *name, int flags, io_channel *channel); +static errcode_t inode_close(io_channel channel); +static errcode_t inode_set_blksize(io_channel channel, int blksize); +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *data); +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *data); +static errcode_t inode_flush(io_channel channel); +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *data); +static errcode_t inode_read_blk64(io_channel channel, + unsigned long long block, int count, void *data); +static errcode_t inode_write_blk64(io_channel channel, + unsigned long long block, int count, const void *data); + +static struct struct_io_manager struct_inode_manager = { + EXT2_ET_MAGIC_IO_MANAGER, + "Inode I/O Manager", + inode_open, + inode_close, + inode_set_blksize, + inode_read_blk, + inode_write_blk, + inode_flush, + inode_write_byte, + NULL, + NULL, + inode_read_blk64, + inode_write_blk64 +}; + +io_manager inode_io_manager = &struct_inode_manager; + +errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name) +{ + struct inode_private_data *data; + errcode_t retval; + + if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), + &data))) + return retval; + data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; + sprintf(data->name, "%u:%d", ino, ino_unique++); + data->file = 0; + data->fs = fs; + data->ino = ino; + data->flags = 0; + if (inode) { + memcpy(&data->inode, inode, sizeof(struct ext2_inode)); + data->flags |= CHANNEL_HAS_INODE; + } + data->next = top_intern; + top_intern = data; + *name = data->name; + return 0; +} + +errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name) +{ + return ext2fs_inode_io_intern2(fs, ino, NULL, name); +} + + +static errcode_t inode_open(const char *name, int flags, io_channel *channel) +{ + io_channel io = NULL; + struct inode_private_data *prev, *data = NULL; + errcode_t retval; + int open_flags; + + if (name == 0) + return EXT2_ET_BAD_DEVICE_NAME; + + for (data = top_intern, prev = NULL; data; + prev = data, data = data->next) + if (strcmp(name, data->name) == 0) + break; + if (!data) + return ENOENT; + if (prev) + prev->next = data->next; + else + top_intern = data->next; + + retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); + if (retval) + goto cleanup; + memset(io, 0, sizeof(struct struct_io_channel)); + + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io->manager = inode_io_manager; + retval = ext2fs_get_mem(strlen(name)+1, &io->name); + if (retval) + goto cleanup; + + strcpy(io->name, name); + io->private_data = data; + io->block_size = 1024; + io->read_error = 0; + io->write_error = 0; + io->refcount = 1; + + open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; + retval = ext2fs_file_open2(data->fs, data->ino, + (data->flags & CHANNEL_HAS_INODE) ? + &data->inode : 0, open_flags, + &data->file); + if (retval) + goto cleanup; + + *channel = io; + return 0; + +cleanup: + if (io->name) + ext2fs_free_mem(&io->name); + if (data) + ext2fs_free_mem(&data); + if (io) + ext2fs_free_mem(&io); + return retval; +} + +static errcode_t inode_close(io_channel channel) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if (--channel->refcount > 0) + return 0; + + retval = ext2fs_file_close(data->file); + + ext2fs_free_mem(&channel->private_data); + if (channel->name) + ext2fs_free_mem(&channel->name); + ext2fs_free_mem(&channel); + return retval; +} + +static errcode_t inode_set_blksize(io_channel channel, int blksize) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + channel->block_size = blksize; + return 0; +} + + +static errcode_t inode_read_blk64(io_channel channel, + unsigned long long block, int count, void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_read(data->file, buf, count, 0); +} + +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + return inode_read_blk64(channel, block, count, buf); +} + +static errcode_t inode_write_blk64(io_channel channel, + unsigned long long block, int count, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_write(data->file, buf, count, 0); +} + +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + return inode_write_blk64(channel, block, count, buf); +} + +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, offset, + EXT2_SEEK_SET, 0))) + return retval; + + return ext2fs_file_write(data->file, buf, size, 0); +} + +/* + * Flush data buffers to disk. + */ +static errcode_t inode_flush(io_channel channel) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + return ext2fs_file_flush(data->file); +} + diff --git a/lib/libext2fs/source/io_manager.c b/lib/libext2fs/source/io_manager.c new file mode 100644 index 0000000..80f9dfc --- /dev/null +++ b/lib/libext2fs/source/io_manager.c @@ -0,0 +1,112 @@ +/* + * io_manager.c --- the I/O manager abstraction + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t io_channel_set_options(io_channel channel, const char *opts) +{ + errcode_t retval = 0; + char *next, *ptr, *options, *arg; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!opts) + return 0; + + if (!channel->manager->set_option) + return EXT2_ET_INVALID_ARGUMENT; + + options = malloc(strlen(opts)+1); + if (!options) + return EXT2_ET_NO_MEMORY; + strcpy(options, opts); + ptr = options; + + while (ptr && *ptr) { + next = strchr(ptr, '&'); + if (next) + *next++ = 0; + + arg = strchr(ptr, '='); + if (arg) + *arg++ = 0; + + retval = (channel->manager->set_option)(channel, ptr, arg); + if (retval) + break; + ptr = next; + } + free(options); + return retval; +} + +errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_byte) + return channel->manager->write_byte(channel, offset, + count, data); + + return EXT2_ET_UNIMPLEMENTED; +} + +errcode_t io_channel_read_blk64(io_channel channel, unsigned long long block, + int count, void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->read_blk64) + return (channel->manager->read_blk64)(channel, block, + count, data); + + if ((block >> 32) != 0) + return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; + + return (channel->manager->read_blk)(channel, (unsigned long) block, + count, data); +} + +errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_blk64) + return (channel->manager->write_blk64)(channel, block, + count, data); + + if ((block >> 32) != 0) + return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; + + return (channel->manager->write_blk)(channel, (unsigned long) block, + count, data); +} + +errcode_t io_channel_discard(io_channel channel, unsigned long long block, + unsigned long long count) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->discard) + return (channel->manager->discard)(channel, block, count); + + return EXT2_ET_UNIMPLEMENTED; +} diff --git a/lib/libext2fs/source/irel.h b/lib/libext2fs/source/irel.h new file mode 100644 index 0000000..9a4958b --- /dev/null +++ b/lib/libext2fs/source/irel.h @@ -0,0 +1,114 @@ +/* + * irel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +struct ext2_inode_reference { + blk_t block; + __u16 offset; +}; + +struct ext2_inode_relocate_entry { + ext2_ino_t new; + ext2_ino_t orig; + __u16 flags; + __u16 max_refs; +}; + +typedef struct ext2_inode_relocation_table *ext2_irel; + +struct ext2_inode_relocation_table { + __u32 magic; + char *name; + ext2_ino_t current; + void *priv_data; + + /* + * Add an inode relocation entry. + */ + errcode_t (*put)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + /* + * Get an inode relocation entry. + */ + errcode_t (*get)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + + /* + * Get an inode relocation entry by its original inode number + */ + errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Initialize for iterating over the inode relocation entries. + */ + errcode_t (*start_iter)(ext2_irel irel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Add an inode reference (i.e., note the fact that a + * particular block/offset contains a reference to an inode) + */ + errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); + + /* + * Initialize for iterating over the inode references for a + * particular inode. + */ + errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); + + /* + * The iterator function for the inode references for an + * inode. The references for only one inode can be interator + * over at a time, as the iterator state is stored in ext2_irel. + */ + errcode_t (*next_ref)(ext2_irel irel, + struct ext2_inode_reference *ref); + + /* + * Move the inode relocation table from one inode number to + * another. Note that the inode references also must move. + */ + errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); + + /* + * Remove an inode relocation entry, along with all of the + * inode references. + */ + errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); + + /* + * Free the inode relocation table. + */ + errcode_t (*free)(ext2_irel irel); +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *irel); + +#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) +#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) +#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ + ((irel)->get_by_orig((irel), orig, old, ent)) +#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) +#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) +#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) +#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) +#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) +#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) +#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) +#define ext2fs_irel_free(irel) ((irel)->free((irel))) diff --git a/lib/libext2fs/source/irel_ma.c b/lib/libext2fs/source/irel_ma.c new file mode 100644 index 0000000..b7eaf6b --- /dev/null +++ b/lib/libext2fs/source/irel_ma.c @@ -0,0 +1,372 @@ +/* + * irel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "irel.h" + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_start_iter(ext2_irel irel); +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); +static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); +static errcode_t ima_free(ext2_irel irel); + +/* + * This data structure stores the array of inode references; there is + * a structure for each inode. + */ +struct inode_reference_entry { + __u16 num; + struct ext2_inode_reference *refs; +}; + +struct irel_ma { + __u32 magic; + ext2_ino_t max_inode; + ext2_ino_t ref_current; + int ref_iter; + ext2_ino_t *orig_map; + struct ext2_inode_relocate_entry *entries; + struct inode_reference_entry *ref_entries; +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *new_irel) +{ + ext2_irel irel = 0; + errcode_t retval; + struct irel_ma *ma = 0; + size_t size; + + *new_irel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), + &irel); + if (retval) + goto errout; + memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &irel->name); + if (retval) + goto errout; + strcpy(irel->name, name); + + retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct irel_ma)); + irel->priv_data = ma; + + size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); + retval = ext2fs_get_array(max_inode+1, sizeof(ext2_ino_t), + &ma->orig_map); + if (retval) + goto errout; + memset(ma->orig_map, 0, size); + + size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * (max_inode+1)); + retval = ext2fs_get_array((max_inode+1), sizeof(struct ext2_inode_relocate_entry), &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + + size = (size_t) (sizeof(struct inode_reference_entry) * (max_inode+1)); + retval = ext2fs_get_mem(size, &ma->ref_entries); + if (retval) + goto errout; + memset(ma->ref_entries, 0, size); + ma->max_inode = max_inode; + + /* + * Fill in the irel data structure + */ + irel->put = ima_put; + irel->get = ima_get; + irel->get_by_orig = ima_get_by_orig; + irel->start_iter = ima_start_iter; + irel->next = ima_next; + irel->add_ref = ima_add_ref; + irel->start_iter_ref = ima_start_iter_ref; + irel->next_ref = ima_next_ref; + irel->move = ima_move; + irel->delete = ima_delete; + irel->free = ima_free; + + *new_irel = irel; + return 0; + +errout: + ima_free(irel); + return retval; +} + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct inode_reference_entry *ref_ent; + struct irel_ma *ma; + errcode_t retval; + size_t size, old_size; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + /* + * Force the orig field to the correct value; the application + * program shouldn't be messing with this field. + */ + if (ma->entries[(unsigned) old].new == 0) + ent->orig = old; + else + ent->orig = ma->entries[(unsigned) old].orig; + + /* + * If max_refs has changed, reallocate the refs array + */ + ref_ent = ma->ref_entries + (unsigned) old; + if (ref_ent->refs && ent->max_refs != + ma->entries[(unsigned) old].max_refs) { + size = (sizeof(struct ext2_inode_reference) * ent->max_refs); + old_size = (sizeof(struct ext2_inode_reference) * + ma->entries[(unsigned) old].max_refs); + retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); + if (retval) + return retval; + } + + ma->entries[(unsigned) old] = *ent; + ma->orig_map[(unsigned) ent->orig] = old; + return 0; +} + +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + *ent = ma->entries[(unsigned) old]; + return 0; +} + +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + ma = irel->priv_data; + if (orig > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + ino = ma->orig_map[(unsigned) orig]; + if (ino == 0) + return ENOENT; + *old = ino; + *ent = ma->entries[(unsigned) ino]; + return 0; +} + +static errcode_t ima_start_iter(ext2_irel irel) +{ + irel->current = 0; + return 0; +} + +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + while (++irel->current < ma->max_inode) { + if (ma->entries[(unsigned) irel->current].new == 0) + continue; + *old = irel->current; + *ent = ma->entries[(unsigned) irel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + size_t size; + struct inode_reference_entry *ref_ent; + struct ext2_inode_relocate_entry *ent; + errcode_t retval; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + ref_ent = ma->ref_entries + (unsigned) ino; + ent = ma->entries + (unsigned) ino; + + /* + * If the inode reference array doesn't exist, create it. + */ + if (ref_ent->refs == 0) { + size = (size_t) ((sizeof(struct ext2_inode_reference) * + ent->max_refs)); + retval = ext2fs_get_array(ent->max_refs, + sizeof(struct ext2_inode_reference), &ref_ent->refs); + if (retval) + return retval; + memset(ref_ent->refs, 0, size); + ref_ent->num = 0; + } + + if (ref_ent->num >= ent->max_refs) + return EXT2_ET_TOO_MANY_REFS; + + ref_ent->refs[(unsigned) ref_ent->num++] = *ref; + return 0; +} + +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) ino].new == 0) + return ENOENT; + ma->ref_current = ino; + ma->ref_iter = 0; + return 0; +} + +static errcode_t ima_next_ref(ext2_irel irel, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + struct inode_reference_entry *ref_ent; + + ma = irel->priv_data; + + ref_ent = ma->ref_entries + ma->ref_current; + + if ((ref_ent->refs == NULL) || + (ma->ref_iter >= ref_ent->num)) { + ref->block = 0; + ref->offset = 0; + return 0; + } + *ref = ref_ent->refs[ma->ref_iter++]; + return 0; +} + + +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if ((old > ma->max_inode) || (new > ma->max_inode)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; + if (ma->ref_entries[(unsigned) new].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); + ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; + + ma->entries[(unsigned) old].new = 0; + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + + ma->orig_map[ma->entries[new].orig] = new; + return 0; +} + +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[old].new = 0; + if (ma->ref_entries[(unsigned) old].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); + ma->orig_map[ma->entries[(unsigned) old].orig] = 0; + + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + return 0; +} + +static errcode_t ima_free(ext2_irel irel) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + if (!irel) + return 0; + + ma = irel->priv_data; + + if (ma) { + if (ma->orig_map) + ext2fs_free_mem(&ma->orig_map); + if (ma->entries) + ext2fs_free_mem(&ma->entries); + if (ma->ref_entries) { + for (ino = 0; ino <= ma->max_inode; ino++) { + if (ma->ref_entries[(unsigned) ino].refs) + ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); + } + ext2fs_free_mem(&ma->ref_entries); + } + ext2fs_free_mem(&ma); + } + if (irel->name) + ext2fs_free_mem(&irel->name); + ext2fs_free_mem(&irel); + return 0; +} diff --git a/lib/libext2fs/source/ismounted.c b/lib/libext2fs/source/ismounted.c new file mode 100644 index 0000000..bc1134f --- /dev/null +++ b/lib/libext2fs/source/ismounted.c @@ -0,0 +1,383 @@ +/* + * ismounted.c --- Check to see if the filesystem was mounted + * + * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_MNTENT_H +#include +#endif +#ifdef HAVE_GETMNTINFO +#include +#include +#include +#endif /* HAVE_GETMNTINFO */ +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifdef HAVE_MNTENT_H +/* + * Helper function which checks a file in /etc/mtab format to see if a + * filesystem is mounted. Returns an error if the file doesn't exist + * or can't be opened. + */ +static errcode_t check_mntent_file(const char *mtab_file, const char *file, + int *mount_flags, char *mtpt, int mtlen) +{ + struct mntent *mnt; + struct stat st_buf; + errcode_t retval = 0; + dev_t file_dev=0, file_rdev=0; + ino_t file_ino=0; + FILE *f; + int fd; + + *mount_flags = 0; + if ((f = setmntent (mtab_file, "r")) == NULL) + return (errno == ENOENT ? EXT2_NO_MTAB_FILE : errno); + if (stat(file, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + file_rdev = st_buf.st_rdev; +#endif /* __GNU__ */ + } else { + file_dev = st_buf.st_dev; + file_ino = st_buf.st_ino; + } + } + while ((mnt = getmntent (f)) != NULL) { + if (mnt->mnt_fsname[0] != '/') + continue; + if (strcmp(file, mnt->mnt_fsname) == 0) + break; + if (stat(mnt->mnt_fsname, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ + if (file_rdev && (file_rdev == st_buf.st_rdev)) + break; +#endif /* __GNU__ */ + } else { + if (file_dev && ((file_dev == st_buf.st_dev) && + (file_ino == st_buf.st_ino))) + break; + } + } + } + + if (mnt == 0) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + /* + * Do an extra check to see if this is the root device. We + * can't trust /etc/mtab, and /proc/mounts will only list + * /dev/root for the root filesystem. Argh. Instead we + * check if the given device has the same major/minor number + * as the device that the root directory is on. + */ + if (file_rdev && stat("/", &st_buf) == 0) { + if (st_buf.st_dev == file_rdev) { + *mount_flags = EXT2_MF_MOUNTED; + if (mtpt) + strncpy(mtpt, "/", mtlen); + goto is_root; + } + } +#endif /* __GNU__ */ + goto errout; + } +#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ + /* Validate the entry in case /etc/mtab is out of date */ + /* + * We need to be paranoid, because some broken distributions + * (read: Slackware) don't initialize /etc/mtab before checking + * all of the non-root filesystems on the disk. + */ + if (stat(mnt->mnt_dir, &st_buf) < 0) { + retval = errno; + if (retval == ENOENT) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s does not exist)\n", + mtab_file, mnt->mnt_dir); +#endif /* DEBUG */ + retval = 0; + } + goto errout; + } + if (file_rdev && (st_buf.st_dev != file_rdev)) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s not mounted on %s)\n", + mtab_file, file, mnt->mnt_dir); +#endif /* DEBUG */ + goto errout; + } +#endif /* __GNU__ */ + *mount_flags = EXT2_MF_MOUNTED; + +#ifdef MNTOPT_RO + /* Check to see if the ro option is set */ + if (hasmntopt(mnt, MNTOPT_RO)) + *mount_flags |= EXT2_MF_READONLY; +#endif + + if (mtpt) + strncpy(mtpt, mnt->mnt_dir, mtlen); + /* + * Check to see if we're referring to the root filesystem. + * If so, do a manual check to see if we can open /etc/mtab + * read/write, since if the root is mounted read/only, the + * contents of /etc/mtab may not be accurate. + */ + if (!strcmp(mnt->mnt_dir, "/")) { +is_root: +#define TEST_FILE "/.ismount-test-file" + *mount_flags |= EXT2_MF_ISROOT; + fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); + if (fd < 0) { + if (errno == EROFS) + *mount_flags |= EXT2_MF_READONLY; + } else + close(fd); + (void) unlink(TEST_FILE); + } + retval = 0; +errout: + endmntent (f); + return retval; +} + +static errcode_t check_mntent(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + errcode_t retval; + +#ifdef DEBUG + retval = check_mntent_file("/tmp/mtab", file, mount_flags, + mtpt, mtlen); + if (retval == 0) + return 0; +#endif /* DEBUG */ +#ifdef __linux__ + retval = check_mntent_file("/proc/mounts", file, mount_flags, + mtpt, mtlen); + if (retval == 0 && (*mount_flags != 0)) + return 0; +#endif /* __linux__ */ +#if defined(MOUNTED) || defined(_PATH_MOUNTED) +#ifndef MOUNTED +#define MOUNTED _PATH_MOUNTED +#endif /* MOUNTED */ + retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); + return retval; +#else + *mount_flags = 0; + return 0; +#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ +} + +#else +#if defined(HAVE_GETMNTINFO) + +static errcode_t check_getmntinfo(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + struct statfs *mp; + int len, n; + const char *s1; + char *s2; + + n = getmntinfo(&mp, MNT_NOWAIT); + if (n == 0) + return errno; + + len = sizeof(_PATH_DEV) - 1; + s1 = file; + if (strncmp(_PATH_DEV, s1, len) == 0) + s1 += len; + + *mount_flags = 0; + while (--n >= 0) { + s2 = mp->f_mntfromname; + if (strncmp(_PATH_DEV, s2, len) == 0) { + s2 += len - 1; + *s2 = 'r'; + } + if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { + *mount_flags = EXT2_MF_MOUNTED; + break; + } + ++mp; + } + if (mtpt) + strncpy(mtpt, mp->f_mntonname, mtlen); + return 0; +} +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ + +/* + * Check to see if we're dealing with the swap device. + */ +static int is_swap_device(const char *file) +{ + FILE *f; + char buf[1024], *cp; + dev_t file_dev; + struct stat st_buf; + int ret = 0; + + file_dev = 0; +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + if ((stat(file, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode)) + file_dev = st_buf.st_rdev; +#endif /* __GNU__ */ + + if (!(f = fopen("/proc/swaps", "r"))) + return 0; + /* Skip the first line */ + if (!fgets(buf, sizeof(buf), f)) + goto leave; + if (*buf && strncmp(buf, "Filename\t", 9)) + /* Linux <=2.6.19 contained a bug in the /proc/swaps + * code where the header would not be displayed + */ + goto valid_first_line; + + while (fgets(buf, sizeof(buf), f)) { +valid_first_line: + if ((cp = strchr(buf, ' ')) != NULL) + *cp = 0; + if ((cp = strchr(buf, '\t')) != NULL) + *cp = 0; + if (strcmp(buf, file) == 0) { + ret++; + break; + } +#ifndef __GNU__ + if (file_dev && (stat(buf, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode) && + file_dev == st_buf.st_rdev) { + ret++; + break; + } +#endif /* __GNU__ */ + } + +leave: + fclose(f); + return ret; +} + + +/* + * ext2fs_check_mount_point() fills determines if the device is + * mounted or otherwise busy, and fills in mount_flags with one or + * more of the following flags: EXT2_MF_MOUNTED, EXT2_MF_ISROOT, + * EXT2_MF_READONLY, EXT2_MF_SWAP, and EXT2_MF_BUSY. If mtpt is + * non-NULL, the directory where the device is mounted is copied to + * where mtpt is pointing, up to mtlen characters. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen) +{ + errcode_t retval = 0; + + if (is_swap_device(device)) { + *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; + strncpy(mtpt, "", mtlen); + } else { +#ifdef HAVE_MNTENT_H + retval = check_mntent(device, mount_flags, mtpt, mtlen); +#else +#ifdef HAVE_GETMNTINFO + retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); +#else + *mount_flags = 0; +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ + } + if (retval) + return retval; + +#ifdef __linux__ /* This only works on Linux 2.6+ systems */ + if ((stat(device, &st_buf) != 0) || + !S_ISBLK(st_buf.st_mode)) + return 0; + fd = open(device, O_RDONLY | O_EXCL); + if (fd < 0) { + if (errno == EBUSY) + *mount_flags |= EXT2_MF_BUSY; + } else + close(fd); +#endif + + return 0; +} + +/* + * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, + * EXT2_MF_READONLY, and EXT2_MF_ROOT + * + */ +errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) +{ + return ext2fs_check_mount_point(file, mount_flags, NULL, 0); +} + +#ifdef DEBUG +int main(int argc, char **argv) +{ + int retval, mount_flags; + char mntpt[80]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + add_error_table(&et_ext2_error_table); + mntpt[0] = 0; + retval = ext2fs_check_mount_point(argv[1], &mount_flags, + mntpt, sizeof(mntpt)); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_check_if_mounted"); + exit(1); + } + printf("Device %s reports flags %02x\n", argv[1], mount_flags); + if (mount_flags & EXT2_MF_BUSY) + printf("\t%s is apparently in use.\n", argv[1]); + if (mount_flags & EXT2_MF_MOUNTED) + printf("\t%s is mounted.\n", argv[1]); + if (mount_flags & EXT2_MF_SWAP) + printf("\t%s is a swap device.\n", argv[1]); + if (mount_flags & EXT2_MF_READONLY) + printf("\t%s is read-only.\n", argv[1]); + if (mount_flags & EXT2_MF_ISROOT) + printf("\t%s is the root filesystem.\n", argv[1]); + if (mntpt[0]) + printf("\t%s is mounted on %s.\n", argv[1], mntpt); + exit(0); +} +#endif /* DEBUG */ diff --git a/lib/libext2fs/source/jfs_compat.h b/lib/libext2fs/source/jfs_compat.h new file mode 100644 index 0000000..7b8aafd --- /dev/null +++ b/lib/libext2fs/source/jfs_compat.h @@ -0,0 +1,68 @@ + +#ifndef _JFS_COMPAT_H +#define _JFS_COMPAT_H + +#include "kernel-list.h" +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#define printk printf +#define KERN_ERR "" +#define KERN_DEBUG "" + +#define READ 0 +#define WRITE 1 + +#define cpu_to_be32(n) htonl(n) +#define be32_to_cpu(n) ntohl(n) + +typedef unsigned int tid_t; +typedef struct journal_s journal_t; + +struct buffer_head; +struct inode; + +struct journal_s +{ + unsigned long j_flags; + int j_errno; + struct buffer_head * j_sb_buffer; + struct journal_superblock_s *j_superblock; + int j_format_version; + unsigned long j_head; + unsigned long j_tail; + unsigned long j_free; + unsigned long j_first, j_last; + kdev_t j_dev; + kdev_t j_fs_dev; + int j_blocksize; + unsigned int j_blk_offset; + unsigned int j_maxlen; + struct inode * j_inode; + tid_t j_tail_sequence; + tid_t j_transaction_sequence; + __u8 j_uuid[16]; + struct jbd_revoke_table_s *j_revoke; + tid_t j_failed_commit; +}; + +#define J_ASSERT(assert) \ + do { if (!(assert)) { \ + printf ("Assertion failure in %s() at %s line %d: " \ + "\"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + fatal_error(e2fsck_global_ctx, 0); \ + } } while (0) + +#define is_journal_abort(x) 0 + +#define BUFFER_TRACE(bh, info) do {} while (0) + +/* Need this so we can compile with configure --enable-gcc-wall */ +#ifdef NO_INLINE_FUNCS +#define inline +#endif + +#endif /* _JFS_COMPAT_H */ diff --git a/lib/libext2fs/source/jfs_dat.h b/lib/libext2fs/source/jfs_dat.h new file mode 100644 index 0000000..62778c6 --- /dev/null +++ b/lib/libext2fs/source/jfs_dat.h @@ -0,0 +1,64 @@ +/* + * jfs_dat.h --- stripped down header file which only contains the JFS + * on-disk data structures + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK 3 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ +} journal_block_tag_t; + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock + */ +typedef struct journal_superblock_s +{ + journal_header_t s_header; + + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +} journal_superblock_t; + diff --git a/lib/libext2fs/source/jfs_user.h b/lib/libext2fs/source/jfs_user.h new file mode 100644 index 0000000..3a52123 --- /dev/null +++ b/lib/libext2fs/source/jfs_user.h @@ -0,0 +1,8 @@ +#ifndef _JFS_USER_H +#define _JFS_USER_H + +typedef unsigned short kdev_t; + +#include "kernel-jbd.h" + +#endif /* _JFS_USER_H */ diff --git a/lib/libext2fs/source/kernel-jbd.h b/lib/libext2fs/source/kernel-jbd.h new file mode 100644 index 0000000..066c031 --- /dev/null +++ b/lib/libext2fs/source/kernel-jbd.h @@ -0,0 +1,952 @@ +/* + * linux/include/linux/jbd.h + * + * Written by Stephen C. Tweedie + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * Definitions for transaction data structures for the buffer cache + * filesystem journaling support. + */ + +#ifndef _LINUX_JBD_H +#define _LINUX_JBD_H + +#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__) + +/* Allow this file to be included directly into e2fsprogs */ +#ifndef __KERNEL__ +#include "jfs_compat.h" +#define JFS_DEBUG +#define jfs_debug jbd_debug +#else + +#include +#include +#include +#endif + +#ifndef __GNUC__ +#define __FUNCTION__ "" +#endif + +#define journal_oom_retry 1 + +#ifdef __STDC__ +#ifdef CONFIG_JBD_DEBUG +/* + * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal + * consistency checks. By default we don't do this unless + * CONFIG_JBD_DEBUG is on. + */ +#define JBD_EXPENSIVE_CHECKING +extern int journal_enable_debug; + +#define jbd_debug(n, f, a...) \ + do { \ + if ((n) <= journal_enable_debug) { \ + printk (KERN_DEBUG "(%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } \ + } while (0) +#else +#ifdef __GNUC__ +#define jbd_debug(f, a...) /**/ +#else +#define jbd_debug(f, ...) /**/ +#endif +#endif +#else +#define jbd_debug(x) /* AIX doesn't do STDC */ +#endif + +extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); +#define jbd_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) +#define jbd_rep_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) + +#define JFS_MIN_JOURNAL_BLOCKS 1024 + +#ifdef __KERNEL__ +typedef struct handle_s handle_t; /* Atomic operation type */ +typedef struct journal_s journal_t; /* Journal control structure */ +#endif + +/* + * Internal structures used by the logging mechanism: + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK_V1 3 +#define JFS_SUPERBLOCK_V2 4 +#define JFS_REVOKE_BLOCK 5 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + +/* + * Checksum types. + */ +#define JBD2_CRC32_CHKSUM 1 +#define JBD2_MD5_CHKSUM 2 +#define JBD2_SHA1_CHKSUM 3 + +#define JBD2_CRC32_CHKSUM_SIZE 4 + +#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) +/* + * Commit block header for storing transactional checksums: + */ +struct commit_header { + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; + unsigned char h_chksum_type; + unsigned char h_chksum_size; + unsigned char h_padding[2]; + __u32 h_chksum[JBD2_CHECKSUM_BYTES]; + __u64 h_commit_sec; + __u32 h_commit_nsec; +}; + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ + __u32 t_blocknr_high; /* most-significant high 32bits. */ +} journal_block_tag_t; + +#define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t)) +#define JBD_TAG_SIZE32 (8) + +/* + * The revoke descriptor: used on disk to describe a series of blocks to + * be revoked from the log + */ +typedef struct journal_revoke_header_s +{ + journal_header_t r_header; + int r_count; /* Count of bytes used in the block */ +} journal_revoke_header_t; + + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock. All fields are in big-endian byte order. + */ +typedef struct journal_superblock_s +{ +/* 0x0000 */ + journal_header_t s_header; + +/* 0x000C */ + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + +/* 0x0018 */ + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +/* 0x0020 */ + /* Error value, as set by journal_abort(). */ + __s32 s_errno; + +/* 0x0024 */ + /* Remaining fields are only valid in a version-2 superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ +/* 0x0030 */ + __u8 s_uuid[16]; /* 128-bit uuid for journal */ + +/* 0x0040 */ + __u32 s_nr_users; /* Nr of filesystems sharing log */ + + __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ + +/* 0x0048 */ + __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ + __u32 s_max_trans_data; /* Limit of data blocks per trans. */ + +/* 0x0050 */ + __u32 s_padding[44]; + +/* 0x0100 */ + __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ +/* 0x0400 */ +} journal_superblock_t; + +#define JFS_HAS_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) +#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) +#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) + +#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 +#define JFS_FEATURE_INCOMPAT_64BIT 0x00000002 +#define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 + +/* Features known to this kernel version: */ +#define JFS_KNOWN_COMPAT_FEATURES 0 +#define JFS_KNOWN_ROCOMPAT_FEATURES 0 +#define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\ + JFS_FEATURE_INCOMPAT_ASYNC_COMMIT|\ + JFS_FEATURE_INCOMPAT_64BIT) + +#ifdef __KERNEL__ + +#include +#include + +#define JBD_ASSERTIONS +#ifdef JBD_ASSERTIONS +#define J_ASSERT(assert) \ +do { \ + if (!(assert)) { \ + printk (KERN_EMERG \ + "Assertion failure in %s() at %s:%d: \"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + BUG(); \ + } \ +} while (0) + +#if defined(CONFIG_BUFFER_DEBUG) +void buffer_assertion_failure(struct buffer_head *bh); +#define J_ASSERT_BH(bh, expr) \ + do { \ + if (!(expr)) \ + buffer_assertion_failure(bh); \ + J_ASSERT(expr); \ + } while (0) +#define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr) +#else +#define J_ASSERT_BH(bh, expr) J_ASSERT(expr) +#define J_ASSERT_JH(jh, expr) J_ASSERT(expr) +#endif + +#else +#define J_ASSERT(assert) +#endif /* JBD_ASSERTIONS */ + +enum jbd_state_bits { + BH_JWrite + = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */ + BH_Freed, /* 1 if buffer has been freed (truncated) */ + BH_Revoked, /* 1 if buffer has been revoked from the log */ + BH_RevokeValid, /* 1 if buffer revoked flag is valid */ + BH_JBDDirty, /* 1 if buffer is dirty but journaled */ +}; + +/* Return true if the buffer is one which JBD is managing */ +static inline int buffer_jbd(struct buffer_head *bh) +{ + return __buffer_state(bh, JBD); +} + +static inline struct buffer_head *jh2bh(struct journal_head *jh) +{ + return jh->b_bh; +} + +static inline struct journal_head *bh2jh(struct buffer_head *bh) +{ + return bh->b_private; +} + +struct jbd_revoke_table_s; + +/* The handle_t type represents a single atomic update being performed + * by some process. All filesystem modifications made by the process go + * through this handle. Recursive operations (such as quota operations) + * are gathered into a single update. + * + * The buffer credits field is used to account for journaled buffers + * being modified by the running process. To ensure that there is + * enough log space for all outstanding operations, we need to limit the + * number of outstanding buffers possible at any time. When the + * operation completes, any buffer credits not used are credited back to + * the transaction, so that at all times we know how many buffers the + * outstanding updates on a transaction might possibly touch. */ + +struct handle_s +{ + /* Which compound transaction is this update a part of? */ + transaction_t * h_transaction; + + /* Number of remaining buffers we are allowed to dirty: */ + int h_buffer_credits; + + /* Reference count on this handle */ + int h_ref; + + /* Field for caller's use to track errors through large fs + operations */ + int h_err; + + /* Flags */ + unsigned int h_sync: 1; /* sync-on-close */ + unsigned int h_jdata: 1; /* force data journaling */ + unsigned int h_aborted: 1; /* fatal error on handle */ +}; + + +/* The transaction_t type is the guts of the journaling mechanism. It + * tracks a compound transaction through its various states: + * + * RUNNING: accepting new updates + * LOCKED: Updates still running but we don't accept new ones + * RUNDOWN: Updates are tidying up but have finished requesting + * new buffers to modify (state not used for now) + * FLUSH: All updates complete, but we are still writing to disk + * COMMIT: All data on disk, writing commit record + * FINISHED: We still have to keep the transaction for checkpointing. + * + * The transaction keeps track of all of the buffers modified by a + * running transaction, and all of the buffers committed but not yet + * flushed to home for finished transactions. + */ + +struct transaction_s +{ + /* Pointer to the journal for this transaction. */ + journal_t * t_journal; + + /* Sequence number for this transaction */ + tid_t t_tid; + + /* Transaction's current state */ + enum { + T_RUNNING, + T_LOCKED, + T_RUNDOWN, + T_FLUSH, + T_COMMIT, + T_FINISHED + } t_state; + + /* Where in the log does this transaction's commit start? */ + unsigned long t_log_start; + + /* Doubly-linked circular list of all inodes owned by this + transaction */ /* AKPM: unused */ + struct inode * t_ilist; + + /* Number of buffers on the t_buffers list */ + int t_nr_buffers; + + /* Doubly-linked circular list of all buffers reserved but not + yet modified by this transaction */ + struct journal_head * t_reserved_list; + + /* Doubly-linked circular list of all metadata buffers owned by this + transaction */ + struct journal_head * t_buffers; + + /* + * Doubly-linked circular list of all data buffers still to be + * flushed before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_sync_datalist; + + /* + * Doubly-linked circular list of all writepage data buffers + * still to be written before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_async_datalist; + + /* Doubly-linked circular list of all forget buffers (superceded + buffers which we can un-checkpoint once this transaction + commits) */ + struct journal_head * t_forget; + + /* + * Doubly-linked circular list of all buffers still to be + * flushed before this transaction can be checkpointed. + */ + /* Protected by journal_datalist_lock */ + struct journal_head * t_checkpoint_list; + + /* Doubly-linked circular list of temporary buffers currently + undergoing IO in the log */ + struct journal_head * t_iobuf_list; + + /* Doubly-linked circular list of metadata buffers being + shadowed by log IO. The IO buffers on the iobuf list and the + shadow buffers on this list match each other one for one at + all times. */ + struct journal_head * t_shadow_list; + + /* Doubly-linked circular list of control buffers being written + to the log. */ + struct journal_head * t_log_list; + + /* Number of outstanding updates running on this transaction */ + int t_updates; + + /* Number of buffers reserved for use by all handles in this + * transaction handle but not yet modified. */ + int t_outstanding_credits; + + /* + * Forward and backward links for the circular list of all + * transactions awaiting checkpoint. + */ + /* Protected by journal_datalist_lock */ + transaction_t *t_cpnext, *t_cpprev; + + /* When will the transaction expire (become due for commit), in + * jiffies ? */ + unsigned long t_expires; + + /* How many handles used this transaction? */ + int t_handle_count; +}; + + +/* The journal_t maintains all of the journaling state information for a + * single filesystem. It is linked to from the fs superblock structure. + * + * We use the journal_t to keep track of all outstanding transaction + * activity on the filesystem, and to manage the state of the log + * writing process. */ + +struct journal_s +{ + /* General journaling state flags */ + unsigned long j_flags; + + /* Is there an outstanding uncleared error on the journal (from + * a prior abort)? */ + int j_errno; + + /* The superblock buffer */ + struct buffer_head * j_sb_buffer; + journal_superblock_t * j_superblock; + + /* Version of the superblock format */ + int j_format_version; + + /* Number of processes waiting to create a barrier lock */ + int j_barrier_count; + + /* The barrier lock itself */ + struct semaphore j_barrier; + + /* Transactions: The current running transaction... */ + transaction_t * j_running_transaction; + + /* ... the transaction we are pushing to disk ... */ + transaction_t * j_committing_transaction; + + /* ... and a linked circular list of all transactions waiting + * for checkpointing. */ + /* Protected by journal_datalist_lock */ + transaction_t * j_checkpoint_transactions; + + /* Wait queue for waiting for a locked transaction to start + committing, or for a barrier lock to be released */ + wait_queue_head_t j_wait_transaction_locked; + + /* Wait queue for waiting for checkpointing to complete */ + wait_queue_head_t j_wait_logspace; + + /* Wait queue for waiting for commit to complete */ + wait_queue_head_t j_wait_done_commit; + + /* Wait queue to trigger checkpointing */ + wait_queue_head_t j_wait_checkpoint; + + /* Wait queue to trigger commit */ + wait_queue_head_t j_wait_commit; + + /* Wait queue to wait for updates to complete */ + wait_queue_head_t j_wait_updates; + + /* Semaphore for locking against concurrent checkpoints */ + struct semaphore j_checkpoint_sem; + + /* The main journal lock, used by lock_journal() */ + struct semaphore j_sem; + + /* Journal head: identifies the first unused block in the journal. */ + unsigned long j_head; + + /* Journal tail: identifies the oldest still-used block in the + * journal. */ + unsigned long j_tail; + + /* Journal free: how many free blocks are there in the journal? */ + unsigned long j_free; + + /* Journal start and end: the block numbers of the first usable + * block and one beyond the last usable block in the journal. */ + unsigned long j_first, j_last; + + /* Device, blocksize and starting block offset for the location + * where we store the journal. */ + kdev_t j_dev; + int j_blocksize; + unsigned int j_blk_offset; + + /* Device which holds the client fs. For internal journal this + * will be equal to j_dev. */ + kdev_t j_fs_dev; + + /* Total maximum capacity of the journal region on disk. */ + unsigned int j_maxlen; + + /* Optional inode where we store the journal. If present, all + * journal block numbers are mapped into this inode via + * bmap(). */ + struct inode * j_inode; + + /* Sequence number of the oldest transaction in the log */ + tid_t j_tail_sequence; + /* Sequence number of the next transaction to grant */ + tid_t j_transaction_sequence; + /* Sequence number of the most recently committed transaction */ + tid_t j_commit_sequence; + /* Sequence number of the most recent transaction wanting commit */ + tid_t j_commit_request; + + /* Journal uuid: identifies the object (filesystem, LVM volume + * etc) backed by this journal. This will eventually be + * replaced by an array of uuids, allowing us to index multiple + * devices within a single journal and to perform atomic updates + * across them. */ + + __u8 j_uuid[16]; + + /* Pointer to the current commit thread for this journal */ + struct task_struct * j_task; + + /* Maximum number of metadata buffers to allow in a single + * compound commit transaction */ + int j_max_transaction_buffers; + + /* What is the maximum transaction lifetime before we begin a + * commit? */ + unsigned long j_commit_interval; + + /* The timer used to wakeup the commit thread: */ + struct timer_list * j_commit_timer; + int j_commit_timer_active; + + /* Link all journals together - system-wide */ + struct list_head j_all_journals; + + /* The revoke table: maintains the list of revoked blocks in the + current transaction. */ + struct jbd_revoke_table_s *j_revoke; + + /* Failed journal commit ID */ + unsigned int j_failed_commit; +}; + +/* + * Journal flag definitions + */ +#define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */ +#define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */ +#define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */ +#define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ +#define JFS_LOADED 0x010 /* The journal superblock has been loaded */ + +/* + * Function declarations for the journaling transaction and buffer + * management + */ + +/* Filing buffers */ +extern void __journal_unfile_buffer(struct journal_head *); +extern void journal_unfile_buffer(struct journal_head *); +extern void __journal_refile_buffer(struct journal_head *); +extern void journal_refile_buffer(struct journal_head *); +extern void __journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_free_buffer(struct journal_head *bh); +extern void journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_clean_data_list(transaction_t *transaction); + +/* Log buffer allocation */ +extern struct journal_head * journal_get_descriptor_buffer(journal_t *); +extern unsigned long journal_next_log_block(journal_t *); + +/* Commit management */ +extern void journal_commit_transaction(journal_t *); + +/* Checkpoint list management */ +int __journal_clean_checkpoint_list(journal_t *journal); +extern void journal_remove_checkpoint(struct journal_head *); +extern void __journal_remove_checkpoint(struct journal_head *); +extern void journal_insert_checkpoint(struct journal_head *, transaction_t *); +extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *); + +/* Buffer IO */ +extern int +journal_write_metadata_buffer(transaction_t *transaction, + struct journal_head *jh_in, + struct journal_head **jh_out, + int blocknr); + +/* Transaction locking */ +extern void __wait_on_journal (journal_t *); + +/* + * Journal locking. + * + * We need to lock the journal during transaction state changes so that + * nobody ever tries to take a handle on the running transaction while + * we are in the middle of moving it to the commit phase. + * + * Note that the locking is completely interrupt unsafe. We never touch + * journal structures from interrupts. + * + * In 2.2, the BKL was required for lock_journal. This is no longer + * the case. + */ + +static inline void lock_journal(journal_t *journal) +{ + down(&journal->j_sem); +} + +/* This returns zero if we acquired the semaphore */ +static inline int try_lock_journal(journal_t * journal) +{ + return down_trylock(&journal->j_sem); +} + +static inline void unlock_journal(journal_t * journal) +{ + up(&journal->j_sem); +} + + +static inline handle_t *journal_current_handle(void) +{ + return current->journal_info; +} + +/* The journaling code user interface: + * + * Create and destroy handles + * Register buffer modifications against the current transaction. + */ + +extern handle_t *journal_start(journal_t *, int nblocks); +extern handle_t *journal_try_start(journal_t *, int nblocks); +extern int journal_restart (handle_t *, int nblocks); +extern int journal_extend (handle_t *, int nblocks); +extern int journal_get_write_access (handle_t *, struct buffer_head *); +extern int journal_get_create_access (handle_t *, struct buffer_head *); +extern int journal_get_undo_access (handle_t *, struct buffer_head *); +extern int journal_dirty_data (handle_t *, + struct buffer_head *, int async); +extern int journal_dirty_metadata (handle_t *, struct buffer_head *); +extern void journal_release_buffer (handle_t *, struct buffer_head *); +extern void journal_forget (handle_t *, struct buffer_head *); +extern void journal_sync_buffer (struct buffer_head *); +extern int journal_flushpage(journal_t *, struct page *, unsigned long); +extern int journal_try_to_free_buffers(journal_t *, struct page *, int); +extern int journal_stop(handle_t *); +extern int journal_flush (journal_t *); + +extern void journal_lock_updates (journal_t *); +extern void journal_unlock_updates (journal_t *); + +extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev, + int start, int len, int bsize); +extern journal_t * journal_init_inode (struct inode *); +extern int journal_update_format (journal_t *); +extern int journal_check_used_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_check_available_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_set_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_create (journal_t *); +extern int journal_load (journal_t *journal); +extern void journal_destroy (journal_t *); +extern int journal_recover (journal_t *journal); +extern int journal_wipe (journal_t *, int); +extern int journal_skip_recovery (journal_t *); +extern void journal_update_superblock (journal_t *, int); +extern void __journal_abort (journal_t *); +extern void journal_abort (journal_t *, int); +extern int journal_errno (journal_t *); +extern void journal_ack_err (journal_t *); +extern int journal_clear_err (journal_t *); +extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr); +extern int journal_force_commit(journal_t *journal); + +/* + * journal_head management + */ +extern struct journal_head + *journal_add_journal_head(struct buffer_head *bh); +extern void journal_remove_journal_head(struct buffer_head *bh); +extern void __journal_remove_journal_head(struct buffer_head *bh); +extern void journal_unlock_journal_head(struct journal_head *jh); + +/* Primary revoke support */ +#define JOURNAL_REVOKE_DEFAULT_HASH 256 +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +extern void journal_destroy_revoke(journal_t *); +extern int journal_revoke (handle_t *, + unsigned long, struct buffer_head *); +extern int journal_cancel_revoke(handle_t *, struct journal_head *); +extern void journal_write_revoke_records(journal_t *, transaction_t *); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +/* The log thread user interface: + * + * Request space in the current transaction, and force transaction commit + * transitions on demand. + */ + +extern int log_space_left (journal_t *); /* Called with journal locked */ +extern tid_t log_start_commit (journal_t *, transaction_t *); +extern void log_wait_commit (journal_t *, tid_t); +extern int log_do_checkpoint (journal_t *, int); + +extern void log_wait_for_space(journal_t *, int nblocks); +extern void __journal_drop_transaction(journal_t *, transaction_t *); +extern int cleanup_journal_tail(journal_t *); + +/* Reduce journal memory usage by flushing */ +extern void shrink_journal_memory(void); + +/* Debugging code only: */ + +#define jbd_ENOSYS() \ +do { \ + printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \ + current->state = TASK_UNINTERRUPTIBLE; \ + schedule(); \ +} while (1) + +/* + * is_journal_abort + * + * Simple test wrapper function to test the JFS_ABORT state flag. This + * bit, when set, indicates that we have had a fatal error somewhere, + * either inside the journaling layer or indicated to us by the client + * (eg. ext3), and that we and should not commit any further + * transactions. + */ + +static inline int is_journal_aborted(journal_t *journal) +{ + return journal->j_flags & JFS_ABORT; +} + +static inline int is_handle_aborted(handle_t *handle) +{ + if (handle->h_aborted) + return 1; + return is_journal_aborted(handle->h_transaction->t_journal); +} + +static inline void journal_abort_handle(handle_t *handle) +{ + handle->h_aborted = 1; +} + +/* Not all architectures define BUG() */ +#ifndef BUG +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + * ((char *) 0) = 0; \ + } while (0) +#endif /* BUG */ + +#else + +extern int journal_recover (journal_t *journal); +extern int journal_skip_recovery (journal_t *); + +/* Primary revoke support */ +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +extern void journal_destroy_revoke(journal_t *); +#endif /* __KERNEL__ */ + +static inline int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused)); +static inline int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused)); + +/* Comparison functions for transaction IDs: perform comparisons using + * modulo arithmetic so that they work over sequence number wraps. */ + +static inline int tid_gt(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference > 0); +} + +static inline int tid_geq(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference >= 0); +} + +extern int journal_blocks_per_page(struct inode *inode); + +/* + * Definitions which augment the buffer_head layer + */ + +/* journaling buffer types */ +#define BJ_None 0 /* Not journaled */ +#define BJ_SyncData 1 /* Normal data: flush before commit */ +#define BJ_AsyncData 2 /* writepage data: wait on it before commit */ +#define BJ_Metadata 3 /* Normal journaled metadata */ +#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_IO 5 /* Buffer is for temporary IO use */ +#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 7 /* Buffer contains log descriptors */ +#define BJ_Reserved 8 /* Buffer is reserved for access by journal */ +#define BJ_Types 9 + +extern int jbd_blocks_per_page(struct inode *inode); + +#ifdef __KERNEL__ + +extern spinlock_t jh_splice_lock; +/* + * Once `expr1' has been found true, take jh_splice_lock + * and then reevaluate everything. + */ +#define SPLICE_LOCK(expr1, expr2) \ + ({ \ + int ret = (expr1); \ + if (ret) { \ + spin_lock(&jh_splice_lock); \ + ret = (expr1) && (expr2); \ + spin_unlock(&jh_splice_lock); \ + } \ + ret; \ + }) + +/* + * A number of buffer state predicates. They test for + * buffer_jbd() because they are used in core kernel code. + * + * These will be racy on SMP unless we're *sure* that the + * buffer won't be detached from the journalling system + * in parallel. + */ + +/* Return true if the buffer is on journal list `list' */ +static inline int buffer_jlist_eq(struct buffer_head *bh, int list) +{ + return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list); +} + +/* Return true if this bufer is dirty wrt the journal */ +static inline int buffer_jdirty(struct buffer_head *bh) +{ + return buffer_jbd(bh) && __buffer_state(bh, JBDDirty); +} + +/* Return true if it's a data buffer which journalling is managing */ +static inline int buffer_jbd_data(struct buffer_head *bh) +{ + return SPLICE_LOCK(buffer_jbd(bh), + bh2jh(bh)->b_jlist == BJ_SyncData || + bh2jh(bh)->b_jlist == BJ_AsyncData); +} + +#ifdef CONFIG_SMP +#define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock)) +#else +#define assert_spin_locked(lock) do {} while(0) +#endif + +#define buffer_trace_init(bh) do {} while (0) +#define print_buffer_fields(bh) do {} while (0) +#define print_buffer_trace(bh) do {} while (0) +#define BUFFER_TRACE(bh, info) do {} while (0) +#define BUFFER_TRACE2(bh, bh2, info) do {} while (0) +#define JBUFFER_TRACE(jh, info) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */ + +/* + * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD + * go here. + */ + +#if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE)) + +#define J_ASSERT(expr) do {} while (0) +#define J_ASSERT_BH(bh, expr) do {} while (0) +#define buffer_jbd(bh) 0 +#define buffer_jlist_eq(bh, val) 0 +#define journal_buffer_journal_lru(bh) 0 + +#endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */ +#endif /* _LINUX_JBD_H */ diff --git a/lib/libext2fs/source/kernel-list.h b/lib/libext2fs/source/kernel-list.h new file mode 100644 index 0000000..e07d06b --- /dev/null +++ b/lib/libext2fs/source/kernel-list.h @@ -0,0 +1,112 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = { &name, &name } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define __inline__ +#endif + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Insert a new entry after the specified head.. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Insert a new entry at the tail + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif diff --git a/lib/libext2fs/source/link.c b/lib/libext2fs/source/link.c new file mode 100644 index 0000000..4cc8426 --- /dev/null +++ b/lib/libext2fs/source/link.c @@ -0,0 +1,153 @@ +/* + * link.c --- create links in a ext2fs directory + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + ext2_filsys fs; + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + int done; + unsigned int blocksize; + errcode_t err; + struct ext2_super_block *sb; +}; + +static int link_proc(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *next; + unsigned int rec_len, min_rec_len, curr_rec_len; + int ret = 0; + + rec_len = EXT2_DIR_REC_LEN(ls->namelen); + + ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); + if (ls->err) + return DIRENT_ABORT; + + /* + * See if the following directory entry (if any) is unused; + * if so, absorb it into this one. + */ + next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); + if ((offset + curr_rec_len < blocksize - 8) && + (next->inode == 0) && + (offset + curr_rec_len + next->rec_len <= blocksize)) { + curr_rec_len += next->rec_len; + ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + ret = DIRENT_CHANGED; + } + + /* + * If the directory entry is used, see if we can split the + * directory entry to make room for the new name. If so, + * truncate it and return. + */ + if (dirent->inode) { + min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); + if (curr_rec_len < (min_rec_len + rec_len)) + return ret; + rec_len = curr_rec_len - min_rec_len; + ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); + if (ls->err) + return DIRENT_ABORT; + next = (struct ext2_dir_entry *) (buf + offset + + dirent->rec_len); + next->inode = 0; + next->name_len = 0; + ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); + if (ls->err) + return DIRENT_ABORT; + return DIRENT_CHANGED; + } + + /* + * If we get this far, then the directory entry is not used. + * See if we can fit the request entry in. If so, do it. + */ + if (curr_rec_len < rec_len) + return ret; + dirent->inode = ls->inode; + dirent->name_len = ls->namelen; + strncpy(dirent->name, ls->name, ls->namelen); + if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) + dirent->name_len |= (ls->flags & 0x7) << 8; + + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +/* + * Note: the low 3 bits of the flags field are used as the directory + * entry filetype. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags) +{ + errcode_t retval; + struct link_struct ls; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.fs = fs; + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = flags; + ls.done = 0; + ls.sb = fs->super; + ls.blocksize = fs->blocksize; + ls.err = 0; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, link_proc, &ls); + if (retval) + return retval; + if (ls.err) + return ls.err; + + if (!ls.done) + return EXT2_ET_DIR_NO_SPACE; + + if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) + return retval; + + if (inode.i_flags & EXT2_INDEX_FL) { + inode.i_flags &= ~EXT2_INDEX_FL; + if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) + return retval; + } + + return 0; +} diff --git a/lib/libext2fs/source/llseek.c b/lib/libext2fs/source/llseek.c new file mode 100644 index 0000000..3b919be --- /dev/null +++ b/lib/libext2fs/source/llseek.c @@ -0,0 +1,139 @@ +/* + * llseek.c -- stub calling the llseek system call + * + * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#ifdef __MSDOS__ +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifdef __linux__ + +#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) + +#define my_llseek lseek64 + +#else +#if defined(HAVE_LLSEEK) +#include + +#ifndef HAVE_LLSEEK_PROTOTYPE +extern long long llseek (int fd, long long offset, int origin); +#endif + +#define my_llseek llseek + +#else /* ! HAVE_LLSEEK */ + +#if SIZEOF_LONG == SIZEOF_LONG_LONG + +#define llseek lseek + +#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */ + +#include + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +#ifndef __i386__ +static int _llseek (unsigned int, unsigned long, + unsigned long, ext2_loff_t *, unsigned int); + +static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, + unsigned long, offset_low,ext2_loff_t *,result, + unsigned int, origin) +#endif + +static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin) +{ + ext2_loff_t result; + int retval; + +#ifndef __i386__ + retval = _llseek(fd, ((unsigned long long) offset) >> 32, +#else + retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32), +#endif + ((unsigned long long) offset) & 0xffffffff, + &result, origin); + return (retval == -1 ? (ext2_loff_t) retval : result); +} + +#endif /* __alpha__ || __ia64__ */ + +#endif /* HAVE_LLSEEK */ +#endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */ + +ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) +{ + ext2_loff_t result; + static int do_compat = 0; + + if ((sizeof(off_t) >= sizeof(ext2_loff_t)) || + (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) + return lseek(fd, (off_t) offset, origin); + + if (do_compat) { + errno = EINVAL; + return -1; + } + + result = my_llseek (fd, offset, origin); + if (result == -1 && errno == ENOSYS) { + /* + * Just in case this code runs on top of an old kernel + * which does not support the llseek system call + */ + do_compat++; + errno = EINVAL; + } + return result; +} + +#else /* !linux */ + +#ifndef EINVAL +#define EINVAL EXT2_ET_INVALID_ARGUMENT +#endif + +ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) +{ +#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) + return lseek64 (fd, offset, origin); +#else + if ((sizeof(off_t) < sizeof(ext2_loff_t)) && + (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { + errno = EINVAL; + return -1; + } + return lseek (fd, (off_t) offset, origin); +#endif +} + +#endif /* linux */ + + diff --git a/lib/libext2fs/source/lookup.c b/lib/libext2fs/source/lookup.c new file mode 100644 index 0000000..97aa088 --- /dev/null +++ b/lib/libext2fs/source/lookup.c @@ -0,0 +1,69 @@ +/* + * lookup.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct lookup_struct { + const char *name; + int len; + ext2_ino_t *inode; + int found; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int lookup_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct lookup_struct *ls = (struct lookup_struct *) priv_data; + + if (ls->len != (dirent->name_len & 0xFF)) + return 0; + if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) + return 0; + *ls->inode = dirent->inode; + ls->found++; + return DIRENT_ABORT; +} + + +errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode) +{ + errcode_t retval; + struct lookup_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ls.name = name; + ls.len = namelen; + ls.inode = inode; + ls.found = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); + if (retval) + return retval; + + return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; +} + + diff --git a/lib/libext2fs/source/mem_allocate.h b/lib/libext2fs/source/mem_allocate.h new file mode 100644 index 0000000..31a354b --- /dev/null +++ b/lib/libext2fs/source/mem_allocate.h @@ -0,0 +1,30 @@ +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +extern __inline__ void* mem_alloc (size_t size) { + return malloc(size); +} + +extern __inline__ void* mem_realloc (void *p, size_t size) { + return realloc(p, size); +} + +extern __inline__ void* mem_align (size_t a, size_t size) { + #ifdef __wii__ + return memalign(32, size); + #else + return malloc(size); + #endif +} + +extern __inline__ void mem_free (void* mem) { + free(mem); +} + +extern void* _EXT2_cache_mem_align (size_t a, size_t size); + +extern void _EXT2_cache_mem_free (void* mem); + +#endif /* _MEM_ALLOCATE_H */ diff --git a/lib/libext2fs/source/mkdir.c b/lib/libext2fs/source/mkdir.c new file mode 100644 index 0000000..86c65da --- /dev/null +++ b/lib/libext2fs/source/mkdir.c @@ -0,0 +1,142 @@ +/* + * mkdir.c --- make a directory in the filesystem + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name) +{ + errcode_t retval; + struct ext2_inode parent_inode, inode; + ext2_ino_t ino = inum; + ext2_ino_t scratch_ino; + blk64_t blk; + char *block = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * Allocate an inode, if necessary + */ + if (!ino) { + retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, + 0, &ino); + if (retval) + goto cleanup; + } + + /* + * Allocate a data block for the directory + */ + retval = ext2fs_new_block2(fs, 0, 0, &blk); + if (retval) + goto cleanup; + + /* + * Create a scratch template for the directory + */ + retval = ext2fs_new_dir_block(fs, ino, parent, &block); + if (retval) + goto cleanup; + + /* + * Get the parent's inode, if necessary + */ + if (parent != ino) { + retval = ext2fs_read_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } else + memset(&parent_inode, 0, sizeof(parent_inode)); + + /* + * Create the inode structure.... + */ + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); + inode.i_uid = inode.i_gid = 0; + ext2fs_iblk_set(fs, &inode, 1); + /* FIXME-64 */ + inode.i_block[0] = blk; + inode.i_links_count = 2; + inode.i_size = fs->blocksize; + + /* + * Write out the inode and inode data block + */ + retval = ext2fs_write_dir_block(fs, blk, block); + if (retval) + goto cleanup; + retval = ext2fs_write_new_inode(fs, ino, &inode); + if (retval) + goto cleanup; + + /* + * Link the directory into the filesystem hierarchy + */ + if (name) { + retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, + &scratch_ino); + if (!retval) { + retval = EXT2_ET_DIR_EXISTS; + name = 0; + goto cleanup; + } + if (retval != EXT2_ET_FILE_NOT_FOUND) + goto cleanup; + retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); + if (retval) + goto cleanup; + } + + /* + * Update parent inode's counts + */ + if (parent != ino) { + parent_inode.i_links_count++; + retval = ext2fs_write_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } + + /* + * Update accounting.... + */ + ext2fs_block_alloc_stats2(fs, blk, +1); + ext2fs_inode_alloc_stats2(fs, ino, +1, 1); + +cleanup: + if (block) + ext2fs_free_mem(&block); + return retval; + +} + + diff --git a/lib/libext2fs/source/mkjournal.c b/lib/libext2fs/source/mkjournal.c new file mode 100644 index 0000000..47fb92c --- /dev/null +++ b/lib/libext2fs/source/mkjournal.c @@ -0,0 +1,601 @@ +/* + * mkjournal.c --- make a journal for a filesystem + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#ifdef GEKKO +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "jfs_user.h" + +/* + * This function automatically sets up the journal superblock and + * returns it as an allocated block. + */ +errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb) +{ + errcode_t retval; + journal_superblock_t *jsb; + + if (size < 1024) + return EXT2_ET_JOURNAL_TOO_SMALL; + + if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) + return retval; + + memset (jsb, 0, fs->blocksize); + + jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); + if (flags & EXT2_MKJOURNAL_V1_SUPER) + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); + else + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); + jsb->s_blocksize = htonl(fs->blocksize); + jsb->s_maxlen = htonl(size); + jsb->s_nr_users = htonl(1); + jsb->s_first = htonl(1); + jsb->s_sequence = htonl(1); + memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); + /* + * If we're creating an external journal device, we need to + * adjust these fields. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + jsb->s_nr_users = 0; + if (fs->blocksize == 1024) + jsb->s_first = htonl(3); + else + jsb->s_first = htonl(2); + } + + *ret_jsb = (char *) jsb; + return 0; +} + +/* + * This function writes a journal using POSIX routines. It is used + * for creating external journals and creating journals on live + * filesystems. + */ +static errcode_t write_journal_file(ext2_filsys fs, char *filename, + blk_t size, int flags) +{ + errcode_t retval; + char *buf = 0; + int fd, ret_size; + blk_t i; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + /* Open the device or journal file */ + if ((fd = open(filename, O_WRONLY)) < 0) { + retval = errno; + goto errout; + } + + /* Write the superblock out */ + retval = EXT2_ET_SHORT_WRITE; + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + memset(buf, 0, fs->blocksize); + + for (i = 1; i < size; i++) { + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + } + close(fd); + + retval = 0; +errout: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Convenience function which zeros out _num_ blocks starting at + * _blk_. In case of an error, the details of the error is returned + * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. + * Returns 0 on success, and an error code on an error. + * + * As a special case, if the first argument is NULL, then it will + * attempt to free the static zeroizing buffer. (This is to keep + * programs that check for memory leaks happy.) + */ +#define STRIDE_LENGTH 8 +errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, + blk64_t *ret_blk, int *ret_count) +{ + int j, count; + static char *buf; + errcode_t retval; + + /* If fs is null, clean up the static buffer and return */ + if (!fs) { + if (buf) { + free(buf); + buf = 0; + } + return 0; + } + /* Allocate the zeroizing buffer if necessary */ + if (!buf) { + buf = malloc(fs->blocksize * STRIDE_LENGTH); + if (!buf) + return ENOMEM; + memset(buf, 0, fs->blocksize * STRIDE_LENGTH); + } + /* OK, do the write loop */ + j=0; + while (j < num) { + if (blk % STRIDE_LENGTH) { + count = STRIDE_LENGTH - (blk % STRIDE_LENGTH); + if (count > (num - j)) + count = num - j; + } else { + count = num - j; + if (count > STRIDE_LENGTH) + count = STRIDE_LENGTH; + } + retval = io_channel_write_blk64(fs->io, blk, count, buf); + if (retval) { + if (ret_count) + *ret_count = count; + if (ret_blk) + *ret_blk = blk; + return retval; + } + j += count; blk += count; + } + return 0; +} + +errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, + blk_t *ret_blk, int *ret_count) +{ + blk64_t ret_blk2; + errcode_t retval; + + retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count); + if (retval) + *ret_blk = (blk_t) ret_blk2; + return retval; +} + +/* + * Helper function for creating the journal using direct I/O routines + */ +struct mkjournal_struct { + int num_blocks; + int newblocks; + blk64_t goal; + blk64_t blk_to_zero; + int zero_count; + char *buf; + errcode_t err; +}; + +static int mkjournal_proc(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; + blk64_t new_blk; + errcode_t retval; + + if (*blocknr) { + es->goal = *blocknr; + return 0; + } + retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt >= 0) + es->num_blocks--; + + es->newblocks++; + retval = 0; + if (blockcnt <= 0) + retval = io_channel_write_blk64(fs->io, new_blk, 1, es->buf); + else { + if (es->zero_count) { + if ((es->blk_to_zero + es->zero_count == new_blk) && + (es->zero_count < 1024)) + es->zero_count++; + else { + retval = ext2fs_zero_blocks2(fs, + es->blk_to_zero, + es->zero_count, + 0, 0); + es->zero_count = 0; + } + } + if (es->zero_count == 0) { + es->blk_to_zero = new_blk; + es->zero_count = 1; + } + } + + if (blockcnt == 0) + memset(es->buf, 0, fs->blocksize); + + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + *blocknr = es->goal = new_blk; + ext2fs_block_alloc_stats2(fs, new_blk, +1); + + if (es->num_blocks == 0) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; + +} + +/* + * This function creates a journal using direct I/O routines. + */ +static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, + blk64_t size, int flags) +{ + char *buf; + dgrp_t group, start, end, i, log_flex; + errcode_t retval; + struct ext2_inode inode; + struct mkjournal_struct es; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + if ((retval = ext2fs_read_bitmaps(fs))) + return retval; + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + return retval; + + if (inode.i_blocks > 0) + return EEXIST; + + es.num_blocks = size; + es.newblocks = 0; + es.buf = buf; + es.err = 0; + es.zero_count = 0; + + if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { + inode.i_flags |= EXT4_EXTENTS_FL; + if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) + return retval; + } + + /* + * Set the initial goal block to be roughly at the middle of + * the filesystem. Pick a group that has the largest number + * of free blocks. + */ + group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block) / 2); + log_flex = 1 << fs->super->s_log_groups_per_flex; + if (fs->super->s_log_groups_per_flex && (group > log_flex)) { + group = group & ~(log_flex - 1); + while ((group < fs->group_desc_count) && + ext2fs_bg_free_blocks_count(fs, group) == 0) + group++; + if (group == fs->group_desc_count) + group = 0; + start = group; + } else + start = (group > 0) ? group-1 : group; + end = ((group+1) < fs->group_desc_count) ? group+1 : group; + group = start; + for (i=start+1; i <= end; i++) + if (ext2fs_bg_free_blocks_count(fs, i) > + ext2fs_bg_free_blocks_count(fs, group)) + group = i; + + es.goal = (fs->super->s_blocks_per_group * group) + + fs->super->s_first_data_block; + + retval = ext2fs_block_iterate3(fs, journal_ino, BLOCK_FLAG_APPEND, + 0, mkjournal_proc, &es); + if (es.err) { + retval = es.err; + goto errout; + } + if (es.zero_count) { + retval = ext2fs_zero_blocks2(fs, es.blk_to_zero, + es.zero_count, 0, 0); + if (retval) + goto errout; + } + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + goto errout; + + inode.i_size += fs->blocksize * size; + ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); + inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); + inode.i_links_count = 1; + inode.i_mode = LINUX_S_IFREG | 0600; + + if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) + goto errout; + retval = 0; + + memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); + fs->super->s_jnl_blocks[16] = inode.i_size; + fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; + ext2fs_mark_super_dirty(fs); + +errout: + ext2fs_zero_blocks2(0, 0, 0, 0, 0); + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Find a reasonable journal file size (in blocks) given the number of blocks + * in the filesystem. For very small filesystems, it is not reasonable to + * have a journal that fills more than half of the filesystem. + */ +int ext2fs_default_journal_size(__u64 blocks) +{ + if (blocks < 2048) + return -1; + if (blocks < 32768) + return (1024); + if (blocks < 256*1024) + return (4096); + if (blocks < 512*1024) + return (8192); + if (blocks < 1024*1024) + return (16384); + return 32768; +} + +/* + * This function adds a journal device to a filesystem + */ +errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) +{ + struct stat st; + errcode_t retval; + char buf[1024]; + journal_superblock_t *jsb; + int start; + __u32 i, nr_users; + + /* Make sure the device exists and is a block device */ + if (stat(journal_dev->device_name, &st) < 0) + return errno; + + if (!S_ISBLK(st.st_mode)) + return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ + + /* Get the journal superblock */ + start = 1; + if (journal_dev->blocksize == 1024) + start++; + if ((retval = io_channel_read_blk64(journal_dev->io, start, -1024, + buf))) + return retval; + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) + return EXT2_ET_NO_JOURNAL_SB; + + if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + /* Check and see if this filesystem has already been added */ + nr_users = ntohl(jsb->s_nr_users); + for (i=0; i < nr_users; i++) { + if (memcmp(fs->super->s_uuid, + &jsb->s_users[i*16], 16) == 0) + break; + } + if (i >= nr_users) { + memcpy(&jsb->s_users[nr_users*16], + fs->super->s_uuid, 16); + jsb->s_nr_users = htonl(nr_users+1); + } + + /* Writeback the journal superblock */ + if ((retval = io_channel_write_blk64(journal_dev->io, start, -1024, buf))) + return retval; + + fs->super->s_journal_inum = 0; + fs->super->s_journal_dev = st.st_rdev; + memcpy(fs->super->s_journal_uuid, jsb->s_uuid, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + ext2fs_mark_super_dirty(fs); + return 0; +} + +/* + * This function adds a journal inode to a filesystem, using either + * POSIX routines if the filesystem is mounted, or using direct I/O + * functions if it is not. + */ +errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) +{ + errcode_t retval; + ext2_ino_t journal_ino; + struct stat st; + char jfile[1024]; + int mount_flags; + int fd = -1; + + if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, + jfile, sizeof(jfile)-10))) + return retval; + + if (mount_flags & EXT2_MF_MOUNTED) { + strcat(jfile, "/.journal"); + + /* + * If .../.journal already exists, make sure any + * immutable or append-only flags are cleared. + */ +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + (void) chflags (jfile, 0); +#else +#if HAVE_EXT2_IOCTLS + fd = open(jfile, O_RDONLY); + if (fd >= 0) { + f = 0; + ioctl(fd, EXT2_IOC_SETFLAGS, &f); + close(fd); + } +#endif +#endif + + /* Create the journal file */ + if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) + return errno; + + if ((retval = write_journal_file(fs, jfile, size, flags))) + goto errout; + + /* Get inode number of the journal file */ + if (fstat(fd, &st) < 0) { + retval = errno; + goto errout; + } + +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); +#else +#if HAVE_EXT2_IOCTLS + if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { + retval = errno; + goto errout; + } + f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; + retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); +#endif +#endif + if (retval) { + retval = errno; + goto errout; + } + + if (close(fd) < 0) { + retval = errno; + fd = -1; + goto errout; + } + journal_ino = st.st_ino; + } else { + if ((mount_flags & EXT2_MF_BUSY) && + !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { + retval = EBUSY; + goto errout; + } + journal_ino = EXT2_JOURNAL_INO; + if ((retval = write_journal_inode(fs, journal_ino, + size, flags))) + return retval; + } + + fs->super->s_journal_inum = journal_ino; + fs->super->s_journal_dev = 0; + memset(fs->super->s_journal_uuid, 0, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + + ext2fs_mark_super_dirty(fs); + return 0; +errout: + if (fd > 0) + close(fd); + return retval; +} + +#ifdef DEBUG +main(int argc, char **argv) +{ + errcode_t retval; + char *device_name; + ext2_filsys fs; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filesystem\n", argv[0]); + exit(1); + } + device_name = argv[1]; + + retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, + unix_io_manager, &fs); + if (retval) { + com_err(argv[0], retval, "while opening %s", device_name); + exit(1); + } + + retval = ext2fs_add_journal_inode(fs, 1024); + if (retval) { + com_err(argv[0], retval, "while adding journal to %s", + device_name); + exit(1); + } + retval = ext2fs_flush(fs); + if (retval) { + printf("Warning, had trouble writing out superblocks.\n"); + } + ext2fs_close(fs); + exit(0); + +} +#endif diff --git a/lib/libext2fs/source/namei.c b/lib/libext2fs/source/namei.c new file mode 100644 index 0000000..bc0ae61 --- /dev/null +++ b/lib/libext2fs/source/namei.c @@ -0,0 +1,206 @@ +/* + * namei.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +/* #define NAMEI_DEBUG */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode); + +static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + ext2_ino_t inode, int link_count, + char *buf, ext2_ino_t *res_inode) +{ + char *pathname; + char *buffer = 0; + errcode_t retval; + struct ext2_inode ei; + +#ifdef NAMEI_DEBUG + printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", + root, dir, inode, link_count); + +#endif + retval = ext2fs_read_inode (fs, inode, &ei); + if (retval) return retval; + if (!LINUX_S_ISLNK (ei.i_mode)) { + *res_inode = inode; + return 0; + } + if (link_count++ > 5) { + return EXT2_ET_SYMLINK_LOOP; + } + /* FIXME-64: Actually, this is FIXME EXTENTS */ + if (ext2fs_inode_data_blocks(fs,&ei)) { + retval = ext2fs_get_mem(fs->blocksize, &buffer); + if (retval) + return retval; + retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); + if (retval) { + ext2fs_free_mem(&buffer); + return retval; + } + pathname = buffer; + } else + pathname = (char *)&(ei.i_block[0]); + retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, + link_count, buf, res_inode); + if (buffer) + ext2fs_free_mem(&buffer); + return retval; +} + +/* + * This routine interprets a pathname in the context of the current + * directory and the root directory, and returns the inode of the + * containing directory, and a pointer to the filename of the file + * (pointing into the pathname) and the length of the filename. + */ +static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + const char *pathname, int pathlen, + int link_count, char *buf, + const char **name, int *namelen, + ext2_ino_t *res_inode) +{ + char c; + const char *thisname; + int len; + ext2_ino_t inode; + errcode_t retval; + + if ((c = *pathname) == '/') { + dir = root; + pathname++; + pathlen--; + } + while (1) { + thisname = pathname; + for (len=0; --pathlen >= 0;len++) { + c = *(pathname++); + if (c == '/') + break; + } + if (pathlen < 0) + break; + retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); + if (retval) return retval; + retval = follow_link (fs, root, dir, inode, + link_count, buf, &dir); + if (retval) return retval; + } + *name = thisname; + *namelen = len; + *res_inode = dir; + return 0; +} + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode) +{ + const char *base_name; + int namelen; + ext2_ino_t dir, inode; + errcode_t retval; + +#ifdef NAMEI_DEBUG + printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", + root, base, pathlen, pathname, link_count); +#endif + retval = dir_namei(fs, root, base, pathname, pathlen, + link_count, buf, &base_name, &namelen, &dir); + if (retval) return retval; + if (!namelen) { /* special case: '/usr/' etc */ + *res_inode=dir; + return 0; + } + retval = ext2fs_lookup (fs, dir, base_name, namelen, buf, &inode); + if (retval) + return retval; + if (follow) { + retval = follow_link(fs, root, dir, inode, link_count, + buf, &inode); + if (retval) + return retval; + } +#ifdef NAMEI_DEBUG + printf("open_namei: (link_count=%d) returns %lu\n", + link_count, inode); +#endif + *res_inode = inode; + return 0; +} + +errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); + + ext2fs_free_mem(&buf); + return retval; +} + diff --git a/lib/libext2fs/source/native.c b/lib/libext2fs/source/native.c new file mode 100644 index 0000000..c71a95e --- /dev/null +++ b/lib/libext2fs/source/native.c @@ -0,0 +1,27 @@ +/* + * native.c --- returns the ext2_flag for a native byte order + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +int ext2fs_native_flag(void) +{ +#ifdef WORDS_BIGENDIAN + return EXT2_FLAG_SWAP_BYTES; +#else + return 0; +#endif +} + + + diff --git a/lib/libext2fs/source/newdir.c b/lib/libext2fs/source/newdir.c new file mode 100644 index 0000000..6bc5719 --- /dev/null +++ b/lib/libext2fs/source/newdir.c @@ -0,0 +1,77 @@ +/* + * newdir.c --- create a new directory block + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +/* + * Create new directory block + */ +errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block) +{ + struct ext2_dir_entry *dir = NULL; + errcode_t retval; + char *buf; + int rec_len; + int filetype = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memset(buf, 0, fs->blocksize); + dir = (struct ext2_dir_entry *) buf; + + retval = ext2fs_set_rec_len(fs, fs->blocksize, dir); + if (retval) + return retval; + + if (dir_ino) { + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE) + filetype = EXT2_FT_DIR << 8; + /* + * Set up entry for '.' + */ + dir->inode = dir_ino; + dir->name_len = 1 | filetype; + dir->name[0] = '.'; + rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1); + dir->rec_len = EXT2_DIR_REC_LEN(1); + + /* + * Set up entry for '..' + */ + dir = (struct ext2_dir_entry *) (buf + dir->rec_len); + retval = ext2fs_set_rec_len(fs, rec_len, dir); + if (retval) + return retval; + dir->inode = parent_ino; + dir->name_len = 2 | filetype; + dir->name[0] = '.'; + dir->name[1] = '.'; + + } + *block = buf; + return 0; +} diff --git a/lib/libext2fs/source/openfs.c b/lib/libext2fs/source/openfs.c new file mode 100644 index 0000000..eb04d02 --- /dev/null +++ b/lib/libext2fs/source/openfs.c @@ -0,0 +1,411 @@ +/* + * openfs.c --- open an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" + + +#include "ext2fs.h" +#include "e2image.h" + +blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block, + dgrp_t i) +{ + int bg; + int has_super = 0; + blk64_t ret_blk; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (i < fs->super->s_first_meta_bg)) + return (group_block + i + 1); + + bg = EXT2_DESC_PER_BLOCK(fs->super) * i; + if (ext2fs_bg_has_super(fs, bg)) + has_super = 1; + ret_blk = ext2fs_group_first_block2(fs, bg) + has_super; + /* + * If group_block is not the normal value, we're trying to use + * the backup group descriptors and superblock --- so use the + * alternate location of the second block group in the + * metablock group. Ideally we should be testing each bg + * descriptor block individually for correctness, but we don't + * have the infrastructure in place to do that. + */ + if (group_block != fs->super->s_first_data_block && + ((ret_blk + fs->super->s_blocks_per_group) < + ext2fs_blocks_count(fs->super))) + ret_blk += fs->super->s_blocks_per_group; + return ret_blk; +} + +blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) +{ + return ext2fs_descriptor_block_loc2(fs, group_block, i); +} + +errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_channel * io, + ext2_filsys *ret_fs) +{ + return ext2fs_open2(name, 0, flags, superblock, block_size, + io, ret_fs); +} + +/* + * Note: if superblock is non-zero, block-size must also be non-zero. + * Superblock and block_size can be zero to use the default size. + * + * Valid flags for ext2fs_open() + * + * EXT2_FLAG_RW - Open the filesystem for read/write. + * EXT2_FLAG_FORCE - Open the filesystem even if some of the + * features aren't supported. + * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device + */ +errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_channel * io, + ext2_filsys *ret_fs) +{ + ext2_filsys fs; + io_manager manager = (*io)->manager; + errcode_t retval; + unsigned long i, first_meta_bg; + __u32 features; + int groups_per_block, blocks_per_group, io_flags; + blk64_t group_block, blk; + char *dest, *cp; +#ifdef WORDS_BIGENDIAN + struct ext2_group_desc *gdp; + int j; +#endif + + EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->io = *io; + fs->flags = flags; + /* don't overwrite sb backups unless flag is explicitly cleared */ + fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; + fs->umask = 022; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + strcpy(fs->device_name, name); + cp = strchr(fs->device_name, '?'); + if (!io_options && cp) { + *cp++ = 0; + io_options = cp; + } + + io_flags = 0; + if (flags & EXT2_FLAG_RW) + io_flags |= IO_FLAG_RW; + if (flags & EXT2_FLAG_EXCLUSIVE) + io_flags |= IO_FLAG_EXCLUSIVE; + if (flags & EXT2_FLAG_DIRECT_IO) + io_flags |= IO_FLAG_DIRECT_IO; + retval = manager->open(fs->device_name, io_flags, &fs->io); + if (retval) + goto cleanup; + if (io_options && + (retval = io_channel_set_options(fs->io, io_options))) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super); + if (retval) + goto cleanup; + if (flags & EXT2_FLAG_IMAGE_FILE) { + retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), + &fs->image_header); + if (retval) + goto cleanup; + retval = io_channel_read_blk(fs->io, 0, + -(int)sizeof(struct ext2_image_hdr), + fs->image_header); + if (retval) + goto cleanup; + if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) + return EXT2_ET_MAGIC_E2IMAGE; + superblock = 1; + block_size = fs->image_header->fs_blocksize; + } + + /* + * If the user specifies a specific block # for the + * superblock, then he/she must also specify the block size! + * Otherwise, read the master superblock located at offset + * SUPERBLOCK_OFFSET from the start of the partition. + * + * Note: we only save a backup copy of the superblock if we + * are reading the superblock from the primary superblock location. + */ + if (superblock) { + if (!block_size) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + io_channel_set_blksize(fs->io, block_size); + group_block = superblock; + fs->orig_super = 0; + } else { + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + superblock = 1; + group_block = 0; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto cleanup; + } + retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, + fs->super); + if (retval) + goto cleanup; + if (fs->orig_super) + memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); + +#ifdef WORDS_BIGENDIAN + fs->flags |= EXT2_FLAG_SWAP_BYTES; + ext2fs_swap_super(fs->super); +#else + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + retval = EXT2_ET_UNIMPLEMENTED; + goto cleanup; + } +#endif + + if (fs->super->s_magic != EXT2_SUPER_MAGIC) { + retval = EXT2_ET_BAD_MAGIC; + goto cleanup; + } + if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { + retval = EXT2_ET_REV_TOO_HIGH; + goto cleanup; + } + + /* + * Check for feature set incompatibility + */ + if (!(flags & EXT2_FLAG_FORCE)) { + features = fs->super->s_feature_incompat; +#ifdef EXT2_LIB_SOFTSUPP_INCOMPAT + if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) + features &= !EXT2_LIB_SOFTSUPP_INCOMPAT; +#endif + if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + + features = fs->super->s_feature_ro_compat; +#ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT + if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) + features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT; +#endif + if ((flags & EXT2_FLAG_RW) && + (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + + if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && + (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + } + + if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > + EXT2_MAX_BLOCK_LOG_SIZE) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->blocksize = EXT2_BLOCK_SIZE(fs->super); + if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->clustersize = EXT2_CLUSTER_SIZE(fs->super); + fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * + EXT2_INODE_SIZE(fs->super) + + EXT2_BLOCK_SIZE(fs->super) - 1) / + EXT2_BLOCK_SIZE(fs->super)); + if (block_size) { + if (block_size != fs->blocksize) { + retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; + goto cleanup; + } + } + /* + * Set the blocksize to the filesystem's blocksize. + */ + io_channel_set_blksize(fs->io, fs->blocksize); + + /* + * If this is an external journal device, don't try to read + * the group descriptors, because they're not there. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + *ret_fs = fs; + return 0; + } + + if (EXT2_INODES_PER_GROUP(fs->super) == 0) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + + /* + * Read group descriptors + */ + blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); + if (blocks_per_group == 0 || + blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || + fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || + EXT2_DESC_PER_BLOCK(fs->super) == 0 || + fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block, + blocks_per_group); + if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != + fs->super->s_inodes_count) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, + EXT2_DESC_PER_BLOCK(fs->super)); + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + if (!group_block) + group_block = fs->super->s_first_data_block; + dest = (char *) fs->group_desc; + groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + first_meta_bg = fs->super->s_first_meta_bg; + else + first_meta_bg = fs->desc_blocks; + if (first_meta_bg) { + retval = io_channel_read_blk(fs->io, group_block+1, + first_meta_bg, dest); + if (retval) + goto cleanup; +#ifdef WORDS_BIGENDIAN + gdp = (struct ext2_group_desc *) dest; + for (j=0; j < groups_per_block*first_meta_bg; j++) { + gdp = ext2fs_group_desc(fs, fs->group_desc, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#endif + dest += fs->blocksize*first_meta_bg; + } + for (i=first_meta_bg ; i < fs->desc_blocks; i++) { + blk = ext2fs_descriptor_block_loc2(fs, group_block, i); + retval = io_channel_read_blk64(fs->io, blk, 1, dest); + if (retval) + goto cleanup; +#ifdef WORDS_BIGENDIAN + for (j=0; j < groups_per_block; j++) { + /* The below happens to work... be careful. */ + gdp = ext2fs_group_desc(fs, fs->group_desc, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#endif + dest += fs->blocksize; + } + + fs->stride = fs->super->s_raid_stride; + + /* + * If recovery is from backup superblock, Clear _UNININT flags & + * reset bg_itable_unused to zero + */ + if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + dgrp_t group; + + for (group = 0; group < fs->group_desc_count; group++) { + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, group, 0); + } + ext2fs_mark_super_dirty(fs); + } + + fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; + *ret_fs = fs; + return 0; +cleanup: + if (flags & EXT2_FLAG_NOFREE_ON_ERROR) + *ret_fs = fs; + else + ext2fs_free(fs); + return retval; +} + +/* + * Set/get the filesystem data I/O channel. + * + * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. + */ +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + if (old_io) { + *old_io = (fs->image_io == fs->io) ? 0 : fs->io; + } + return 0; +} + +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = new_io ? new_io : fs->image_io; + return 0; +} + +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = fs->image_io = new_io; + fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | + EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; + fs->flags &= ~EXT2_FLAG_IMAGE_FILE; + return 0; +} diff --git a/lib/libext2fs/source/partitions.h b/lib/libext2fs/source/partitions.h new file mode 100644 index 0000000..9bb1049 --- /dev/null +++ b/lib/libext2fs/source/partitions.h @@ -0,0 +1,49 @@ +#ifndef PARTITIONS_H_ +#define PARTITIONS_H_ + +#include +#include "bitops.h" + +#define MBR_SIGNATURE ext2fs_cpu_to_le16(0xAA55) +#define EBR_SIGNATURE ext2fs_cpu_to_le16(0xAA55) + +#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */ + +#define PARTITION_TYPE_EMPTY 0x00 /* Empty */ +#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */ +#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */ +#define PARTITION_TYPE_LINUX 0x83 /* EXT2/3/4 */ + +/** + * PRIMARY_PARTITION - Block device partition record + */ +typedef struct _PARTITION_RECORD { + u8 status; /* Partition status; see above */ + u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ + u8 type; /* Partition type; see above */ + u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ + u32 lba_start; /* Local block address to first sector of partition */ + u32 block_count; /* Number of blocks in partition */ +} __attribute__((__packed__)) PARTITION_RECORD; + +/** + * MASTER_BOOT_RECORD - Block device master boot record + */ +typedef struct _MASTER_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partitions[4]; /* 4 primary partitions */ + u16 signature; /* MBR signature; 0xAA55 */ +} __attribute__((__packed__)) MASTER_BOOT_RECORD; + +/** + * EXTENDED_PARTITION - Block device extended boot record + */ +typedef struct _EXTENDED_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partition; /* Primary partition */ + PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */ + u8 reserved[32]; /* Normally empty */ + u16 signature; /* EBR signature; 0xAA55 */ +} __attribute__((__packed__)) EXTENDED_BOOT_RECORD; + +#endif diff --git a/lib/libext2fs/source/progress.c b/lib/libext2fs/source/progress.c new file mode 100644 index 0000000..335b98b --- /dev/null +++ b/lib/libext2fs/source/progress.c @@ -0,0 +1,86 @@ +/* + * progress.c - Numeric progress meter + * + * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + * 2003, 2004, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" +#include "ext2fsP.h" + +static char spaces[80], backspaces[80]; + +static int int_log10(unsigned int arg) +{ + int l; + + for (l=0; arg ; l++) + arg = arg / 10; + return l; +} + +void ext2fs_numeric_progress_init(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *label, __u64 max) +{ + /* + * The PRINT_PROGRESS flag turns on or off ALL + * progress-related messages, whereas the SKIP_PROGRESS + * environment variable prints the start and end messages but + * not the numeric countdown in the middle. + */ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + + memset(spaces, ' ', sizeof(spaces)-1); + spaces[sizeof(spaces)-1] = 0; + memset(backspaces, '\b', sizeof(backspaces)-1); + backspaces[sizeof(backspaces)-1] = 0; + progress->skip_progress = 0; + if (getenv("E2FSPROGS_SKIP_PROGRESS")) + progress->skip_progress++; + + memset(progress, 0, sizeof(*progress)); + + /* + * Figure out how many digits we need + */ + progress->max = max; + progress->log_max = int_log10(max); + + if (label) { + fputs(label, stdout); + fflush(stdout); + } +} + +void ext2fs_numeric_progress_update(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + __u64 val) +{ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + if (progress->skip_progress) + return; + + fprintf(stdout, "%*llu/%*llu", progress->log_max, val, + progress->log_max, progress->max); + fprintf(stdout, "%.*s", (2*progress->log_max)+1, backspaces); +} + +void ext2fs_numeric_progress_close(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *message) +{ + if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS)) + return; + fprintf(stdout, "%.*s", (2*progress->log_max)+1, spaces); + fprintf(stdout, "%.*s", (2*progress->log_max)+1, backspaces); + if (message) + fputs(message, stdout); +} diff --git a/lib/libext2fs/source/punch.c b/lib/libext2fs/source/punch.c new file mode 100644 index 0000000..8c6ec54 --- /dev/null +++ b/lib/libext2fs/source/punch.c @@ -0,0 +1,324 @@ +/* + * punch.c --- deallocate blocks allocated to an inode + * + * Copyright (C) 2010 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#undef PUNCH_DEBUG + +/* + * This function returns 1 if the specified block is all zeros + */ +static int check_zero_block(char *buf, int blocksize) +{ + char *cp = buf; + int left = blocksize; + + while (left > 0) { + if (*cp++) + return 0; + left--; + } + return 1; +} + +/* + * This clever recursive function handles i_blocks[] as well as + * indirect, double indirect, and triple indirect blocks. It iterates + * over the entries in the i_blocks array or indirect blocks, and for + * each one, will recursively handle any indirect blocks and then + * frees and deallocates the blocks. + */ +static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode, + char *block_buf, blk_t *p, int level, + blk_t start, blk_t count, int max) +{ + errcode_t retval; + blk_t b, offset; + int i, incr; + int freed = 0; + +#ifdef PUNCH_DEBUG + printf("Entering ind_punch, level %d, start %u, count %u, " + "max %d\n", level, start, count, max); +#endif + incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level); + for (i=0, offset=0; i < max; i++, p++, offset += incr) { + if (offset > count) + break; + if (*p == 0 || (offset+incr) <= start) + continue; + b = *p; + if (level > 0) { + blk_t start2; +#ifdef PUNCH_DEBUG + printf("Reading indirect block %u\n", b); +#endif + retval = ext2fs_read_ind_block(fs, b, block_buf); + if (retval) + return retval; + start2 = (start > offset) ? start - offset : 0; + retval = ind_punch(fs, inode, block_buf + fs->blocksize, + (blk_t *) block_buf, level - 1, + start2, count - offset, + fs->blocksize >> 2); + if (retval) + return retval; + retval = ext2fs_write_ind_block(fs, b, block_buf); + if (retval) + return retval; + if (!check_zero_block(block_buf, fs->blocksize)) + continue; + } +#ifdef PUNCH_DEBUG + printf("Freeing block %u (offset %d)\n", b, offset); +#endif + ext2fs_block_alloc_stats(fs, b, -1); + *p = 0; + freed++; + } +#ifdef PUNCH_DEBUG + printf("Freed %d blocks\n", freed); +#endif + return ext2fs_iblk_sub_blocks(fs, inode, freed); +} + +static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode, + char *block_buf, blk_t start, blk_t count) +{ + errcode_t retval; + char *buf = 0; + int level; + int num = EXT2_NDIR_BLOCKS; + blk_t *bp = inode->i_block; + blk_t addr_per_block; + blk_t max = EXT2_NDIR_BLOCKS; + + if (!block_buf) { + retval = ext2fs_get_array(3, fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + addr_per_block = (blk_t) fs->blocksize >> 2; + + for (level=0; level < 4; level++, max *= addr_per_block) { +#ifdef PUNCH_DEBUG + printf("Main loop level %d, start %u count %u " + "max %d num %d\n", level, start, count, max, num); +#endif + if (start < max) { + retval = ind_punch(fs, inode, block_buf, bp, level, + start, count, num); + if (retval) + goto errout; + if (count > max) + count -= max - start; + else + break; + start = 0; + } else + start -= max; + bp += num; + if (level == 0) { + num = 1; + max = 1; + } + } + retval = 0; +errout: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +#ifdef PUNCH_DEBUG + +#define dbg_printf(f, a...) printf(f, ## a) + +static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) +{ + if (desc) + printf("%s: ", desc); + printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", + extent->e_lblk, extent->e_lblk + extent->e_len - 1, + extent->e_len, extent->e_pblk); + if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) + fputs("LEAF ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fputs("UNINIT ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + fputs("2ND_VISIT ", stdout); + if (!extent->e_flags) + fputs("(none)", stdout); + fputc('\n', stdout); + +} +#else +#define dbg_print_extent(desc, ex) do { } while (0) +#define dbg_printf(f, a...) do { } while (0) +#endif + +static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + blk64_t start, blk64_t end) +{ + ext2_extent_handle_t handle = 0; + struct ext2fs_extent extent; + errcode_t retval; + blk64_t free_start, next; + __u32 free_count, newlen; + int freed = 0; + + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + return retval; + ext2fs_extent_goto(handle, start); + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto errout; + while (1) { + dbg_print_extent("main loop", &extent); + next = extent.e_lblk + extent.e_len; + dbg_printf("start %llu, end %llu, next %llu\n", + (unsigned long long) start, + (unsigned long long) end, + (unsigned long long) next); + if (start <= extent.e_lblk) { + if (end < extent.e_lblk) + goto next_extent; + dbg_printf("Case #1\n"); + /* Start of deleted region before extent; + adjust beginning of extent */ + free_start = extent.e_pblk; + if (next > end) + free_count = end - extent.e_lblk + 1; + else + free_count = extent.e_len; + extent.e_len -= free_count; + extent.e_lblk += free_count; + extent.e_pblk += free_count; + } else if (end >= next-1) { + if (start >= next) + break; + /* End of deleted region after extent; + adjust end of extent */ + dbg_printf("Case #2\n"); + newlen = start - extent.e_lblk; + free_start = extent.e_pblk + newlen; + free_count = extent.e_len - newlen; + extent.e_len = newlen; + } else { + struct ext2fs_extent newex; + + dbg_printf("Case #3\n"); + /* The hard case; we need to split the extent */ + newex.e_pblk = extent.e_pblk + + (end + 1 - extent.e_lblk); + newex.e_lblk = end + 1; + newex.e_len = next - end - 1; + newex.e_flags = extent.e_flags; + + extent.e_len = start - extent.e_lblk; + free_start = extent.e_pblk + extent.e_len; + free_count = end - start + 1; + + dbg_print_extent("inserting", &newex); + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newex); + if (retval) + goto errout; + /* Now pointing at inserted extent; so go back */ + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, + &newex); + if (retval) + goto errout; + } + if (extent.e_len) { + dbg_print_extent("replacing", &extent); + retval = ext2fs_extent_replace(handle, 0, &extent); + } else { + dbg_printf("deleting current extent\n"); + retval = ext2fs_extent_delete(handle, 0); + } + if (retval) + goto errout; + dbg_printf("Free start %llu, free count = %u\n", + free_start, free_count); + while (free_count-- > 0) { + ext2fs_block_alloc_stats(fs, free_start++, -1); + freed++; + } + next_extent: + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + break; + if (retval) + goto errout; + } + dbg_printf("Freed %d blocks\n", freed); + retval = ext2fs_iblk_sub_blocks(fs, inode, freed); +errout: + ext2fs_extent_free(handle); + return retval; +} + +/* + * Deallocate all logical blocks starting at start to end, inclusive. + * If end is ~0, then this is effectively truncate. + */ +extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, blk64_t start, + blk64_t end) +{ + errcode_t retval; + struct ext2_inode inode_buf; + + if (start > end) + return EINVAL; + + if (start == end) + return 0; + + /* Read inode structure if necessary */ + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + if (inode->i_flags & EXT4_EXTENTS_FL) + retval = ext2fs_punch_extent(fs, ino, inode, start, end); + else { + blk_t count; + + if (start > ~0U) + return 0; + count = ((end - start) < ~0U) ? (end - start) : ~0U; + retval = ext2fs_punch_ind(fs, inode, block_buf, + (blk_t) start, count); + } + if (retval) + return retval; + + return ext2fs_write_inode(fs, ino, inode); +} diff --git a/lib/libext2fs/source/read_bb.c b/lib/libext2fs/source/read_bb.c new file mode 100644 index 0000000..e5d6322 --- /dev/null +++ b/lib/libext2fs/source/read_bb.c @@ -0,0 +1,102 @@ +/* + * read_bb --- read the bad blocks inode + * + * Copyright (C) 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct read_bb_record { + ext2_badblocks_list bb_list; + errcode_t err; +}; + +/* + * Helper function for ext2fs_read_bb_inode() + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct read_bb_record *rb = (struct read_bb_record *) priv_data; + + if (blockcnt < 0) + return 0; + + if ((*block_nr < fs->super->s_first_data_block) || + (*block_nr >= ext2fs_blocks_count(fs->super))) + return 0; /* Ignore illegal blocks */ + + rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); + if (rb->err) + return BLOCK_ABORT; + return 0; +} + +/* + * Reads the current bad blocks from the bad blocks inode. + */ +errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) +{ + errcode_t retval; + struct read_bb_record rb; + struct ext2_inode inode; + blk_t numblocks; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + return retval; + numblocks = inode.i_blocks; + if (!((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + (inode.i_flags & EXT4_HUGE_FILE_FL))) + numblocks = numblocks / (fs->blocksize / 512); + numblocks += 20; + if (numblocks < 50) + numblocks = 50; + if (numblocks > 50000) + numblocks = 500; + retval = ext2fs_badblocks_list_create(bb_list, numblocks); + if (retval) + return retval; + } + + rb.bb_list = *bb_list; + rb.err = 0; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_READ_ONLY, + 0, mark_bad_block, &rb); + if (retval) + return retval; + + return rb.err; +} + + diff --git a/lib/libext2fs/source/read_bb_file.c b/lib/libext2fs/source/read_bb_file.c new file mode 100644 index 0000000..25454a8 --- /dev/null +++ b/lib/libext2fs/source/read_bb_file.c @@ -0,0 +1,105 @@ +/* + * read_bb_file.c --- read a list of bad blocks from a FILE * + * + * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)) +{ + errcode_t retval; + blk_t blockno; + int count; + char buf[128]; + + if (fs) + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_badblocks_list_create(bb_list, 10); + if (retval) + return retval; + } + + while (!feof (f)) { + if (fgets(buf, sizeof(buf), f) == NULL) + break; + count = sscanf(buf, "%u", &blockno); + if (count <= 0) + continue; + if (fs && + ((blockno < fs->super->s_first_data_block) || + (blockno >= ext2fs_blocks_count(fs->super)))) { + if (invalid) + (invalid)(fs, blockno, buf, priv_data); + continue; + } + retval = ext2fs_badblocks_list_add(*bb_list, blockno); + if (retval) + return retval; + } + return 0; +} + +struct compat_struct { + void (*invalid)(ext2_filsys, blk_t); +}; + +static void call_compat_invalid(ext2_filsys fs, blk_t blk, + char *badstr EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct compat_struct *st; + + st = (struct compat_struct *) priv_data; + if (st->invalid) + (st->invalid)(fs, blk); +} + + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, blk_t blk)) +{ + struct compat_struct st; + + st.invalid = invalid; + + return ext2fs_read_bb_FILE2(fs, f, bb_list, &st, + call_compat_invalid); +} + + diff --git a/lib/libext2fs/source/res_gdt.c b/lib/libext2fs/source/res_gdt.c new file mode 100644 index 0000000..0d98842 --- /dev/null +++ b/lib/libext2fs/source/res_gdt.c @@ -0,0 +1,220 @@ +/* + * res_gdt.c --- reserve blocks for growing the group descriptor table + * during online resizing. + * + * Copyright (C) 2002 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +static unsigned int list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} + +/* + * This code assumes that the reserved blocks have already been marked in-use + * during ext2fs_initialize(), so that they are not allocated for other + * uses before we can add them to the resize inode (which has to come + * after the creation of the inode table). + */ +errcode_t ext2fs_create_resize_inode(ext2_filsys fs) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf = 0, *gdt_buf = 0; + unsigned long long apb, inode_size; + /* FIXME-64 - can't deal with extents */ + blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; + int dindir_dirty = 0, inode_dirty = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + + retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf); + if (retval) + goto out_free; + gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); + + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) + goto out_free; + + /* Maximum possible file size (we donly use the dindirect blocks) */ + apb = EXT2_ADDR_PER_BLOCK(sb); + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { +#ifdef RES_GDT_DEBUG + printf("reading GDT dindir %u\n", dindir_blk); +#endif + retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); + if (retval) + goto out_inode; + } else { + blk_t goal = sb->s_first_data_block + fs->desc_blocks + + sb->s_reserved_gdt_blocks + 2 + + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + ext2fs_iblk_set(fs, &inode, 1); + memset(dindir_buf, 0, fs->blocksize); +#ifdef RES_GDT_DEBUG + printf("allocated GDT dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; + if(inode.i_size_high) { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + } + inode.i_ctime = fs->now ? fs->now : time(0); + } + + for (rsv_off = 0, gdt_off = fs->desc_blocks, + gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; + rsv_off < sb->s_reserved_gdt_blocks; + rsv_off++, gdt_off++, gdt_blk++) { + unsigned int three = 1, five = 5, seven = 7; + unsigned int grp, last = 0; + int gdt_dirty = 0; + + gdt_off %= apb; + if (!dindir_buf[gdt_off]) { + /* FIXME XXX XXX + blk_t new_blk; + + retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); + if (retval) + goto out_free; + if (new_blk != gdt_blk) { + // XXX free block + retval = -1; // XXX + } + */ + gdt_dirty = dindir_dirty = inode_dirty = 1; + memset(gdt_buf, 0, fs->blocksize); + dindir_buf[gdt_off] = gdt_blk; + ext2fs_iblk_add_blocks(fs, &inode, 1); +#ifdef RES_GDT_DEBUG + printf("added primary GDT block %u at %u[%u]\n", + gdt_blk, dindir_blk, gdt_off); +#endif + } else if (dindir_buf[gdt_off] == gdt_blk) { +#ifdef RES_GDT_DEBUG + printf("reading primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } else { +#ifdef RES_GDT_DEBUG + printf("bad primary GDT %u != %u at %u[%u]\n", + dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + + while ((grp = list_backups(fs, &three, &five, &seven)) < + fs->group_desc_count) { + blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; + + if (!gdt_buf[last]) { +#ifdef RES_GDT_DEBUG + printf("added backup GDT %u grp %u@%u[%u]\n", + expect, grp, gdt_blk, last); +#endif + gdt_buf[last] = expect; + ext2fs_iblk_add_blocks(fs, &inode, 1); + gdt_dirty = inode_dirty = 1; + } else if (gdt_buf[last] != expect) { +#ifdef RES_GDT_DEBUG + printf("bad backup GDT %u != %u at %u[%u]\n", + gdt_buf[last], expect, gdt_blk, last); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + last++; + } + if (gdt_dirty) { +#ifdef RES_GDT_DEBUG + printf("writing primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } + } + +out_dindir: + if (dindir_dirty) { + retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: +#ifdef RES_GDT_DEBUG + printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, + inode.i_size); +#endif + if (inode_dirty) { + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode); + if (!retval) + retval = retval2; + } +out_free: + ext2fs_free_mem(&dindir_buf); + return retval; +} + diff --git a/lib/libext2fs/source/rw_bitmaps.c b/lib/libext2fs/source/rw_bitmaps.c new file mode 100644 index 0000000..7b74b42 --- /dev/null +++ b/lib/libext2fs/source/rw_bitmaps.c @@ -0,0 +1,351 @@ +/* + * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. + * + * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "e2image.h" + +static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) +{ + dgrp_t i; + unsigned int j; + int block_nbytes, inode_nbytes; + unsigned int nbits; + errcode_t retval; + char *block_buf = 0, *inode_buf = 0; + int csum_flag = 0; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + ext2_ino_t ino_itr = 1; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + csum_flag = 1; + + inode_nbytes = block_nbytes = 0; + if (do_block) { + block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, + &block_buf); + if (retval) + return retval; + memset(block_buf, 0xff, fs->blocksize); + } + if (do_inode) { + inode_nbytes = (size_t) + ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); + retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, + &inode_buf); + if (retval) + return retval; + memset(inode_buf, 0xff, fs->blocksize); + } + + for (i = 0; i < fs->group_desc_count; i++) { + if (!do_block) + goto skip_block_bitmap; + + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) + ) + goto skip_this_block_bitmap; + + retval = ext2fs_get_block_bitmap_range2(fs->block_map, + blk_itr, block_nbytes << 3, block_buf); + if (retval) + return retval; + + if (i == fs->group_desc_count - 1) { + /* Force bitmap padding for the last group */ + nbits = ((ext2fs_blocks_count(fs->super) + - (__u64) fs->super->s_first_data_block) + % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super)); + if (nbits) + for (j = nbits; j < fs->blocksize * 8; j++) + ext2fs_set_bit(j, block_buf); + } + blk = ext2fs_block_bitmap_loc(fs, i); + if (blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + block_buf); + if (retval) + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + skip_this_block_bitmap: + blk_itr += block_nbytes << 3; + skip_block_bitmap: + + if (!do_inode) + continue; + + if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) + ) + goto skip_this_inode_bitmap; + + retval = ext2fs_get_inode_bitmap_range2(fs->inode_map, + ino_itr, inode_nbytes << 3, inode_buf); + if (retval) + return retval; + + blk = ext2fs_inode_bitmap_loc(fs, i); + if (blk) { + retval = io_channel_write_blk64(fs->io, blk, 1, + inode_buf); + if (retval) + return EXT2_ET_INODE_BITMAP_WRITE; + } + skip_this_inode_bitmap: + ino_itr += inode_nbytes << 3; + + } + if (do_block) { + fs->flags &= ~EXT2_FLAG_BB_DIRTY; + ext2fs_free_mem(&block_buf); + } + if (do_inode) { + fs->flags &= ~EXT2_FLAG_IB_DIRTY; + ext2fs_free_mem(&inode_buf); + } + return 0; +} + +static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) +{ + dgrp_t i; + char *block_bitmap = 0, *inode_bitmap = 0; + char *buf; + errcode_t retval; + int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; + int csum_flag = 0; + int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE; + unsigned int cnt; + blk64_t blk; + blk64_t blk_itr = fs->super->s_first_data_block; + blk64_t blk_cnt; + ext2_ino_t ino_itr = 1; + ext2_ino_t ino_cnt; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + csum_flag = 1; + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + return retval; + if (do_block) { + if (fs->block_map) + ext2fs_free_block_bitmap(fs->block_map); + strcpy(buf, "block bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + if (do_image) + retval = ext2fs_get_mem(fs->blocksize, &block_bitmap); + else + retval = ext2fs_get_memalign((unsigned) block_nbytes, + fs->blocksize, + &block_bitmap); + + if (retval) + goto cleanup; + } else + block_nbytes = 0; + if (do_inode) { + if (fs->inode_map) + ext2fs_free_inode_bitmap(fs->inode_map); + strcpy(buf, "inode bitmap for "); + strcat(buf, fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + retval = ext2fs_get_mem(do_image ? fs->blocksize : + (unsigned) inode_nbytes, &inode_bitmap); + if (retval) + goto cleanup; + } else + inode_nbytes = 0; + ext2fs_free_mem(&buf); + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + blk = (fs->image_header->offset_inodemap / fs->blocksize); + ino_cnt = fs->super->s_inodes_count; + while (inode_nbytes > 0) { + retval = io_channel_read_blk64(fs->image_io, blk++, + 1, inode_bitmap); + if (retval) + goto cleanup; + cnt = fs->blocksize << 3; + if (cnt > ino_cnt) + cnt = ino_cnt; + retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, + ino_itr, cnt, inode_bitmap); + if (retval) + goto cleanup; + ino_itr += fs->blocksize << 3; + ino_cnt -= fs->blocksize << 3; + inode_nbytes -= fs->blocksize; + } + blk = (fs->image_header->offset_blockmap / + fs->blocksize); + blk_cnt = (blk64_t)EXT2_BLOCKS_PER_GROUP(fs->super) * + fs->group_desc_count; + while (block_nbytes > 0) { + retval = io_channel_read_blk64(fs->image_io, blk++, + 1, block_bitmap); + if (retval) + goto cleanup; + cnt = fs->blocksize << 3; + if (cnt > blk_cnt) + cnt = blk_cnt; + retval = ext2fs_set_block_bitmap_range2(fs->block_map, + blk_itr, cnt, block_bitmap); + if (retval) + goto cleanup; + blk_itr += fs->blocksize << 3; + blk_cnt -= fs->blocksize << 3; + block_nbytes -= fs->blocksize; + } + goto success_cleanup; + } + + for (i = 0; i < fs->group_desc_count; i++) { + if (block_bitmap) { + blk = ext2fs_block_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -block_nbytes, block_bitmap); + if (retval) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } + } else + memset(block_bitmap, 0, block_nbytes); + cnt = block_nbytes << 3; + retval = ext2fs_set_block_bitmap_range2(fs->block_map, + blk_itr, cnt, block_bitmap); + if (retval) + goto cleanup; + blk_itr += block_nbytes << 3; + } + if (inode_bitmap) { + blk = ext2fs_inode_bitmap_loc(fs, i); + if (csum_flag && + ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && + ext2fs_group_desc_csum_verify(fs, i)) + blk = 0; + if (blk) { + retval = io_channel_read_blk64(fs->io, blk, + -inode_nbytes, inode_bitmap); + if (retval) { + retval = EXT2_ET_INODE_BITMAP_READ; + goto cleanup; + } + } else + memset(inode_bitmap, 0, inode_nbytes); + cnt = inode_nbytes << 3; + retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, + ino_itr, cnt, inode_bitmap); + if (retval) + goto cleanup; + ino_itr += inode_nbytes << 3; + } + } +success_cleanup: + if (inode_bitmap) + ext2fs_free_mem(&inode_bitmap); + if (block_bitmap) + ext2fs_free_mem(&block_bitmap); + return 0; + +cleanup: + if (do_block) { + ext2fs_free_mem(&fs->block_map); + fs->block_map = 0; + } + if (do_inode) { + ext2fs_free_mem(&fs->inode_map); + fs->inode_map = 0; + } + if (inode_bitmap) + ext2fs_free_mem(&inode_bitmap); + if (block_bitmap) + ext2fs_free_mem(&block_bitmap); + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) +{ + return read_bitmaps(fs, 1, 0); +} + +errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) +{ + return read_bitmaps(fs, 0, 1); +} + +errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) +{ + return write_bitmaps(fs, 1, 0); +} + +errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) +{ + return write_bitmaps(fs, 0, 1); +} + +errcode_t ext2fs_read_bitmaps(ext2_filsys fs) +{ + if (fs->inode_map && fs->block_map) + return 0; + + return read_bitmaps(fs, !fs->inode_map, !fs->block_map); +} + +errcode_t ext2fs_write_bitmaps(ext2_filsys fs) +{ + int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); + int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); + + if (!do_inode && !do_block) + return 0; + + return write_bitmaps(fs, do_inode, do_block); +} diff --git a/lib/libext2fs/source/sparse.c b/lib/libext2fs/source/sparse.c new file mode 100644 index 0000000..15ded0a --- /dev/null +++ b/lib/libext2fs/source/sparse.c @@ -0,0 +1,52 @@ +/* + * sparse.c --- find the groups in an ext2 filesystem with metadata backups + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * Copyright (C) 2002 Andreas Dilger. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} diff --git a/lib/libext2fs/source/swapfs.c b/lib/libext2fs/source/swapfs.c new file mode 100644 index 0000000..b856a09 --- /dev/null +++ b/lib/libext2fs/source/swapfs.c @@ -0,0 +1,315 @@ +/* + * swapfs.c --- swap ext2 filesystem data structures + * + * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_ext_attr.h" + +#ifdef WORDS_BIGENDIAN +void ext2fs_swap_super(struct ext2_super_block * sb) +{ + int i; + sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); + sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); + sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); + sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); + sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); + sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); + sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); + sb->s_log_cluster_size = ext2fs_swab32(sb->s_log_cluster_size); + sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); + sb->s_clusters_per_group = ext2fs_swab32(sb->s_clusters_per_group); + sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); + sb->s_mtime = ext2fs_swab32(sb->s_mtime); + sb->s_wtime = ext2fs_swab32(sb->s_wtime); + sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); + sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); + sb->s_magic = ext2fs_swab16(sb->s_magic); + sb->s_state = ext2fs_swab16(sb->s_state); + sb->s_errors = ext2fs_swab16(sb->s_errors); + sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); + sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); + sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); + sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); + sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); + sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); + sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); + sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); + sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); + sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); + sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); + sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); + sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); + sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); + sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); + sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); + sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); + sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); + sb->s_desc_size = ext2fs_swab16(sb->s_desc_size); + sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); + sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); + sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); + sb->s_blocks_count_hi = ext2fs_swab32(sb->s_blocks_count_hi); + sb->s_r_blocks_count_hi = ext2fs_swab32(sb->s_r_blocks_count_hi); + sb->s_free_blocks_hi = ext2fs_swab32(sb->s_free_blocks_hi); + sb->s_min_extra_isize = ext2fs_swab16(sb->s_min_extra_isize); + sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize); + sb->s_flags = ext2fs_swab32(sb->s_flags); + sb->s_kbytes_written = ext2fs_swab64(sb->s_kbytes_written); + sb->s_snapshot_inum = ext2fs_swab32(sb->s_snapshot_inum); + sb->s_snapshot_id = ext2fs_swab32(sb->s_snapshot_id); + sb->s_snapshot_r_blocks_count = + ext2fs_swab64(sb->s_snapshot_r_blocks_count); + sb->s_snapshot_list = ext2fs_swab32(sb->s_snapshot_list); + sb->s_usr_quota_inum = ext2fs_swab32(sb->s_usr_quota_inum); + sb->s_grp_quota_inum = ext2fs_swab32(sb->s_grp_quota_inum); + + for (i=0; i < 4; i++) + sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); + + /* if journal backup is for a valid extent-based journal... */ + if (!ext2fs_extent_header_verify(sb->s_jnl_blocks, + sizeof(sb->s_jnl_blocks))) { + /* ... swap only the journal i_size */ + sb->s_jnl_blocks[16] = ext2fs_swab32(sb->s_jnl_blocks[16]); + /* and the extent data is not swapped on read */ + return; + } + + /* direct/indirect journal: swap it all */ + for (i=0; i < 17; i++) + sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); +} + +void ext2fs_swap_group_desc2(ext2_filsys fs, struct ext2_group_desc *gdp) +{ + /* Do the 32-bit parts first */ + gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); + gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); + gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); + gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); + gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); + gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); + gdp->bg_flags = ext2fs_swab16(gdp->bg_flags); + gdp->bg_itable_unused = ext2fs_swab16(gdp->bg_itable_unused); + gdp->bg_checksum = ext2fs_swab16(gdp->bg_checksum); + /* If we're 32-bit, we're done */ + if (fs && (!fs->super->s_desc_size || + (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT))) + return; + + /* Swap the 64-bit parts */ + struct ext4_group_desc *gdp4 = (struct ext4_group_desc *) gdp; + gdp4->bg_block_bitmap_hi = ext2fs_swab32(gdp4->bg_block_bitmap_hi); + gdp4->bg_inode_bitmap_hi = ext2fs_swab32(gdp4->bg_inode_bitmap_hi); + gdp4->bg_inode_table_hi = ext2fs_swab32(gdp4->bg_inode_table_hi); + gdp4->bg_free_blocks_count_hi = + ext2fs_swab16(gdp4->bg_free_blocks_count_hi); + gdp4->bg_free_inodes_count_hi = + ext2fs_swab16(gdp4->bg_free_inodes_count_hi); + gdp4->bg_used_dirs_count_hi = + ext2fs_swab16(gdp4->bg_used_dirs_count_hi); + gdp4->bg_itable_unused_hi = ext2fs_swab16(gdp4->bg_itable_unused_hi); +} + +void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) +{ + return ext2fs_swap_group_desc2(0, gdp); +} + + +void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, + struct ext2_ext_attr_header *from_header) +{ + int n; + + to_header->h_magic = ext2fs_swab32(from_header->h_magic); + to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); + to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); + to_header->h_hash = ext2fs_swab32(from_header->h_hash); + for (n = 0; n < 4; n++) + to_header->h_reserved[n] = + ext2fs_swab32(from_header->h_reserved[n]); +} + +void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, + struct ext2_ext_attr_entry *from_entry) +{ + to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs); + to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block); + to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size); + to_entry->e_hash = ext2fs_swab32(from_entry->e_hash); +} + +void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) +{ + struct ext2_ext_attr_header *from_header = + (struct ext2_ext_attr_header *)from; + struct ext2_ext_attr_header *to_header = + (struct ext2_ext_attr_header *)to; + struct ext2_ext_attr_entry *from_entry, *to_entry; + char *from_end = (char *)from_header + bufsize; + + if (to_header != from_header) + memcpy(to_header, from_header, bufsize); + + if (has_header) { + ext2fs_swap_ext_attr_header(to_header, from_header); + + from_entry = (struct ext2_ext_attr_entry *)(from_header+1); + to_entry = (struct ext2_ext_attr_entry *)(to_header+1); + } else { + from_entry = (struct ext2_ext_attr_entry *)from_header; + to_entry = (struct ext2_ext_attr_entry *)to_header; + } + + while ((char *)from_entry < from_end && *(__u32 *)from_entry) { + ext2fs_swap_ext_attr_entry(to_entry, from_entry); + from_entry = EXT2_EXT_ATTR_NEXT(from_entry); + to_entry = EXT2_EXT_ATTR_NEXT(to_entry); + } +} + +void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize) +{ + unsigned i, has_data_blocks = 0, extra_isize = 0, attr_magic = 0; + int has_extents = 0; + int islnk = 0; + __u32 *eaf, *eat; + + if (hostorder && LINUX_S_ISLNK(f->i_mode)) + islnk = 1; + t->i_mode = ext2fs_swab16(f->i_mode); + if (!hostorder && LINUX_S_ISLNK(t->i_mode)) + islnk = 1; + t->i_uid = ext2fs_swab16(f->i_uid); + t->i_size = ext2fs_swab32(f->i_size); + t->i_atime = ext2fs_swab32(f->i_atime); + t->i_ctime = ext2fs_swab32(f->i_ctime); + t->i_mtime = ext2fs_swab32(f->i_mtime); + t->i_dtime = ext2fs_swab32(f->i_dtime); + t->i_gid = ext2fs_swab16(f->i_gid); + t->i_links_count = ext2fs_swab16(f->i_links_count); + t->i_file_acl = ext2fs_swab32(f->i_file_acl); + if (hostorder) + has_data_blocks = ext2fs_inode_data_blocks(fs, + (struct ext2_inode *) f); + t->i_blocks = ext2fs_swab32(f->i_blocks); + if (!hostorder) + has_data_blocks = ext2fs_inode_data_blocks(fs, + (struct ext2_inode *) t); + if (hostorder && (f->i_flags & EXT4_EXTENTS_FL)) + has_extents = 1; + t->i_flags = ext2fs_swab32(f->i_flags); + if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL)) + has_extents = 1; + t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); + /* extent data are swapped on access, not here */ + if (!has_extents && (!islnk || has_data_blocks)) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = ext2fs_swab32(f->i_block[i]); + } else if (t != f) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = f->i_block[i]; + } + t->i_generation = ext2fs_swab32(f->i_generation); + t->i_faddr = ext2fs_swab32(f->i_faddr); + + switch (fs->super->s_creator_os) { + case EXT2_OS_LINUX: + t->osd1.linux1.l_i_version = + ext2fs_swab32(f->osd1.linux1.l_i_version); + t->osd2.linux2.l_i_blocks_hi = + ext2fs_swab16(f->osd2.linux2.l_i_blocks_hi); + t->osd2.linux2.l_i_file_acl_high = + ext2fs_swab16(f->osd2.linux2.l_i_file_acl_high); + t->osd2.linux2.l_i_uid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); + t->osd2.linux2.l_i_gid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); + t->osd2.linux2.l_i_reserved2 = + ext2fs_swab32(f->osd2.linux2.l_i_reserved2); + break; + case EXT2_OS_HURD: + t->osd1.hurd1.h_i_translator = + ext2fs_swab32 (f->osd1.hurd1.h_i_translator); + t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; + t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; + t->osd2.hurd2.h_i_mode_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); + t->osd2.hurd2.h_i_uid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); + t->osd2.hurd2.h_i_gid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); + t->osd2.hurd2.h_i_author = + ext2fs_swab32 (f->osd2.hurd2.h_i_author); + break; + default: + break; + } + + if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) + return; /* no i_extra_isize field */ + + if (hostorder) + extra_isize = f->i_extra_isize; + t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); + if (!hostorder) + extra_isize = t->i_extra_isize; + if (extra_isize > EXT2_INODE_SIZE(fs->super) - + sizeof(struct ext2_inode)) { + /* this is error case: i_extra_size is too large */ + return; + } + + i = sizeof(struct ext2_inode) + extra_isize + sizeof(__u32); + if (bufsize < (int) i) + return; /* no space for EA magic */ + + eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + + extra_isize); + + attr_magic = *eaf; + if (!hostorder) + attr_magic = ext2fs_swab32(attr_magic); + + if (attr_magic != EXT2_EXT_ATTR_MAGIC) + return; /* it seems no magic here */ + + eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + + extra_isize); + *eat = ext2fs_swab32(*eaf); + + /* convert EA(s) */ + ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), + bufsize - sizeof(struct ext2_inode) - + extra_isize - sizeof(__u32), 0); + +} + +void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, + struct ext2_inode *f, int hostorder) +{ + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, + (struct ext2_inode_large *) f, hostorder, + sizeof(struct ext2_inode)); +} + +#endif diff --git a/lib/libext2fs/source/tdb.c b/lib/libext2fs/source/tdb.c new file mode 100644 index 0000000..0550f46 --- /dev/null +++ b/lib/libext2fs/source/tdb.c @@ -0,0 +1,4145 @@ +/* +URL: svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb/common +Rev: 23590 +Last Changed Date: 2007-06-22 13:36:10 -0400 (Fri, 22 Jun 2007) +*/ + /* + trivial database library - standalone version + + Copyright (C) Andrew Tridgell 1999-2005 + Copyright (C) Jeremy Allison 2000-2006 + Copyright (C) Paul `Rusty' Russell 2000 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef CONFIG_STAND_ALONE +#define HAVE_MMAP +#define HAVE_STRDUP +#define HAVE_SYS_MMAN_H +#define HAVE_UTIME_H +#define HAVE_UTIME +#endif +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +#ifndef HAVE_STRDUP +#define strdup rep_strdup +static char *rep_strdup(const char *s) +{ + char *ret; + int length; + if (!s) + return NULL; + + if (!length) + length = strlen(s); + + ret = malloc(length + 1); + if (ret) { + strncpy(ret, s, length); + ret[length] = '\0'; + } + return ret; +} +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 ) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +typedef int bool; + +#include "tdb.h" + +#ifndef u32 +#define u32 unsigned +#endif + +#ifndef HAVE_GETPAGESIZE +#define getpagesize() 0x2000 +#endif + +typedef u32 tdb_len_t; +typedef u32 tdb_off_t; + +#ifndef offsetof +#define offsetof(t,f) ((unsigned int)&((t *)0)->f) +#endif + +#define TDB_MAGIC_FOOD "TDB file\n" +#define TDB_VERSION (0x26011967 + 6) +#define TDB_MAGIC (0x26011999U) +#define TDB_FREE_MAGIC (~TDB_MAGIC) +#define TDB_DEAD_MAGIC (0xFEE1DEAD) +#define TDB_RECOVERY_MAGIC (0xf53bc0e7U) +#define TDB_ALIGNMENT 4 +#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) +#define DEFAULT_HASH_SIZE 131 +#define FREELIST_TOP (sizeof(struct tdb_header)) +#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) +#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) +#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) +#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) +#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t)) +#define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t)) +#define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1) +#define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start) +#define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number) +#define TDB_PAD_BYTE 0x42 +#define TDB_PAD_U32 0x42424242 + +/* NB assumes there is a local variable called "tdb" that is the + * current context, also takes doubly-parenthesized print-style + * argument. */ +#define TDB_LOG(x) tdb->log.log_fn x + +/* lock offsets */ +#define GLOBAL_LOCK 0 +#define ACTIVE_LOCK 4 +#define TRANSACTION_LOCK 8 + +/* free memory if the pointer is valid and zero the pointer */ +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) +#endif + +#define BUCKET(hash) ((hash) % tdb->header.hash_size) + +#define DOCONV() (tdb->flags & TDB_CONVERT) +#define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x) + + +/* the body of the database is made of one list_struct for the free space + plus a separate data list for each hash value */ +struct list_struct { + tdb_off_t next; /* offset of the next record in the list */ + tdb_len_t rec_len; /* total byte length of record */ + tdb_len_t key_len; /* byte length of key */ + tdb_len_t data_len; /* byte length of data */ + u32 full_hash; /* the full 32 bit hash of the key */ + u32 magic; /* try to catch errors */ + /* the following union is implied: + union { + char record[rec_len]; + struct { + char key[key_len]; + char data[data_len]; + } + u32 totalsize; (tailer) + } + */ +}; + + +/* this is stored at the front of every database */ +struct tdb_header { + char magic_food[32]; /* for /etc/magic */ + u32 version; /* version of the code */ + u32 hash_size; /* number of hash entries */ + tdb_off_t rwlocks; /* obsolete - kept to detect old formats */ + tdb_off_t recovery_start; /* offset of transaction recovery region */ + tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */ + tdb_off_t reserved[29]; +}; + +struct tdb_lock_type { + int list; + u32 count; + u32 ltype; +}; + +struct tdb_traverse_lock { + struct tdb_traverse_lock *next; + u32 off; + u32 hash; + int lock_rw; +}; + + +struct tdb_methods { + int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int ); + int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t); + void (*next_hash_chain)(struct tdb_context *, u32 *); + int (*tdb_oob)(struct tdb_context *, tdb_off_t , int ); + int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t ); + int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int, size_t); +}; + +struct tdb_context { + char *name; /* the name of the database */ + void *map_ptr; /* where it is currently mapped */ + int fd; /* open file descriptor for the database */ + tdb_len_t map_size; /* how much space has been mapped */ + int read_only; /* opened read-only */ + int traverse_read; /* read-only traversal */ + struct tdb_lock_type global_lock; + int num_lockrecs; + struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */ + enum TDB_ERROR ecode; /* error code for last tdb error */ + struct tdb_header header; /* a cached copy of the header */ + u32 flags; /* the flags passed to tdb_open */ + struct tdb_traverse_lock travlocks; /* current traversal locks */ + struct tdb_context *next; /* all tdbs to avoid multiple opens */ + dev_t device; /* uniquely identifies this tdb */ + ino_t inode; /* uniquely identifies this tdb */ + struct tdb_logging_context log; + unsigned int (*hash_fn)(TDB_DATA *key); + int open_flags; /* flags used in the open - needed by reopen */ + unsigned int num_locks; /* number of chain locks held */ + const struct tdb_methods *methods; + struct tdb_transaction *transaction; + int page_size; + int max_dead_records; + bool have_transaction_lock; +}; + + +/* + internal prototypes +*/ +static int tdb_munmap(struct tdb_context *tdb); +static void tdb_mmap(struct tdb_context *tdb); +static int tdb_lock(struct tdb_context *tdb, int list, int ltype); +static int tdb_unlock(struct tdb_context *tdb, int list, int ltype); +static int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); +static int tdb_transaction_lock(struct tdb_context *tdb, int ltype); +static int tdb_transaction_unlock(struct tdb_context *tdb); +static int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); +static int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static void *tdb_convert(void *buf, u32 size); +static int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); +static unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); +static int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, + tdb_off_t offset, tdb_len_t len, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data); +static tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, + struct list_struct *rec); +static void tdb_io_init(struct tdb_context *tdb); +static int tdb_expand(struct tdb_context *tdb, tdb_off_t size); +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, + struct list_struct *rec); + + +/* file: error.c */ + +enum TDB_ERROR tdb_error(struct tdb_context *tdb) +{ + return tdb->ecode; +} + +static struct tdb_errname { + enum TDB_ERROR ecode; const char *estring; +} emap[] = { {TDB_SUCCESS, "Success"}, + {TDB_ERR_CORRUPT, "Corrupt database"}, + {TDB_ERR_IO, "IO Error"}, + {TDB_ERR_LOCK, "Locking error"}, + {TDB_ERR_OOM, "Out of memory"}, + {TDB_ERR_EXISTS, "Record exists"}, + {TDB_ERR_NOLOCK, "Lock exists on other keys"}, + {TDB_ERR_EINVAL, "Invalid parameter"}, + {TDB_ERR_NOEXIST, "Record does not exist"}, + {TDB_ERR_RDONLY, "write not permitted"} }; + +/* Error string for the last tdb error */ +const char *tdb_errorstr(struct tdb_context *tdb) +{ + u32 i; + for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) + if (tdb->ecode == emap[i].ecode) + return emap[i].estring; + return "Invalid error code"; +} + +/* file: lock.c */ + +#define TDB_MARK_LOCK 0x80000000 + +/* a byte range locking function - return 0 on success + this functions locks/unlocks 1 byte at the specified offset. + + On error, errno is also set so that errors are passed back properly + through tdb_open(). + + note that a len of zero means lock to end of file +*/ +int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, + int rw_type, int lck_type, int probe, size_t len) +{ + struct flock fl; + int ret; + + if (tdb->flags & TDB_NOLOCK) { + return 0; + } + + if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + fl.l_type = rw_type; + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = len; + fl.l_pid = 0; + + do { + ret = fcntl(tdb->fd,lck_type,&fl); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* Generic lock error. errno set by fcntl. + * EAGAIN is an expected return from non-blocking + * locks. */ + if (!probe && lck_type != F_SETLK) { + /* Ensure error code is set for log fun to examine. */ + tdb->ecode = TDB_ERR_LOCK; + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", + tdb->fd, offset, rw_type, lck_type, (int)len)); + } + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + return 0; +} + + +/* + upgrade a read lock to a write lock. This needs to be handled in a + special way as some OSes (such as solaris) have too conservative + deadlock detection and claim a deadlock when progress can be + made. For those OSes we may loop for a while. +*/ +int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len) +{ + int count = 1000; + while (count--) { + struct timeval tv; + if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { + return 0; + } + if (errno != EDEADLK) { + break; + } + /* sleep for as short a time as we can - more portable than usleep() */ + tv.tv_sec = 0; + tv.tv_usec = 1; +#ifdef HAVE_SYS_SELECT_H + select(0, NULL, NULL, NULL, &tv); +#endif + } + TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset)); + return -1; +} + + +/* lock a list in the database. list -1 is the alloc list */ +static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op) +{ + struct tdb_lock_type *new_lck; + int i; + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", + list, ltype)); + return -1; + } + if (tdb->flags & TDB_NOLOCK) + return 0; + + for (i=0; inum_lockrecs; i++) { + if (tdb->lockrecs[i].list == list) { + if (tdb->lockrecs[i].count == 0) { + /* + * Can't happen, see tdb_unlock(). It should + * be an assert. + */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: " + "lck->count == 0 for list %d", list)); + } + /* + * Just increment the in-memory struct, posix locks + * don't stack. + */ + tdb->lockrecs[i].count++; + return 0; + } + } + + new_lck = (struct tdb_lock_type *)realloc( + tdb->lockrecs, + sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1)); + if (new_lck == NULL) { + errno = ENOMEM; + return -1; + } + tdb->lockrecs = new_lck; + + /* Since fcntl locks don't nest, we do a lock for the first one, + and simply bump the count for future ones */ + if (!mark_lock && + tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op, + 0, 1)) { + return -1; + } + + tdb->num_locks++; + + tdb->lockrecs[tdb->num_lockrecs].list = list; + tdb->lockrecs[tdb->num_lockrecs].count = 1; + tdb->lockrecs[tdb->num_lockrecs].ltype = ltype; + tdb->num_lockrecs += 1; + + return 0; +} + +/* lock a list in the database. list -1 is the alloc list */ +int tdb_lock(struct tdb_context *tdb, int list, int ltype) +{ + int ret; + ret = _tdb_lock(tdb, list, ltype, F_SETLKW); + if (ret) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d " + "ltype=%d (%s)\n", list, ltype, strerror(errno))); + } + return ret; +} + +/* lock a list in the database. list -1 is the alloc list. non-blocking lock */ +int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype) +{ + return _tdb_lock(tdb, list, ltype, F_SETLK); +} + + +/* unlock the database: returns void because it's too late for errors. */ + /* changed to return int it may be interesting to know there + has been an error --simo */ +int tdb_unlock(struct tdb_context *tdb, int list, int ltype) +{ + int ret = -1; + int i; + struct tdb_lock_type *lck = NULL; + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* a global lock allows us to avoid per chain locks */ + if (tdb->global_lock.count && + (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { + return 0; + } + + if (tdb->global_lock.count) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->flags & TDB_NOLOCK) + return 0; + + /* Sanity checks */ + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); + return ret; + } + + for (i=0; inum_lockrecs; i++) { + if (tdb->lockrecs[i].list == list) { + lck = &tdb->lockrecs[i]; + break; + } + } + + if ((lck == NULL) || (lck->count == 0)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); + return -1; + } + + if (lck->count > 1) { + lck->count--; + return 0; + } + + /* + * This lock has count==1 left, so we need to unlock it in the + * kernel. We don't bother with decrementing the in-memory array + * element, we're about to overwrite it with the last array element + * anyway. + */ + + if (mark_lock) { + ret = 0; + } else { + ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, + F_SETLKW, 0, 1); + } + tdb->num_locks--; + + /* + * Shrink the array by overwriting the element just unlocked with the + * last array element. + */ + + if (tdb->num_lockrecs > 1) { + *lck = tdb->lockrecs[tdb->num_lockrecs-1]; + } + tdb->num_lockrecs -= 1; + + /* + * We don't bother with realloc when the array shrinks, but if we have + * a completely idle tdb we should get rid of the locked array. + */ + + if (tdb->num_lockrecs == 0) { + SAFE_FREE(tdb->lockrecs); + } + + if (ret) + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); + return ret; +} + +/* + get the transaction lock + */ +int tdb_transaction_lock(struct tdb_context *tdb, int ltype) +{ + if (tdb->have_transaction_lock || tdb->global_lock.count) { + return 0; + } + if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, + F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + tdb->have_transaction_lock = 1; + return 0; +} + +/* + release the transaction lock + */ +int tdb_transaction_unlock(struct tdb_context *tdb) +{ + int ret; + if (!tdb->have_transaction_lock) { + return 0; + } + ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); + if (ret == 0) { + tdb->have_transaction_lock = 0; + } + return ret; +} + + + + +/* lock/unlock entire database */ +static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) +{ + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* There are no locks on read-only dbs */ + if (tdb->read_only || tdb->traverse_read) + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + + if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) { + tdb->global_lock.count++; + return 0; + } + + if (tdb->global_lock.count) { + /* a global lock of a different type exists */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->num_locks != 0) { + /* can't combine global and chain locks */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (!mark_lock && + tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op, + 0, 4*tdb->header.hash_size)) { + if (op == F_SETLKW) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno))); + } + return -1; + } + + tdb->global_lock.count = 1; + tdb->global_lock.ltype = ltype; + + return 0; +} + + + +/* unlock entire db */ +static int _tdb_unlockall(struct tdb_context *tdb, int ltype) +{ + bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); + + ltype &= ~TDB_MARK_LOCK; + + /* There are no locks on read-only dbs */ + if (tdb->read_only || tdb->traverse_read) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) { + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + if (tdb->global_lock.count > 1) { + tdb->global_lock.count--; + return 0; + } + + if (!mark_lock && + tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, + 0, 4*tdb->header.hash_size)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno))); + return -1; + } + + tdb->global_lock.count = 0; + tdb->global_lock.ltype = 0; + + return 0; +} + +/* lock entire database with write lock */ +int tdb_lockall(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK, F_SETLKW); +} + +/* lock entire database with write lock - mark only */ +int tdb_lockall_mark(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW); +} + +/* unlock entire database with write lock - unmark only */ +int tdb_lockall_unmark(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK); +} + +/* lock entire database with write lock - nonblocking varient */ +int tdb_lockall_nonblock(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_WRLCK, F_SETLK); +} + +/* unlock entire database with write lock */ +int tdb_unlockall(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_WRLCK); +} + +/* lock entire database with read lock */ +int tdb_lockall_read(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_RDLCK, F_SETLKW); +} + +/* lock entire database with read lock - nonblock varient */ +int tdb_lockall_read_nonblock(struct tdb_context *tdb) +{ + return _tdb_lockall(tdb, F_RDLCK, F_SETLK); +} + +/* unlock entire database with read lock */ +int tdb_unlockall_read(struct tdb_context *tdb) +{ + return _tdb_unlockall(tdb, F_RDLCK); +} + +/* lock/unlock one hash chain. This is meant to be used to reduce + contention - it cannot guarantee how many records will be locked */ +int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +/* lock/unlock one hash chain, non-blocking. This is meant to be used + to reduce contention - it cannot guarantee how many records will be + locked */ +int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +/* mark a chain as locked without actually locking it. Warning! use with great caution! */ +int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); +} + +/* unmark a chain as locked without actually locking it. Warning! use with great caution! */ +int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); +} + +int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); +} + +int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); +} + +int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); +} + + + +/* record lock stops delete underneath */ +int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) +{ + return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0; +} + +/* + Write locks override our own fcntl readlocks, so check it here. + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ +int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off) +{ + struct tdb_traverse_lock *i; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + return -1; + return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1); +} + +/* + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ +int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off) +{ + return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1); +} + +/* fcntl locks don't stack: avoid unlocking someone else's */ +int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off) +{ + struct tdb_traverse_lock *i; + u32 count = 0; + + if (off == 0) + return 0; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + count++; + return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0); +} + +/* file: io.c */ + +/* check for an out of bounds access - if it is out of bounds then + see if the database has been expanded by someone else and expand + if necessary + note that "len" is the minimum length needed for the db +*/ +static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) +{ + struct stat st; + if (len <= tdb->map_size) + return 0; + if (tdb->flags & TDB_INTERNAL) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n", + (int)len, (int)tdb->map_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + if (fstat(tdb->fd, &st) == -1) { + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + if (st.st_size < (size_t)len) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n", + (int)len, (int)st.st_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + /* Unmap, update size, remap */ + if (tdb_munmap(tdb) == -1) + return TDB_ERRCODE(TDB_ERR_IO, -1); + tdb->map_size = st.st_size; + tdb_mmap(tdb); + return 0; +} + +/* write a lump of data at a specified offset */ +static int tdb_write(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) +{ + if (len == 0) { + return 0; + } + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) + return -1; + + if (tdb->map_ptr) { + memcpy(off + (char *)tdb->map_ptr, buf, len); + } else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n", + off, len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + return 0; +} + +/* Endian conversion: we only ever deal with 4 byte quantities */ +void *tdb_convert(void *buf, u32 size) +{ + u32 i, *p = (u32 *)buf; + for (i = 0; i < size / 4; i++) + p[i] = TDB_BYTEREV(p[i]); + return buf; +} + + +/* read a lump of data at a specified offset, maybe convert */ +static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, + tdb_len_t len, int cv) +{ + if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) { + return -1; + } + + if (tdb->map_ptr) { + memcpy(buf, off + (char *)tdb->map_ptr, len); + } else { + ssize_t ret = pread(tdb->fd, buf, len, off); + if (ret != (ssize_t)len) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d " + "len=%d ret=%d (%s) map_size=%d\n", + (int)off, (int)len, (int)ret, strerror(errno), + (int)tdb->map_size)); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + } + if (cv) { + tdb_convert(buf, len); + } + return 0; +} + + + +/* + do an unlocked scan of the hash table heads to find the next non-zero head. The value + will then be confirmed with the lock held +*/ +static void tdb_next_hash_chain(struct tdb_context *tdb, u32 *chain) +{ + u32 h = *chain; + if (tdb->map_ptr) { + for (;h < tdb->header.hash_size;h++) { + if (0 != *(u32 *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) { + break; + } + } + } else { + u32 off=0; + for (;h < tdb->header.hash_size;h++) { + if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) { + break; + } + } + } + (*chain) = h; +} + + +int tdb_munmap(struct tdb_context *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return 0; + +#ifdef HAVE_MMAP + if (tdb->map_ptr) { + int ret = munmap(tdb->map_ptr, tdb->map_size); + if (ret != 0) + return ret; + } +#endif + tdb->map_ptr = NULL; + return 0; +} + +void tdb_mmap(struct tdb_context *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return; + +#ifdef HAVE_MMAP + if (!(tdb->flags & TDB_NOMMAP)) { + tdb->map_ptr = mmap(NULL, tdb->map_size, + PROT_READ|(tdb->read_only? 0:PROT_WRITE), + MAP_SHARED|MAP_FILE, tdb->fd, 0); + + /* + * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! + */ + + if (tdb->map_ptr == MAP_FAILED) { + tdb->map_ptr = NULL; + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", + tdb->map_size, strerror(errno))); + } + } else { + tdb->map_ptr = NULL; + } +#else + tdb->map_ptr = NULL; +#endif +} + +/* expand a file. we prefer to use ftruncate, as that is what posix + says to use for mmap expansion */ +static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition) +{ + char buf[1024]; + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + if (ftruncate(tdb->fd, size+addition) == -1) { + char b = 0; + if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } + } + + /* now fill the file with something. This ensures that the + file isn't sparse, which would be very bad if we ran out of + disk. This must be done with write, not via mmap */ + memset(buf, TDB_PAD_BYTE, sizeof(buf)); + while (addition) { + int n = addition>sizeof(buf)?sizeof(buf):addition; + int ret = pwrite(tdb->fd, buf, n, size); + if (ret != n) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n", + n, strerror(errno))); + return -1; + } + addition -= n; + size += n; + } + return 0; +} + + +/* expand the database at least size bytes by expanding the underlying + file and doing the mmap again if necessary */ +int tdb_expand(struct tdb_context *tdb, tdb_off_t size) +{ + struct list_struct rec; + tdb_off_t offset; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n")); + return -1; + } + + /* must know about any previous expansions by another process */ + tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); + + /* always make room for at least 10 more records, and round + the database up to a multiple of the page size */ + size = TDB_ALIGN(tdb->map_size + size*10, tdb->page_size) - tdb->map_size; + + if (!(tdb->flags & TDB_INTERNAL)) + tdb_munmap(tdb); + + /* + * We must ensure the file is unmapped before doing this + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* expand the file itself */ + if (!(tdb->flags & TDB_INTERNAL)) { + if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0) + goto fail; + } + + tdb->map_size += size; + + if (tdb->flags & TDB_INTERNAL) { + char *new_map_ptr = (char *)realloc(tdb->map_ptr, + tdb->map_size); + if (!new_map_ptr) { + tdb->map_size -= size; + goto fail; + } + tdb->map_ptr = new_map_ptr; + } else { + /* + * We must ensure the file is remapped before adding the space + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* We're ok if the mmap fails as we'll fallback to read/write */ + tdb_mmap(tdb); + } + + /* form a new freelist record */ + memset(&rec,'\0',sizeof(rec)); + rec.rec_len = size - sizeof(rec); + + /* link it into the free list */ + offset = tdb->map_size - size; + if (tdb_free(tdb, offset, &rec) == -1) + goto fail; + + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + +/* read/write a tdb_off_t */ +int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) +{ + return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); +} + +int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) +{ + tdb_off_t off = *d; + return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); +} + + +/* read a lump of data, allocating the space for it */ +unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) +{ + unsigned char *buf; + + /* some systems don't like zero length malloc */ + if (len == 0) { + len = 1; + } + + if (!(buf = (unsigned char *)malloc(len))) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_OOM; + TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n", + len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_OOM, buf); + } + if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) { + SAFE_FREE(buf); + return NULL; + } + return buf; +} + +/* Give a piece of tdb data to a parser */ + +int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, + tdb_off_t offset, tdb_len_t len, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + TDB_DATA data; + int result; + + data.dsize = len; + + if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) { + /* + * Optimize by avoiding the malloc/memcpy/free, point the + * parser directly at the mmap area. + */ + if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) { + return -1; + } + data.dptr = offset + (unsigned char *)tdb->map_ptr; + return parser(key, data, private_data); + } + + if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) { + return -1; + } + + result = parser(key, data, private_data); + free(data.dptr); + return result; +} + +/* read/write a record */ +int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + if (TDB_BAD_MAGIC(rec)) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0); +} + +int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + struct list_struct r = *rec; + return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r)); +} + +static const struct tdb_methods io_methods = { + tdb_read, + tdb_write, + tdb_next_hash_chain, + tdb_oob, + tdb_expand_file, + tdb_brlock +}; + +/* + initialise the default methods table +*/ +void tdb_io_init(struct tdb_context *tdb) +{ + tdb->methods = &io_methods; +} + +/* file: transaction.c */ + +/* + transaction design: + + - only allow a single transaction at a time per database. This makes + using the transaction API simpler, as otherwise the caller would + have to cope with temporary failures in transactions that conflict + with other current transactions + + - keep the transaction recovery information in the same file as the + database, using a special 'transaction recovery' record pointed at + by the header. This removes the need for extra journal files as + used by some other databases + + - dynamically allocated the transaction recover record, re-using it + for subsequent transactions. If a larger record is needed then + tdb_free() the old record to place it on the normal tdb freelist + before allocating the new record + + - during transactions, keep a linked list of writes all that have + been performed by intercepting all tdb_write() calls. The hooked + transaction versions of tdb_read() and tdb_write() check this + linked list and try to use the elements of the list in preference + to the real database. + + - don't allow any locks to be held when a transaction starts, + otherwise we can end up with deadlock (plus lack of lock nesting + in posix locks would mean the lock is lost) + + - if the caller gains a lock during the transaction but doesn't + release it then fail the commit + + - allow for nested calls to tdb_transaction_start(), re-using the + existing transaction record. If the inner transaction is cancelled + then a subsequent commit will fail + + - keep a mirrored copy of the tdb hash chain heads to allow for the + fast hash heads scan on traverse, updating the mirrored copy in + the transaction version of tdb_write + + - allow callers to mix transaction and non-transaction use of tdb, + although once a transaction is started then an exclusive lock is + gained until the transaction is committed or cancelled + + - the commit stategy involves first saving away all modified data + into a linearised buffer in the transaction recovery area, then + marking the transaction recovery area with a magic value to + indicate a valid recovery record. In total 4 fsync/msync calls are + needed per commit to prevent race conditions. It might be possible + to reduce this to 3 or even 2 with some more work. + + - check for a valid recovery record on open of the tdb, while the + global lock is held. Automatically recover from the transaction + recovery area if needed, then continue with the open as + usual. This allows for smooth crash recovery with no administrator + intervention. + + - if TDB_NOSYNC is passed to flags in tdb_open then transactions are + still available, but no transaction recovery area is used and no + fsync/msync calls are made. + +*/ + +struct tdb_transaction_el { + struct tdb_transaction_el *next, *prev; + tdb_off_t offset; + tdb_len_t length; + unsigned char *data; +}; + +/* + hold the context of any current transaction +*/ +struct tdb_transaction { + /* we keep a mirrored copy of the tdb hash heads here so + tdb_next_hash_chain() can operate efficiently */ + u32 *hash_heads; + + /* the original io methods - used to do IOs to the real db */ + const struct tdb_methods *io_methods; + + /* the list of transaction elements. We use a doubly linked + list with a last pointer to allow us to keep the list + ordered, with first element at the front of the list. It + needs to be doubly linked as the read/write traversals need + to be backwards, while the commit needs to be forwards */ + struct tdb_transaction_el *elements, *elements_last; + + /* non-zero when an internal transaction error has + occurred. All write operations will then fail until the + transaction is ended */ + int transaction_error; + + /* when inside a transaction we need to keep track of any + nested tdb_transaction_start() calls, as these are allowed, + but don't create a new transaction */ + int nesting; + + /* old file size before transaction */ + tdb_len_t old_map_size; +}; + + +/* + read while in a transaction. We need to check first if the data is in our list + of transaction elements, then if not do a real read +*/ +static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, + tdb_len_t len, int cv) +{ + struct tdb_transaction_el *el; + + /* we need to walk the list backwards to get the most recent data */ + for (el=tdb->transaction->elements_last;el;el=el->prev) { + tdb_len_t partial; + + if (off+len <= el->offset) { + continue; + } + if (off >= el->offset + el->length) { + continue; + } + + /* an overlapping read - needs to be split into up to + 2 reads and a memcpy */ + if (off < el->offset) { + partial = el->offset - off; + if (transaction_read(tdb, off, buf, partial, cv) != 0) { + goto fail; + } + len -= partial; + off += partial; + buf = (void *)(partial + (char *)buf); + } + if (off + len <= el->offset + el->length) { + partial = len; + } else { + partial = el->offset + el->length - off; + } + memcpy(buf, el->data + (off - el->offset), partial); + if (cv) { + tdb_convert(buf, len); + } + len -= partial; + off += partial; + buf = (void *)(partial + (char *)buf); + + if (len != 0 && transaction_read(tdb, off, buf, len, cv) != 0) { + goto fail; + } + + return 0; + } + + /* its not in the transaction elements - do a real read */ + return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv); + +fail: + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len)); + tdb->ecode = TDB_ERR_IO; + tdb->transaction->transaction_error = 1; + return -1; +} + + +/* + write while in a transaction +*/ +static int transaction_write(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) +{ + struct tdb_transaction_el *el, *best_el=NULL; + + if (len == 0) { + return 0; + } + + /* if the write is to a hash head, then update the transaction + hash heads */ + if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP && + off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) { + u32 chain = (off-FREELIST_TOP) / sizeof(tdb_off_t); + memcpy(&tdb->transaction->hash_heads[chain], buf, len); + } + + /* first see if we can replace an existing entry */ + for (el=tdb->transaction->elements_last;el;el=el->prev) { + tdb_len_t partial; + + if (best_el == NULL && off == el->offset+el->length) { + best_el = el; + } + + if (off+len <= el->offset) { + continue; + } + if (off >= el->offset + el->length) { + continue; + } + + /* an overlapping write - needs to be split into up to + 2 writes and a memcpy */ + if (off < el->offset) { + partial = el->offset - off; + if (transaction_write(tdb, off, buf, partial) != 0) { + goto fail; + } + len -= partial; + off += partial; + buf = (const void *)(partial + (const char *)buf); + } + if (off + len <= el->offset + el->length) { + partial = len; + } else { + partial = el->offset + el->length - off; + } + memcpy(el->data + (off - el->offset), buf, partial); + len -= partial; + off += partial; + buf = (const void *)(partial + (const char *)buf); + + if (len != 0 && transaction_write(tdb, off, buf, len) != 0) { + goto fail; + } + + return 0; + } + + /* see if we can append the new entry to an existing entry */ + if (best_el && best_el->offset + best_el->length == off && + (off+len < tdb->transaction->old_map_size || + off > tdb->transaction->old_map_size)) { + unsigned char *data = best_el->data; + el = best_el; + el->data = (unsigned char *)realloc(el->data, + el->length + len); + if (el->data == NULL) { + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + el->data = data; + return -1; + } + if (buf) { + memcpy(el->data + el->length, buf, len); + } else { + memset(el->data + el->length, TDB_PAD_BYTE, len); + } + el->length += len; + return 0; + } + + /* add a new entry at the end of the list */ + el = (struct tdb_transaction_el *)malloc(sizeof(*el)); + if (el == NULL) { + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + return -1; + } + el->next = NULL; + el->prev = tdb->transaction->elements_last; + el->offset = off; + el->length = len; + el->data = (unsigned char *)malloc(len); + if (el->data == NULL) { + free(el); + tdb->ecode = TDB_ERR_OOM; + tdb->transaction->transaction_error = 1; + return -1; + } + if (buf) { + memcpy(el->data, buf, len); + } else { + memset(el->data, TDB_PAD_BYTE, len); + } + if (el->prev) { + el->prev->next = el; + } else { + tdb->transaction->elements = el; + } + tdb->transaction->elements_last = el; + return 0; + +fail: + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len)); + tdb->ecode = TDB_ERR_IO; + tdb->transaction->transaction_error = 1; + return -1; +} + +/* + accelerated hash chain head search, using the cached hash heads +*/ +static void transaction_next_hash_chain(struct tdb_context *tdb, u32 *chain) +{ + u32 h = *chain; + for (;h < tdb->header.hash_size;h++) { + /* the +1 takes account of the freelist */ + if (0 != tdb->transaction->hash_heads[h+1]) { + break; + } + } + (*chain) = h; +} + +/* + out of bounds check during a transaction +*/ +static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe) +{ + if (len <= tdb->map_size) { + return 0; + } + return TDB_ERRCODE(TDB_ERR_IO, -1); +} + +/* + transaction version of tdb_expand(). +*/ +static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, + tdb_off_t addition) +{ + /* add a write to the transaction elements, so subsequent + reads see the zero data */ + if (transaction_write(tdb, size, NULL, addition) != 0) { + return -1; + } + + return 0; +} + +/* + brlock during a transaction - ignore them +*/ +static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, + int rw_type, int lck_type, int probe, size_t len) +{ + return 0; +} + +static const struct tdb_methods transaction_methods = { + transaction_read, + transaction_write, + transaction_next_hash_chain, + transaction_oob, + transaction_expand_file, + transaction_brlock +}; + + +/* + start a tdb transaction. No token is returned, as only a single + transaction is allowed to be pending per tdb_context +*/ +int tdb_transaction_start(struct tdb_context *tdb) +{ + /* some sanity checks */ + if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n")); + tdb->ecode = TDB_ERR_EINVAL; + return -1; + } + + /* cope with nested tdb_transaction_start() calls */ + if (tdb->transaction != NULL) { + tdb->transaction->nesting++; + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", + tdb->transaction->nesting)); + return 0; + } + + if (tdb->num_locks != 0 || tdb->global_lock.count) { + /* the caller must not have any locks when starting a + transaction as otherwise we'll be screwed by lack + of nested locks in posix */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + + if (tdb->travlocks.next != NULL) { + /* you cannot use transactions inside a traverse (although you can use + traverse inside a transaction) as otherwise you can end up with + deadlock */ + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n")); + tdb->ecode = TDB_ERR_LOCK; + return -1; + } + + tdb->transaction = (struct tdb_transaction *) + calloc(sizeof(struct tdb_transaction), 1); + if (tdb->transaction == NULL) { + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + /* get the transaction write lock. This is a blocking lock. As + discussed with Volker, there are a number of ways we could + make this async, which we will probably do in the future */ + if (tdb_transaction_lock(tdb, F_WRLCK) == -1) { + SAFE_FREE(tdb->transaction); + return -1; + } + + /* get a read lock from the freelist to the end of file. This + is upgraded to a write lock during the commit */ + if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n")); + tdb->ecode = TDB_ERR_LOCK; + goto fail; + } + + /* setup a copy of the hash table heads so the hash scan in + traverse can be fast */ + tdb->transaction->hash_heads = (u32 *) + calloc(tdb->header.hash_size+1, sizeof(u32)); + if (tdb->transaction->hash_heads == NULL) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads, + TDB_HASHTABLE_SIZE(tdb), 0) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n")); + tdb->ecode = TDB_ERR_IO; + goto fail; + } + + /* make sure we know about any file expansions already done by + anyone else */ + tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); + tdb->transaction->old_map_size = tdb->map_size; + + /* finally hook the io methods, replacing them with + transaction specific methods */ + tdb->transaction->io_methods = tdb->methods; + tdb->methods = &transaction_methods; + + /* by calling this transaction write here, we ensure that we don't grow the + transaction linked list due to hash table updates */ + if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, + TDB_HASHTABLE_SIZE(tdb)) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n")); + tdb->ecode = TDB_ERR_IO; + tdb->methods = tdb->transaction->io_methods; + goto fail; + } + + return 0; + +fail: + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_transaction_unlock(tdb); + SAFE_FREE(tdb->transaction->hash_heads); + SAFE_FREE(tdb->transaction); + return -1; +} + + +/* + cancel the current transaction +*/ +int tdb_transaction_cancel(struct tdb_context *tdb) +{ + if (tdb->transaction == NULL) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); + return -1; + } + + if (tdb->transaction->nesting != 0) { + tdb->transaction->transaction_error = 1; + tdb->transaction->nesting--; + return 0; + } + + tdb->map_size = tdb->transaction->old_map_size; + + /* free all the transaction elements */ + while (tdb->transaction->elements) { + struct tdb_transaction_el *el = tdb->transaction->elements; + tdb->transaction->elements = el->next; + free(el->data); + free(el); + } + + /* remove any global lock created during the transaction */ + if (tdb->global_lock.count != 0) { + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); + tdb->global_lock.count = 0; + } + + /* remove any locks created during the transaction */ + if (tdb->num_locks != 0) { + int i; + for (i=0;inum_lockrecs;i++) { + tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, + F_UNLCK,F_SETLKW, 0, 1); + } + tdb->num_locks = 0; + tdb->num_lockrecs = 0; + SAFE_FREE(tdb->lockrecs); + } + + /* restore the normal io methods */ + tdb->methods = tdb->transaction->io_methods; + + tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); + tdb_transaction_unlock(tdb); + SAFE_FREE(tdb->transaction->hash_heads); + SAFE_FREE(tdb->transaction); + + return 0; +} + +/* + sync to disk +*/ +static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) +{ + if (fsync(tdb->fd) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); + return -1; + } +#ifdef MS_SYNC + if (tdb->map_ptr) { + tdb_off_t moffset = offset & ~(tdb->page_size-1); + if (msync(moffset + (char *)tdb->map_ptr, + length + (offset - moffset), MS_SYNC) != 0) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", + strerror(errno))); + return -1; + } + } +#endif + return 0; +} + + +/* + work out how much space the linearised recovery data will consume +*/ +static tdb_len_t tdb_recovery_size(struct tdb_context *tdb) +{ + struct tdb_transaction_el *el; + tdb_len_t recovery_size = 0; + + recovery_size = sizeof(u32); + for (el=tdb->transaction->elements;el;el=el->next) { + if (el->offset >= tdb->transaction->old_map_size) { + continue; + } + recovery_size += 2*sizeof(tdb_off_t) + el->length; + } + + return recovery_size; +} + +/* + allocate the recovery area, or use an existing recovery area if it is + large enough +*/ +static int tdb_recovery_allocate(struct tdb_context *tdb, + tdb_len_t *recovery_size, + tdb_off_t *recovery_offset, + tdb_len_t *recovery_max_size) +{ + struct list_struct rec; + const struct tdb_methods *methods = tdb->transaction->io_methods; + tdb_off_t recovery_head; + + if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n")); + return -1; + } + + rec.rec_len = 0; + + if (recovery_head != 0 && + methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n")); + return -1; + } + + *recovery_size = tdb_recovery_size(tdb); + + if (recovery_head != 0 && *recovery_size <= rec.rec_len) { + /* it fits in the existing area */ + *recovery_max_size = rec.rec_len; + *recovery_offset = recovery_head; + return 0; + } + + /* we need to free up the old recovery area, then allocate a + new one at the end of the file. Note that we cannot use + tdb_allocate() to allocate the new one as that might return + us an area that is being currently used (as of the start of + the transaction) */ + if (recovery_head != 0) { + if (tdb_free(tdb, recovery_head, &rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n")); + return -1; + } + } + + /* the tdb_free() call might have increased the recovery size */ + *recovery_size = tdb_recovery_size(tdb); + + /* round up to a multiple of page size */ + *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec); + *recovery_offset = tdb->map_size; + recovery_head = *recovery_offset; + + if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, + (tdb->map_size - tdb->transaction->old_map_size) + + sizeof(rec) + *recovery_max_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n")); + return -1; + } + + /* remap the file (if using mmap) */ + methods->tdb_oob(tdb, tdb->map_size + 1, 1); + + /* we have to reset the old map size so that we don't try to expand the file + again in the transaction commit, which would destroy the recovery area */ + tdb->transaction->old_map_size = tdb->map_size; + + /* write the recovery header offset and sync - we can sync without a race here + as the magic ptr in the recovery record has not been set */ + CONVERT(recovery_head); + if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, + &recovery_head, sizeof(tdb_off_t)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); + return -1; + } + + return 0; +} + + +/* + setup the recovery data that will be used on a crash during commit +*/ +static int transaction_setup_recovery(struct tdb_context *tdb, + tdb_off_t *magic_offset) +{ + struct tdb_transaction_el *el; + tdb_len_t recovery_size; + unsigned char *data, *p; + const struct tdb_methods *methods = tdb->transaction->io_methods; + struct list_struct *rec; + tdb_off_t recovery_offset, recovery_max_size; + tdb_off_t old_map_size = tdb->transaction->old_map_size; + u32 magic, tailer; + + /* + check that the recovery area has enough space + */ + if (tdb_recovery_allocate(tdb, &recovery_size, + &recovery_offset, &recovery_max_size) == -1) { + return -1; + } + + data = (unsigned char *)malloc(recovery_size + sizeof(*rec)); + if (data == NULL) { + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + rec = (struct list_struct *)data; + memset(rec, 0, sizeof(*rec)); + + rec->magic = 0; + rec->data_len = recovery_size; + rec->rec_len = recovery_max_size; + rec->key_len = old_map_size; + CONVERT(rec); + + /* build the recovery data into a single blob to allow us to do a single + large write, which should be more efficient */ + p = data + sizeof(*rec); + for (el=tdb->transaction->elements;el;el=el->next) { + if (el->offset >= old_map_size) { + continue; + } + if (el->offset + el->length > tdb->transaction->old_map_size) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n")); + free(data); + tdb->ecode = TDB_ERR_CORRUPT; + return -1; + } + memcpy(p, &el->offset, 4); + memcpy(p+4, &el->length, 4); + if (DOCONV()) { + tdb_convert(p, 8); + } + /* the recovery area contains the old data, not the + new data, so we have to call the original tdb_read + method to get it */ + if (methods->tdb_read(tdb, el->offset, p + 8, el->length, 0) != 0) { + free(data); + tdb->ecode = TDB_ERR_IO; + return -1; + } + p += 8 + el->length; + } + + /* and the tailer */ + tailer = sizeof(*rec) + recovery_max_size; + memcpy(p, &tailer, 4); + CONVERT(p); + + /* write the recovery data to the recovery area */ + if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n")); + free(data); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* as we don't have ordered writes, we have to sync the recovery + data before we update the magic to indicate that the recovery + data is present */ + if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) { + free(data); + return -1; + } + + free(data); + + magic = TDB_RECOVERY_MAGIC; + CONVERT(magic); + + *magic_offset = recovery_offset + offsetof(struct list_struct, magic); + + if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* ensure the recovery magic marker is on disk */ + if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { + return -1; + } + + return 0; +} + +/* + commit the current transaction +*/ +int tdb_transaction_commit(struct tdb_context *tdb) +{ + const struct tdb_methods *methods; + tdb_off_t magic_offset = 0; + u32 zero = 0; + + if (tdb->transaction == NULL) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); + return -1; + } + + if (tdb->transaction->transaction_error) { + tdb->ecode = TDB_ERR_IO; + tdb_transaction_cancel(tdb); + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); + return -1; + } + + if (tdb->transaction->nesting != 0) { + tdb->transaction->nesting--; + return 0; + } + + /* check for a null transaction */ + if (tdb->transaction->elements == NULL) { + tdb_transaction_cancel(tdb); + return 0; + } + + methods = tdb->transaction->io_methods; + + /* if there are any locks pending then the caller has not + nested their locks properly, so fail the transaction */ + if (tdb->num_locks || tdb->global_lock.count) { + tdb->ecode = TDB_ERR_LOCK; + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n")); + tdb_transaction_cancel(tdb); + return -1; + } + + /* upgrade the main transaction lock region to a write lock */ + if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n")); + tdb->ecode = TDB_ERR_LOCK; + tdb_transaction_cancel(tdb); + return -1; + } + + /* get the global lock - this prevents new users attaching to the database + during the commit */ + if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n")); + tdb->ecode = TDB_ERR_LOCK; + tdb_transaction_cancel(tdb); + return -1; + } + + if (!(tdb->flags & TDB_NOSYNC)) { + /* write the recovery data to the end of the file */ + if (transaction_setup_recovery(tdb, &magic_offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_transaction_cancel(tdb); + return -1; + } + } + + /* expand the file to the new size if needed */ + if (tdb->map_size != tdb->transaction->old_map_size) { + if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, + tdb->map_size - + tdb->transaction->old_map_size) == -1) { + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n")); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + tdb_transaction_cancel(tdb); + return -1; + } + tdb->map_size = tdb->transaction->old_map_size; + methods->tdb_oob(tdb, tdb->map_size + 1, 1); + } + + /* perform all the writes */ + while (tdb->transaction->elements) { + struct tdb_transaction_el *el = tdb->transaction->elements; + + if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); + + /* we've overwritten part of the data and + possibly expanded the file, so we need to + run the crash recovery code */ + tdb->methods = methods; + tdb_transaction_recover(tdb); + + tdb_transaction_cancel(tdb); + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); + return -1; + } + tdb->transaction->elements = el->next; + free(el->data); + free(el); + } + + if (!(tdb->flags & TDB_NOSYNC)) { + /* ensure the new data is on disk */ + if (transaction_sync(tdb, 0, tdb->map_size) == -1) { + return -1; + } + + /* remove the recovery marker */ + if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n")); + return -1; + } + + /* ensure the recovery marker has been removed on disk */ + if (transaction_sync(tdb, magic_offset, 4) == -1) { + return -1; + } + } + + tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); + + /* + TODO: maybe write to some dummy hdr field, or write to magic + offset without mmap, before the last sync, instead of the + utime() call + */ + + /* on some systems (like Linux 2.6.x) changes via mmap/msync + don't change the mtime of the file, this means the file may + not be backed up (as tdb rounding to block sizes means that + file size changes are quite rare too). The following forces + mtime changes when a transaction completes */ +#ifdef HAVE_UTIME + utime(tdb->name, NULL); +#endif + + /* use a transaction cancel to free memory and remove the + transaction locks */ + tdb_transaction_cancel(tdb); + return 0; +} + + +/* + recover from an aborted transaction. Must be called with exclusive + database write access already established (including the global + lock to prevent new processes attaching) +*/ +int tdb_transaction_recover(struct tdb_context *tdb) +{ + tdb_off_t recovery_head, recovery_eof; + unsigned char *data, *p; + u32 zero = 0; + struct list_struct rec; + + /* find the recovery area */ + if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + if (recovery_head == 0) { + /* we have never allocated a recovery record */ + return 0; + } + + /* read the recovery record */ + if (tdb->methods->tdb_read(tdb, recovery_head, &rec, + sizeof(rec), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + if (rec.magic != TDB_RECOVERY_MAGIC) { + /* there is no valid recovery data */ + return 0; + } + + if (tdb->read_only) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n")); + tdb->ecode = TDB_ERR_CORRUPT; + return -1; + } + + recovery_eof = rec.key_len; + + data = (unsigned char *)malloc(rec.data_len); + if (data == NULL) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n")); + tdb->ecode = TDB_ERR_OOM; + return -1; + } + + /* read the full recovery data */ + if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data, + rec.data_len, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* recover the file data */ + p = data; + while (p+8 < data + rec.data_len) { + u32 ofs, len; + if (DOCONV()) { + tdb_convert(p, 8); + } + memcpy(&ofs, p, 4); + memcpy(&len, p+4, 4); + + if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) { + free(data); + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs)); + tdb->ecode = TDB_ERR_IO; + return -1; + } + p += 8 + len; + } + + free(data); + + if (transaction_sync(tdb, 0, tdb->map_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* if the recovery area is after the recovered eof then remove it */ + if (recovery_eof <= recovery_head) { + if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + } + + /* remove the recovery magic */ + if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), + &zero) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + /* reduce the file size to the old size */ + tdb_munmap(tdb); + if (ftruncate(tdb->fd, recovery_eof) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + tdb->map_size = recovery_eof; + tdb_mmap(tdb); + + if (transaction_sync(tdb, 0, recovery_eof) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } + + TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", + recovery_eof)); + + /* all done */ + return 0; +} + +/* file: freelist.c */ + +/* read a freelist record and check for simple errors */ +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) +{ + if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + + if (rec->magic == TDB_MAGIC) { + /* this happens when a app is showdown while deleting a record - we should + not completely fail when this happens */ + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n", + rec->magic, off)); + rec->magic = TDB_FREE_MAGIC; + if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1) + return -1; + } + + if (rec->magic != TDB_FREE_MAGIC) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", + rec->magic, off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) + return -1; + return 0; +} + + + +/* Remove an element from the freelist. Must have alloc lock. */ +static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next) +{ + tdb_off_t last_ptr, i; + + /* read in the freelist top */ + last_ptr = FREELIST_TOP; + while (tdb_ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { + if (i == off) { + /* We've found it! */ + return tdb_ofs_write(tdb, last_ptr, &next); + } + /* Follow chain (next offset is at start of record) */ + last_ptr = i; + } + TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); +} + + +/* update a record tailer (must hold allocation lock) */ +static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, + const struct list_struct *rec) +{ + tdb_off_t totalsize; + + /* Offset of tailer from record header */ + totalsize = sizeof(*rec) + rec->rec_len; + return tdb_ofs_write(tdb, offset + totalsize - sizeof(tdb_off_t), + &totalsize); +} + +/* Add an element into the freelist. Merge adjacent records if + neccessary. */ +int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) +{ + tdb_off_t right, left; + + /* Allocation and tailer lock */ + if (tdb_lock(tdb, -1, F_WRLCK) != 0) + return -1; + + /* set an initial tailer, so if we fail we don't leave a bogus record */ + if (update_tailer(tdb, offset, rec) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n")); + goto fail; + } + + /* Look right first (I'm an Australian, dammit) */ + right = offset + sizeof(*rec) + rec->rec_len; + if (right + sizeof(*rec) <= tdb->map_size) { + struct list_struct r; + + if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right)); + goto left; + } + + /* If it's free, expand to include it. */ + if (r.magic == TDB_FREE_MAGIC) { + if (remove_from_freelist(tdb, right, r.next) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right)); + goto left; + } + rec->rec_len += sizeof(r) + r.rec_len; + } + } + +left: + /* Look left */ + left = offset - sizeof(tdb_off_t); + if (left > TDB_DATA_START(tdb->header.hash_size)) { + struct list_struct l; + tdb_off_t leftsize; + + /* Read in tailer and jump back to header */ + if (tdb_ofs_read(tdb, left, &leftsize) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left)); + goto update; + } + + /* it could be uninitialised data */ + if (leftsize == 0 || leftsize == TDB_PAD_U32) { + goto update; + } + + left = offset - leftsize; + + /* Now read in record */ + if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); + goto update; + } + + /* If it's free, expand to include it. */ + if (l.magic == TDB_FREE_MAGIC) { + if (remove_from_freelist(tdb, left, l.next) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left)); + goto update; + } else { + offset = left; + rec->rec_len += leftsize; + } + } + } + +update: + if (update_tailer(tdb, offset, rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); + goto fail; + } + + /* Now, prepend to free list */ + rec->magic = TDB_FREE_MAGIC; + + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || + tdb_rec_write(tdb, offset, rec) == -1 || + tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset)); + goto fail; + } + + /* And we're done. */ + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + + +/* + the core of tdb_allocate - called when we have decided which + free list entry to use + */ +static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr, + struct list_struct *rec, tdb_off_t last_ptr) +{ + struct list_struct newrec; + tdb_off_t newrec_ptr; + + memset(&newrec, '\0', sizeof(newrec)); + + /* found it - now possibly split it up */ + if (rec->rec_len > length + MIN_REC_SIZE) { + /* Length of left piece */ + length = TDB_ALIGN(length, TDB_ALIGNMENT); + + /* Right piece to go on free list */ + newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); + newrec_ptr = rec_ptr + sizeof(*rec) + length; + + /* And left record is shortened */ + rec->rec_len = length; + } else { + newrec_ptr = 0; + } + + /* Remove allocated record from the free list */ + if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) { + return 0; + } + + /* Update header: do this before we drop alloc + lock, otherwise tdb_free() might try to + merge with us, thinking we're free. + (Thanks Jeremy Allison). */ + rec->magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { + return 0; + } + + /* Did we create new block? */ + if (newrec_ptr) { + /* Update allocated record tailer (we + shortened it). */ + if (update_tailer(tdb, rec_ptr, rec) == -1) { + return 0; + } + + /* Free new record */ + if (tdb_free(tdb, newrec_ptr, &newrec) == -1) { + return 0; + } + } + + /* all done - return the new record offset */ + return rec_ptr; +} + +/* allocate some space from the free list. The offset returned points + to a unconnected list_struct within the database with room for at + least length bytes of total data + + 0 is returned if the space could not be allocated + */ +tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec) +{ + tdb_off_t rec_ptr, last_ptr, newrec_ptr; + struct { + tdb_off_t rec_ptr, last_ptr; + tdb_len_t rec_len; + } bestfit; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) + return 0; + + /* Extra bytes required for tailer */ + length += sizeof(tdb_off_t); + + again: + last_ptr = FREELIST_TOP; + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) + goto fail; + + bestfit.rec_ptr = 0; + bestfit.last_ptr = 0; + bestfit.rec_len = 0; + + /* + this is a best fit allocation strategy. Originally we used + a first fit strategy, but it suffered from massive fragmentation + issues when faced with a slowly increasing record size. + */ + while (rec_ptr) { + if (tdb_rec_free_read(tdb, rec_ptr, rec) == -1) { + goto fail; + } + + if (rec->rec_len >= length) { + if (bestfit.rec_ptr == 0 || + rec->rec_len < bestfit.rec_len) { + bestfit.rec_len = rec->rec_len; + bestfit.rec_ptr = rec_ptr; + bestfit.last_ptr = last_ptr; + /* consider a fit to be good enough if + we aren't wasting more than half + the space */ + if (bestfit.rec_len < 2*length) { + break; + } + } + } + + /* move to the next record */ + last_ptr = rec_ptr; + rec_ptr = rec->next; + } + + if (bestfit.rec_ptr != 0) { + if (tdb_rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) { + goto fail; + } + + newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr); + tdb_unlock(tdb, -1, F_WRLCK); + return newrec_ptr; + } + + /* we didn't find enough space. See if we can expand the + database and if we can then try again */ + if (tdb_expand(tdb, length + sizeof(*rec)) == 0) + goto again; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return 0; +} + +/* file: freelistcheck.c */ + +/* Check the freelist is good and contains no loops. + Very memory intensive - only do this as a consistency + checker. Heh heh - uses an in memory tdb as the storage + for the "seen" record list. For some reason this strikes + me as extremely clever as I don't have to write another tree + data structure implementation :-). + */ + +static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr) +{ + TDB_DATA key, data; + + memset(&data, '\0', sizeof(data)); + key.dptr = (unsigned char *)&rec_ptr; + key.dsize = sizeof(rec_ptr); + return tdb_store(mem_tdb, key, data, TDB_INSERT); +} + +int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) +{ + struct tdb_context *mem_tdb = NULL; + struct list_struct rec; + tdb_off_t rec_ptr, last_ptr; + int ret = -1; + + *pnum_entries = 0; + + mem_tdb = tdb_open("flval", tdb->header.hash_size, + TDB_INTERNAL, O_RDWR, 0600); + if (!mem_tdb) { + return -1; + } + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + tdb_close(mem_tdb); + return 0; + } + + last_ptr = FREELIST_TOP; + + /* Store the FREELIST_TOP record. */ + if (seen_insert(mem_tdb, last_ptr) == -1) { + ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + goto fail; + } + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) { + goto fail; + } + + while (rec_ptr) { + + /* If we can't store this record (we've seen it + before) then the free list has a loop and must + be corrupt. */ + + if (seen_insert(mem_tdb, rec_ptr)) { + ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + goto fail; + } + + if (tdb_rec_free_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + /* move to the next record */ + last_ptr = rec_ptr; + rec_ptr = rec.next; + *pnum_entries += 1; + } + + ret = 0; + + fail: + + tdb_close(mem_tdb); + tdb_unlock(tdb, -1, F_WRLCK); + return ret; +} + +/* file: traverse.c */ + +/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ +static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock, + struct list_struct *rec) +{ + int want_next = (tlock->off != 0); + + /* Lock each chain from the start one. */ + for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { + if (!tlock->off && tlock->hash != 0) { + /* this is an optimisation for the common case where + the hash chain is empty, which is particularly + common for the use of tdb with ldb, where large + hashes are used. In that case we spend most of our + time in tdb_brlock(), locking empty hash chains. + + To avoid this, we do an unlocked pre-check to see + if the hash chain is empty before starting to look + inside it. If it is empty then we can avoid that + hash chain. If it isn't empty then we can't believe + the value we get back, as we read it without a + lock, so instead we get the lock and re-fetch the + value below. + + Notice that not doing this optimisation on the + first hash chain is critical. We must guarantee + that we have done at least one fcntl lock at the + start of a search to guarantee that memory is + coherent on SMP systems. If records are added by + others during the search then thats OK, and we + could possibly miss those with this trick, but we + could miss them anyway without this trick, so the + semantics don't change. + + With a non-indexed ldb search this trick gains us a + factor of around 80 in speed on a linux 2.6.x + system (testing using ldbtest). + */ + tdb->methods->next_hash_chain(tdb, &tlock->hash); + if (tlock->hash == tdb->header.hash_size) { + continue; + } + } + + if (tdb_lock(tdb, tlock->hash, tlock->lock_rw) == -1) + return -1; + + /* No previous record? Start at top of chain. */ + if (!tlock->off) { + if (tdb_ofs_read(tdb, TDB_HASH_TOP(tlock->hash), + &tlock->off) == -1) + goto fail; + } else { + /* Otherwise unlock the previous record. */ + if (tdb_unlock_record(tdb, tlock->off) != 0) + goto fail; + } + + if (want_next) { + /* We have offset of old record: grab next */ + if (tdb_rec_read(tdb, tlock->off, rec) == -1) + goto fail; + tlock->off = rec->next; + } + + /* Iterate through chain */ + while( tlock->off) { + tdb_off_t current; + if (tdb_rec_read(tdb, tlock->off, rec) == -1) + goto fail; + + /* Detect infinite loops. From "Shlomi Yaakobovich" . */ + if (tlock->off == rec->next) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n")); + goto fail; + } + + if (!TDB_DEAD(rec)) { + /* Woohoo: we found one! */ + if (tdb_lock_record(tdb, tlock->off) != 0) + goto fail; + return tlock->off; + } + + /* Try to clean dead ones from old traverses */ + current = tlock->off; + tlock->off = rec->next; + if (!(tdb->read_only || tdb->traverse_read) && + tdb_do_delete(tdb, current, rec) != 0) + goto fail; + } + tdb_unlock(tdb, tlock->hash, tlock->lock_rw); + want_next = 0; + } + /* We finished iteration without finding anything */ + return TDB_ERRCODE(TDB_SUCCESS, 0); + + fail: + tlock->off = 0; + if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n")); + return -1; +} + +/* traverse the entire database - calling fn(tdb, key, data) on each element. + return -1 on error or the record count traversed + if fn is NULL then it is not called + a non-zero return value from fn() indicates that the traversal should stop + */ +static int tdb_traverse_internal(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data, + struct tdb_traverse_lock *tl) +{ + TDB_DATA key, dbuf; + struct list_struct rec; + int ret, count = 0; + + /* This was in the initializaton, above, but the IRIX compiler + * did not like it. crh + */ + tl->next = tdb->travlocks.next; + + /* fcntl locks don't stack: beware traverse inside traverse */ + tdb->travlocks.next = tl; + + /* tdb_next_lock places locks on the record returned, and its chain */ + while ((ret = tdb_next_lock(tdb, tl, &rec)) > 0) { + count++; + /* now read the full record */ + key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), + rec.key_len + rec.data_len); + if (!key.dptr) { + ret = -1; + if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) + goto out; + if (tdb_unlock_record(tdb, tl->off) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); + goto out; + } + key.dsize = rec.key_len; + dbuf.dptr = key.dptr + rec.key_len; + dbuf.dsize = rec.data_len; + + /* Drop chain lock, call out */ + if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) { + ret = -1; + SAFE_FREE(key.dptr); + goto out; + } + if (fn && fn(tdb, key, dbuf, private_data)) { + /* They want us to terminate traversal */ + ret = count; + if (tdb_unlock_record(tdb, tl->off) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; + ret = -1; + } + SAFE_FREE(key.dptr); + goto out; + } + SAFE_FREE(key.dptr); + } +out: + tdb->travlocks.next = tl->next; + if (ret < 0) + return -1; + else + return count; +} + + +/* + a write style traverse - temporarily marks the db read only +*/ +int tdb_traverse_read(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data) +{ + struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; + int ret; + + /* we need to get a read lock on the transaction lock here to + cope with the lock ordering semantics of solaris10 */ + if (tdb_transaction_lock(tdb, F_RDLCK)) { + return -1; + } + + tdb->traverse_read++; + ret = tdb_traverse_internal(tdb, fn, private_data, &tl); + tdb->traverse_read--; + + tdb_transaction_unlock(tdb); + + return ret; +} + +/* + a write style traverse - needs to get the transaction lock to + prevent deadlocks +*/ +int tdb_traverse(struct tdb_context *tdb, + tdb_traverse_func fn, void *private_data) +{ + struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; + int ret; + + if (tdb->read_only || tdb->traverse_read) { + return tdb_traverse_read(tdb, fn, private_data); + } + + if (tdb_transaction_lock(tdb, F_WRLCK)) { + return -1; + } + + ret = tdb_traverse_internal(tdb, fn, private_data, &tl); + + tdb_transaction_unlock(tdb); + + return ret; +} + + +/* find the first entry in the database and return its key */ +TDB_DATA tdb_firstkey(struct tdb_context *tdb) +{ + TDB_DATA key; + struct list_struct rec; + + /* release any old lock */ + if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) + return tdb_null; + tdb->travlocks.off = tdb->travlocks.hash = 0; + tdb->travlocks.lock_rw = F_RDLCK; + + /* Grab first record: locks chain and returned record. */ + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) + return tdb_null; + /* now read the key */ + key.dsize = rec.key_len; + key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); + + /* Unlock the hash chain of the record we just read. */ + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n")); + return key; +} + +/* find the next entry in the database, returning its key */ +TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) +{ + u32 oldhash; + TDB_DATA key = tdb_null; + struct list_struct rec; + unsigned char *k = NULL; + + /* Is locked key the old key? If so, traverse will be reliable. */ + if (tdb->travlocks.off) { + if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw)) + return tdb_null; + if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1 + || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), + rec.key_len)) + || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { + /* No, it wasn't: unlock it and start from scratch */ + if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) { + SAFE_FREE(k); + return tdb_null; + } + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) { + SAFE_FREE(k); + return tdb_null; + } + tdb->travlocks.off = 0; + } + + SAFE_FREE(k); + } + + if (!tdb->travlocks.off) { + /* No previous element: do normal find, and lock record */ + tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec); + if (!tdb->travlocks.off) + return tdb_null; + tdb->travlocks.hash = BUCKET(rec.full_hash); + if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); + return tdb_null; + } + } + oldhash = tdb->travlocks.hash; + + /* Grab next record: locks chain and returned record, + unlocks old record */ + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { + key.dsize = rec.key_len; + key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), + key.dsize); + /* Unlock the chain of this new record */ + if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + } + /* Unlock the chain of old record */ + if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + return key; +} + +/* file: dump.c */ + +static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash, + tdb_off_t offset) +{ + struct list_struct rec; + tdb_off_t tailer_ofs, tailer; + + if (tdb->methods->tdb_read(tdb, offset, (char *)&rec, + sizeof(rec), DOCONV()) == -1) { + printf("ERROR: failed to read record at %u\n", offset); + return 0; + } + + printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d " + "key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n", + hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, + rec.full_hash, rec.magic); + + tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off_t); + + if (tdb_ofs_read(tdb, tailer_ofs, &tailer) == -1) { + printf("ERROR: failed to read tailer at %u\n", tailer_ofs); + return rec.next; + } + + if (tailer != rec.rec_len + sizeof(rec)) { + printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n", + (unsigned int)tailer, (unsigned int)(rec.rec_len + sizeof(rec))); + } + return rec.next; +} + +static int tdb_dump_chain(struct tdb_context *tdb, int i) +{ + tdb_off_t rec_ptr, top; + + top = TDB_HASH_TOP(i); + + if (tdb_lock(tdb, i, F_WRLCK) != 0) + return -1; + + if (tdb_ofs_read(tdb, top, &rec_ptr) == -1) + return tdb_unlock(tdb, i, F_WRLCK); + + if (rec_ptr) + printf("hash=%d\n", i); + + while (rec_ptr) { + rec_ptr = tdb_dump_record(tdb, i, rec_ptr); + } + + return tdb_unlock(tdb, i, F_WRLCK); +} + +void tdb_dump_all(struct tdb_context *tdb) +{ + int i; + for (i=0;iheader.hash_size;i++) { + tdb_dump_chain(tdb, i); + } + printf("freelist:\n"); + tdb_dump_chain(tdb, -1); +} + +int tdb_printfreelist(struct tdb_context *tdb) +{ + int ret; + long total_free = 0; + tdb_off_t offset, rec_ptr; + struct list_struct rec; + + if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) + return ret; + + offset = FREELIST_TOP; + + /* read in the freelist top */ + if (tdb_ofs_read(tdb, offset, &rec_ptr) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + } + + printf("freelist top=[0x%08x]\n", rec_ptr ); + while (rec_ptr) { + if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec, + sizeof(rec), DOCONV()) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + if (rec.magic != TDB_FREE_MAGIC) { + printf("bad magic 0x%08x in free list\n", rec.magic); + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] (end = 0x%08x)\n", + rec_ptr, rec.rec_len, rec.rec_len, rec_ptr + rec.rec_len); + total_free += rec.rec_len; + + /* move to the next record */ + rec_ptr = rec.next; + } + printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, + (int)total_free); + + return tdb_unlock(tdb, -1, F_WRLCK); +} + +/* file: tdb.c */ + +TDB_DATA tdb_null; + +/* + non-blocking increment of the tdb sequence number if the tdb has been opened using + the TDB_SEQNUM flag +*/ +void tdb_increment_seqnum_nonblock(struct tdb_context *tdb) +{ + tdb_off_t seqnum=0; + + if (!(tdb->flags & TDB_SEQNUM)) { + return; + } + + /* we ignore errors from this, as we have no sane way of + dealing with them. + */ + tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); + seqnum++; + tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum); +} + +/* + increment the tdb sequence number if the tdb has been opened using + the TDB_SEQNUM flag +*/ +static void tdb_increment_seqnum(struct tdb_context *tdb) +{ + if (!(tdb->flags & TDB_SEQNUM)) { + return; + } + + if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { + return; + } + + tdb_increment_seqnum_nonblock(tdb); + + tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1); +} + +static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data) +{ + return memcmp(data.dptr, key.dptr, data.dsize); +} + +/* Returns 0 on fail. On success, return offset of record, and fills + in rec */ +static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash, + struct list_struct *r) +{ + tdb_off_t rec_ptr; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, r) == -1) + return 0; + + if (!TDB_DEAD(r) && hash==r->full_hash + && key.dsize==r->key_len + && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r), + r->key_len, tdb_key_compare, + NULL) == 0) { + return rec_ptr; + } + rec_ptr = r->next; + } + return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); +} + +/* As tdb_find, but if you succeed, keep the lock */ +tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, + struct list_struct *rec) +{ + u32 rec_ptr; + + if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) + return 0; + if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) + tdb_unlock(tdb, BUCKET(hash), locktype); + return rec_ptr; +} + + +/* update an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1. +*/ +static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf) +{ + struct list_struct rec; + tdb_off_t rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) + return -1; + + /* must be long enough key, data and tailer */ + if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) { + tdb->ecode = TDB_SUCCESS; /* Not really an error */ + return -1; + } + + if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, + dbuf.dptr, dbuf.dsize) == -1) + return -1; + + if (dbuf.dsize != rec.data_len) { + /* update size */ + rec.data_len = dbuf.dsize; + return tdb_rec_write(tdb, rec_ptr, &rec); + } + + return 0; +} + +/* find an entry in the database given a key */ +/* If an entry doesn't exist tdb_err will be set to + * TDB_ERR_NOEXIST. If a key has no data attached + * then the TDB_DATA will have zero length but + * a non-zero pointer + */ +TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + TDB_DATA ret; + u32 hash; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) + return tdb_null; + + ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len); + ret.dsize = rec.data_len; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return ret; +} + +/* + * Find an entry in the database and hand the record's data to a parsing + * function. The parsing function is executed under the chain read lock, so it + * should be fast and should not block on other syscalls. + * + * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS. + * + * For mmapped tdb's that do not have a transaction open it points the parsing + * function directly at the mmap area, it avoids the malloc/memcpy in this + * case. If a transaction is open or no mmap is available, it has to do + * malloc/read/parse/free. + * + * This is interesting for all readers of potentially large data structures in + * the tdb records, ldb indexes being one example. + */ + +int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + int ret; + u32 hash; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + + if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { + return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + } + + ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len, parser, private_data); + + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + + return ret; +} + +/* check if an entry in the database exists + + note that 1 is returned if the key is found and 0 is returned if not found + this doesn't match the conventions in the rest of this module, but is + compatible with gdbm +*/ +static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) +{ + struct list_struct rec; + + if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) + return 0; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return 1; +} + +int tdb_exists(struct tdb_context *tdb, TDB_DATA key) +{ + u32 hash = tdb->hash_fn(&key); + return tdb_exists_hash(tdb, key, hash); +} + +/* actually delete an entry in the database given the offset */ +int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec) +{ + tdb_off_t last_ptr, i; + struct list_struct lastrec; + + if (tdb->read_only || tdb->traverse_read) return -1; + + if (tdb_write_lock_record(tdb, rec_ptr) == -1) { + /* Someone traversing here: mark it as dead */ + rec->magic = TDB_DEAD_MAGIC; + return tdb_rec_write(tdb, rec_ptr, rec); + } + if (tdb_write_unlock_record(tdb, rec_ptr) != 0) + return -1; + + /* find previous record in hash chain */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) + return -1; + for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) + if (tdb_rec_read(tdb, i, &lastrec) == -1) + return -1; + + /* unlink it: next ptr is at start of record. */ + if (last_ptr == 0) + last_ptr = TDB_HASH_TOP(rec->full_hash); + if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) + return -1; + + /* recover the space */ + if (tdb_free(tdb, rec_ptr, rec) == -1) + return -1; + return 0; +} + +static int tdb_count_dead(struct tdb_context *tdb, u32 hash) +{ + int res = 0; + tdb_off_t rec_ptr; + struct list_struct rec; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) + return 0; + + if (rec.magic == TDB_DEAD_MAGIC) { + res += 1; + } + rec_ptr = rec.next; + } + return res; +} + +/* + * Purge all DEAD records from a hash chain + */ +static int tdb_purge_dead(struct tdb_context *tdb, u32 hash) +{ + int res = -1; + struct list_struct rec; + tdb_off_t rec_ptr; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + return -1; + } + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + goto fail; + + while (rec_ptr) { + tdb_off_t next; + + if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + + next = rec.next; + + if (rec.magic == TDB_DEAD_MAGIC + && tdb_do_delete(tdb, rec_ptr, &rec) == -1) { + goto fail; + } + rec_ptr = next; + } + res = 0; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return res; +} + +/* delete an entry in the database given a key */ +static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) +{ + tdb_off_t rec_ptr; + struct list_struct rec; + int ret; + + if (tdb->max_dead_records != 0) { + + /* + * Allow for some dead records per hash chain, mainly for + * tdb's with a very high create/delete rate like locking.tdb. + */ + + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) { + /* + * Don't let the per-chain freelist grow too large, + * delete all existing dead records + */ + tdb_purge_dead(tdb, hash); + } + + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) { + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return -1; + } + + /* + * Just mark the record as dead. + */ + rec.magic = TDB_DEAD_MAGIC; + ret = tdb_rec_write(tdb, rec_ptr, &rec); + } + else { + if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, + &rec))) + return -1; + + ret = tdb_do_delete(tdb, rec_ptr, &rec); + } + + if (ret == 0) { + tdb_increment_seqnum(tdb); + } + + if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n")); + return ret; +} + +int tdb_delete(struct tdb_context *tdb, TDB_DATA key) +{ + u32 hash = tdb->hash_fn(&key); + return tdb_delete_hash(tdb, key, hash); +} + +/* + * See if we have a dead record around with enough space + */ +static tdb_off_t tdb_find_dead(struct tdb_context *tdb, u32 hash, + struct list_struct *r, tdb_len_t length) +{ + tdb_off_t rec_ptr; + + /* read in the hash top */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (tdb_rec_read(tdb, rec_ptr, r) == -1) + return 0; + + if (TDB_DEAD(r) && r->rec_len >= length) { + /* + * First fit for simple coding, TODO: change to best + * fit + */ + return rec_ptr; + } + rec_ptr = r->next; + } + return 0; +} + +/* store an element in the database, replacing any existing element + with the same key + + return 0 on success, -1 on failure +*/ +int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) +{ + struct list_struct rec; + u32 hash; + tdb_off_t rec_ptr; + char *p = NULL; + int ret = -1; + + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + /* check for it existing, on insert. */ + if (flag == TDB_INSERT) { + if (tdb_exists_hash(tdb, key, hash)) { + tdb->ecode = TDB_ERR_EXISTS; + goto fail; + } + } else { + /* first try in-place update, on modify or replace. */ + if (tdb_update_hash(tdb, key, hash, dbuf) == 0) { + goto done; + } + if (tdb->ecode == TDB_ERR_NOEXIST && + flag == TDB_MODIFY) { + /* if the record doesn't exist and we are in TDB_MODIFY mode then + we should fail the store */ + goto fail; + } + } + /* reset the error code potentially set by the tdb_update() */ + tdb->ecode = TDB_SUCCESS; + + /* delete any existing record - if it doesn't exist we don't + care. Doing this first reduces fragmentation, and avoids + coalescing with `allocated' block before it's updated. */ + if (flag != TDB_INSERT) + tdb_delete_hash(tdb, key, hash); + + /* Copy key+value *before* allocating free space in case malloc + fails and we are left with a dead spot in the tdb. */ + + if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + + memcpy(p, key.dptr, key.dsize); + if (dbuf.dsize) + memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); + + if (tdb->max_dead_records != 0) { + /* + * Allow for some dead records per hash chain, look if we can + * find one that can hold the new record. We need enough space + * for key, data and tailer. If we find one, we don't have to + * consult the central freelist. + */ + rec_ptr = tdb_find_dead( + tdb, hash, &rec, + key.dsize + dbuf.dsize + sizeof(tdb_off_t)); + + if (rec_ptr != 0) { + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 + || tdb->methods->tdb_write( + tdb, rec_ptr + sizeof(rec), + p, key.dsize + dbuf.dsize) == -1) { + goto fail; + } + goto done; + } + } + + /* + * We have to allocate some space from the freelist, so this means we + * have to lock it. Use the chance to purge all the DEAD records from + * the hash chain under the freelist lock. + */ + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + goto fail; + } + + if ((tdb->max_dead_records != 0) + && (tdb_purge_dead(tdb, hash) == -1)) { + tdb_unlock(tdb, -1, F_WRLCK); + goto fail; + } + + /* we have to allocate some space */ + rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec); + + tdb_unlock(tdb, -1, F_WRLCK); + + if (rec_ptr == 0) { + goto fail; + } + + /* Read hash top into next ptr */ + if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) + goto fail; + + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + + /* write out and point the top of the hash chain at it */ + if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 + || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 + || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { + /* Need to tdb_unallocate() here */ + goto fail; + } + + done: + ret = 0; + fail: + if (ret == 0) { + tdb_increment_seqnum(tdb); + } + + SAFE_FREE(p); + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return ret; +} + + +/* Append to an entry. Create if not exist. */ +int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) +{ + u32 hash; + TDB_DATA dbuf; + int ret = -1; + + /* find which hash bucket it is in */ + hash = tdb->hash_fn(&key); + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + dbuf = tdb_fetch(tdb, key); + + if (dbuf.dptr == NULL) { + dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize); + } else { + unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr, + dbuf.dsize + new_dbuf.dsize); + if (new_dptr == NULL) { + free(dbuf.dptr); + } + dbuf.dptr = new_dptr; + } + + if (dbuf.dptr == NULL) { + tdb->ecode = TDB_ERR_OOM; + goto failed; + } + + memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize); + dbuf.dsize += new_dbuf.dsize; + + ret = tdb_store(tdb, key, dbuf, 0); + +failed: + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + SAFE_FREE(dbuf.dptr); + return ret; +} + + +/* + return the name of the current tdb file + useful for external logging functions +*/ +const char *tdb_name(struct tdb_context *tdb) +{ + return tdb->name; +} + +/* + return the underlying file descriptor being used by tdb, or -1 + useful for external routines that want to check the device/inode + of the fd +*/ +int tdb_fd(struct tdb_context *tdb) +{ + return tdb->fd; +} + +/* + return the current logging function + useful for external tdb routines that wish to log tdb errors +*/ +tdb_log_func tdb_log_fn(struct tdb_context *tdb) +{ + return tdb->log.log_fn; +} + + +/* + get the tdb sequence number. Only makes sense if the writers opened + with TDB_SEQNUM set. Note that this sequence number will wrap quite + quickly, so it should only be used for a 'has something changed' + test, not for code that relies on the count of the number of changes + made. If you want a counter then use a tdb record. + + The aim of this sequence number is to allow for a very lightweight + test of a possible tdb change. +*/ +int tdb_get_seqnum(struct tdb_context *tdb) +{ + tdb_off_t seqnum=0; + + tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); + return seqnum; +} + +int tdb_hash_size(struct tdb_context *tdb) +{ + return tdb->header.hash_size; +} + +size_t tdb_map_size(struct tdb_context *tdb) +{ + return tdb->map_size; +} + +int tdb_get_flags(struct tdb_context *tdb) +{ + return tdb->flags; +} + + +/* + enable sequence number handling on an open tdb +*/ +void tdb_enable_seqnum(struct tdb_context *tdb) +{ + tdb->flags |= TDB_SEQNUM; +} + +/* file: open.c */ + +/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ +static struct tdb_context *tdbs = NULL; + + +/* This is based on the hash algorithm from gdbm */ +static unsigned int default_tdb_hash(TDB_DATA *key) +{ + u32 value; /* Used to compute the hash value. */ + u32 i; /* Used to cycle through random values. */ + + /* Set the initial value from the key size. */ + for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) + value = (value + (key->dptr[i] << (i*5 % 24))); + + return (1103515243 * value + 12345); +} + + +/* initialise a new database with a specified hash size */ +static int tdb_new_database(struct tdb_context *tdb, int hash_size) +{ + struct tdb_header *newdb; + int size, ret = -1; + + /* We make it up in memory, then write it out if not internal */ + size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t); + if (!(newdb = (struct tdb_header *)calloc(size, 1))) + return TDB_ERRCODE(TDB_ERR_OOM, -1); + + /* Fill in the header */ + newdb->version = TDB_VERSION; + newdb->hash_size = hash_size; + if (tdb->flags & TDB_INTERNAL) { + tdb->map_size = size; + tdb->map_ptr = (char *)newdb; + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Convert the `ondisk' version if asked. */ + CONVERT(*newdb); + return 0; + } + if (lseek(tdb->fd, 0, SEEK_SET) == -1) + goto fail; + + if (ftruncate(tdb->fd, 0) == -1) + goto fail; + + /* This creates an endian-converted header, as if read from disk */ + CONVERT(*newdb); + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Don't endian-convert the magic food! */ + memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); + if (write(tdb->fd, newdb, size) != size) { + ret = -1; + } else { + ret = 0; + } + + fail: + SAFE_FREE(newdb); + return ret; +} + + + +static int tdb_already_open(dev_t device, + ino_t ino) +{ + struct tdb_context *i; + + for (i = tdbs; i; i = i->next) { + if (i->device == device && i->inode == ino) { + return 1; + } + } + + return 0; +} + +/* open the database, creating it if necessary + + The open_flags and mode are passed straight to the open call on the + database file. A flags value of O_WRONLY is invalid. The hash size + is advisory, use zero for a default value. + + Return is NULL on error, in which case errno is also set. Don't + try to call tdb_error or tdb_errname, just do strerror(errno). + + @param name may be NULL for internal databases. */ +struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL); +} + +/* a default logging function */ +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); +static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ +} + + +struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + const struct tdb_logging_context *log_ctx, + tdb_hash_func hash_fn) +{ + struct tdb_context *tdb; + struct stat st; + int rev = 0, locked = 0; + unsigned char *vp; + u32 vertest; + + if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) { + /* Can't log this */ + errno = ENOMEM; + goto fail; + } + tdb_io_init(tdb); + tdb->fd = -1; + tdb->name = NULL; + tdb->map_ptr = NULL; + tdb->flags = tdb_flags; + tdb->open_flags = open_flags; + if (log_ctx) { + tdb->log = *log_ctx; + } else { + tdb->log.log_fn = null_log_fn; + tdb->log.log_private = NULL; + } + tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; + + /* cache the page size */ + tdb->page_size = getpagesize(); + if (tdb->page_size <= 0) { + tdb->page_size = 0x2000; + } + + if ((open_flags & O_ACCMODE) == O_WRONLY) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n", + name)); + errno = EINVAL; + goto fail; + } + + if (hash_size == 0) + hash_size = DEFAULT_HASH_SIZE; + if ((open_flags & O_ACCMODE) == O_RDONLY) { + tdb->read_only = 1; + /* read only databases don't do locking or clear if first */ + tdb->flags |= TDB_NOLOCK; + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + } + + /* internal databases don't mmap or lock, and start off cleared */ + if (tdb->flags & TDB_INTERNAL) { + tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + if (tdb_new_database(tdb, hash_size) != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!")); + goto fail; + } + goto internal; + } + + if ((tdb->fd = open(name, open_flags, mode)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: could not open file %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by open(2) */ + } + + /* ensure there is only one process initialising at once */ + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by tdb_brlock */ + } + + /* we need to zero database if we are the only one with it open */ + if ((tdb_flags & TDB_CLEAR_IF_FIRST) && + (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) { + open_flags |= O_CREAT; + if (ftruncate(tdb->fd, 0) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "failed to truncate %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by ftruncate */ + } + } + + if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) + || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 + || (tdb->header.version != TDB_VERSION + && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { + /* its not a valid database - possibly initialise it */ + if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { + errno = EIO; /* ie bad format or something */ + goto fail; + } + rev = (tdb->flags & TDB_CONVERT); + } + vp = (unsigned char *)&tdb->header.version; + vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) | + (((u32)vp[2]) << 8) | (u32)vp[3]; + tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; + if (!rev) + tdb->flags &= ~TDB_CONVERT; + else { + tdb->flags |= TDB_CONVERT; + tdb_convert(&tdb->header, sizeof(tdb->header)); + } + if (fstat(tdb->fd, &st) == -1) + goto fail; + + if (tdb->header.rwlocks != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); + goto fail; + } + + /* Is it already in the open list? If so, fail. */ + if (tdb_already_open(st.st_dev, st.st_ino)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " + "%s (%d,%d) is already open in this process\n", + name, (int)st.st_dev, (int)st.st_ino)); + errno = EBUSY; + goto fail; + } + + if (!(tdb->name = (char *)strdup(name))) { + errno = ENOMEM; + goto fail; + } + + tdb->map_size = st.st_size; + tdb->device = st.st_dev; + tdb->inode = st.st_ino; + tdb->max_dead_records = 0; + tdb_mmap(tdb); + if (locked) { + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " + "failed to take ACTIVE_LOCK on %s: %s\n", + name, strerror(errno))); + goto fail; + } + + } + + /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if + we didn't get the initial exclusive lock as we need to let all other + users know we're using it. */ + + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + /* leave this lock in place to indicate it's in use */ + if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1) + goto fail; + } + + /* if needed, run recovery */ + if (tdb_transaction_recover(tdb) == -1) { + goto fail; + } + + internal: + /* Internal (memory-only) databases skip all the code above to + * do with disk files, and resume here by releasing their + * global lock and hooking into the active list. */ + if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1) + goto fail; + tdb->next = tdbs; + tdbs = tdb; + return tdb; + + fail: + { int save_errno = errno; + + if (!tdb) + return NULL; + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + if (close(tdb->fd) != 0) + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); + SAFE_FREE(tdb); + errno = save_errno; + return NULL; + } +} + +/* + * Set the maximum number of dead records per hash chain + */ + +void tdb_set_max_dead(struct tdb_context *tdb, int max_dead) +{ + tdb->max_dead_records = max_dead; +} + +/** + * Close a database. + * + * @returns -1 for error; 0 for success. + **/ +int tdb_close(struct tdb_context *tdb) +{ + struct tdb_context **i; + int ret = 0; + + if (tdb->transaction) { + tdb_transaction_cancel(tdb); + } + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + ret = close(tdb->fd); + SAFE_FREE(tdb->lockrecs); + + /* Remove from contexts list */ + for (i = &tdbs; *i; i = &(*i)->next) { + if (*i == tdb) { + *i = tdb->next; + break; + } + } + + memset(tdb, 0, sizeof(*tdb)); + SAFE_FREE(tdb); + + return ret; +} + +/* register a loging function */ +void tdb_set_logging_function(struct tdb_context *tdb, + const struct tdb_logging_context *log_ctx) +{ + tdb->log = *log_ctx; +} + +void *tdb_get_logging_private(struct tdb_context *tdb) +{ + return tdb->log.log_private; +} + +/* reopen a tdb - this can be used after a fork to ensure that we have an independent + seek pointer from our parent and to re-establish locks */ +int tdb_reopen(struct tdb_context *tdb) +{ + struct stat st; + + if (tdb->flags & TDB_INTERNAL) { + return 0; /* Nothing to do. */ + } + + if (tdb->num_locks != 0 || tdb->global_lock.count) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n")); + goto fail; + } + + if (tdb->transaction != 0) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n")); + goto fail; + } + + if (tdb_munmap(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); + goto fail; + } + if (close(tdb->fd) != 0) + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n")); + tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); + if (tdb->fd == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno))); + goto fail; + } + if ((tdb->flags & TDB_CLEAR_IF_FIRST) && + (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); + goto fail; + } + if (fstat(tdb->fd, &st) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); + goto fail; + } + if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n")); + goto fail; + } + tdb_mmap(tdb); + + return 0; + +fail: + tdb_close(tdb); + return -1; +} + +/* reopen all tdb's */ +int tdb_reopen_all(int parent_longlived) +{ + struct tdb_context *tdb; + + for (tdb=tdbs; tdb; tdb = tdb->next) { + /* + * If the parent is longlived (ie. a + * parent daemon architecture), we know + * it will keep it's active lock on a + * tdb opened with CLEAR_IF_FIRST. Thus + * for child processes we don't have to + * add an active lock. This is essential + * to improve performance on systems that + * keep POSIX locks as a non-scalable data + * structure in the kernel. + */ + if (parent_longlived) { + /* Ensure no clear-if-first. */ + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + } + + if (tdb_reopen(tdb) != 0) + return -1; + } + + return 0; +} diff --git a/lib/libext2fs/source/tdb.h b/lib/libext2fs/source/tdb.h new file mode 100644 index 0000000..bfcd943 --- /dev/null +++ b/lib/libext2fs/source/tdb.h @@ -0,0 +1,215 @@ +#ifndef __TDB_H__ +#define __TDB_H__ + +/* + Unix SMB/CIFS implementation. + + trivial database library + + Copyright (C) Andrew Tridgell 1999-2004 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* flags to tdb_store() */ +#define TDB_REPLACE 1 +#define TDB_INSERT 2 +#define TDB_MODIFY 3 + +/* flags for tdb_open() */ +#define TDB_DEFAULT 0 /* just a readability place holder */ +#define TDB_CLEAR_IF_FIRST 1 +#define TDB_INTERNAL 2 /* don't store on disk */ +#define TDB_NOLOCK 4 /* don't do any locking */ +#define TDB_NOMMAP 8 /* don't use mmap */ +#define TDB_CONVERT 16 /* convert endian (internal use) */ +#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ +#define TDB_NOSYNC 64 /* don't use synchronous transactions */ +#define TDB_SEQNUM 128 /* maintain a sequence number */ + +#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) + +/* error codes */ +enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, + TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, + TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY}; + +/* debugging uses one of the following levels */ +enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, + TDB_DEBUG_WARNING, TDB_DEBUG_TRACE}; + +typedef struct TDB_DATA { + unsigned char *dptr; + size_t dsize; +} TDB_DATA; + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* ext2fs tdb renames */ +#define tdb_open ext2fs_tdb_open +#define tdb_open_ex ext2fs_tdb_open_ex +#define tdb_set_max_dead ext2fs_tdb_set_max_dead +#define tdb_reopen ext2fs_tdb_reopen +#define tdb_reopen_all ext2fs_tdb_reopen_all +#define tdb_set_logging_function ext2fs_tdb_set_logging_function +#define tdb_error ext2fs_tdb_error +#define tdb_errorstr ext2fs_tdb_errorstr +#define tdb_fetch ext2fs_tdb_fetch +#define tdb_parse_record ext2fs_tdb_parse_record +#define tdb_delete ext2fs_tdb_delete +#define tdb_store ext2fs_tdb_store +#define tdb_append ext2fs_tdb_append +#define tdb_close ext2fs_tdb_close +#define tdb_firstkey ext2fs_tdb_firstkey +#define tdb_nextkey ext2fs_tdb_nextkey +#define tdb_traverse ext2fs_tdb_traverse +#define tdb_traverse_read ext2fs_tdb_traverse_read +#define tdb_exists ext2fs_tdb_exists +#define tdb_lockall ext2fs_tdb_lockall +#define tdb_unlockall ext2fs_tdb_unlockall +#define tdb_lockall_read ext2fs_tdb_lockall_read +#define tdb_unlockall_read ext2fs_tdb_unlockall_read +#define tdb_name ext2fs_tdb_name +#define tdb_fd ext2fs_tdb_fd +#define tdb_log_fn ext2fs_tdb_log_fn +#define tdb_get_logging_private ext2fs_tdb_get_logging_private +#define tdb_transaction_start ext2fs_tdb_transaction_start +#define tdb_transaction_commit ext2fs_tdb_transaction_commit +#define tdb_transaction_cancel ext2fs_tdb_transaction_cancel +#define tdb_transaction_recover ext2fs_tdb_transaction_recover +#define tdb_get_seqnum ext2fs_tdb_get_seqnum +#define tdb_hash_size ext2fs_tdb_hash_size +#define tdb_map_size ext2fs_tdb_map_size +#define tdb_get_flags ext2fs_tdb_get_flags +#define tdb_chainlock ext2fs_tdb_chainlock +#define tdb_chainunlock ext2fs_tdb_chainunlock +#define tdb_chainlock_read ext2fs_tdb_chainlock_read +#define tdb_chainunlock_read ext2fs_tdb_chainunlock_read +#define tdb_dump_all ext2fs_tdb_dump_all +#define tdb_printfreelist ext2fs_tdb_printfreelist +#define tdb_validate_freelist ext2fs_tdb_validate_freelist +#define tdb_chainlock_mark ext2fs_tdb_chainlock_mark +#define tdb_chainlock_nonblock ext2fs_tdb_chainlock_nonblock +#define tdb_chainlock_unmark ext2fs_tdb_chainlock_unmark +#define tdb_enable_seqnum ext2fs_tdb_enable_seqnum +#define tdb_increment_seqnum_nonblock ext2fs_tdb_increment_seqnum_nonblock +#define tdb_lock_nonblock ext2fs_tdb_lock_nonblock +#define tdb_lockall_mark ext2fs_tdb_lockall_mark +#define tdb_lockall_nonblock ext2fs_tdb_lockall_nonblock +#define tdb_lockall_read_nonblock ext2fs_tdb_lockall_read_nonblock +#define tdb_lockall_unmark ext2fs_tdb_lockall_unmark + +/* this is the context structure that is returned from a db open */ +typedef struct tdb_context TDB_CONTEXT; + +typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *); +typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_ATTRIBUTE(3, 4); +typedef unsigned int (*tdb_hash_func)(TDB_DATA *key); + +struct tdb_logging_context { + tdb_log_func log_fn; + void *log_private; +}; + +struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode); +struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + const struct tdb_logging_context *log_ctx, + tdb_hash_func hash_fn); +void tdb_set_max_dead(struct tdb_context *tdb, int max_dead); + +int tdb_reopen(struct tdb_context *tdb); +int tdb_reopen_all(int parent_longlived); +void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx); +enum TDB_ERROR tdb_error(struct tdb_context *tdb); +const char *tdb_errorstr(struct tdb_context *tdb); +TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key); +int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, + int (*parser)(TDB_DATA key, TDB_DATA data, + void *private_data), + void *private_data); +int tdb_delete(struct tdb_context *tdb, TDB_DATA key); +int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); +int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf); +int tdb_close(struct tdb_context *tdb); +TDB_DATA tdb_firstkey(struct tdb_context *tdb); +TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key); +int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *); +int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *); +int tdb_exists(struct tdb_context *tdb, TDB_DATA key); +int tdb_lockall(struct tdb_context *tdb); +int tdb_lockall_nonblock(struct tdb_context *tdb); +int tdb_unlockall(struct tdb_context *tdb); +int tdb_lockall_read(struct tdb_context *tdb); +int tdb_lockall_read_nonblock(struct tdb_context *tdb); +int tdb_unlockall_read(struct tdb_context *tdb); +int tdb_lockall_mark(struct tdb_context *tdb); +int tdb_lockall_unmark(struct tdb_context *tdb); +const char *tdb_name(struct tdb_context *tdb); +int tdb_fd(struct tdb_context *tdb); +tdb_log_func tdb_log_fn(struct tdb_context *tdb); +void *tdb_get_logging_private(struct tdb_context *tdb); +int tdb_transaction_start(struct tdb_context *tdb); +int tdb_transaction_commit(struct tdb_context *tdb); +int tdb_transaction_cancel(struct tdb_context *tdb); +int tdb_transaction_recover(struct tdb_context *tdb); +int tdb_get_seqnum(struct tdb_context *tdb); +int tdb_hash_size(struct tdb_context *tdb); +size_t tdb_map_size(struct tdb_context *tdb); +int tdb_get_flags(struct tdb_context *tdb); +void tdb_enable_seqnum(struct tdb_context *tdb); +void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); + +/* Low level locking functions: use with care */ +int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key); +int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key); + +/* Debug functions. Not used in production. */ +void tdb_dump_all(struct tdb_context *tdb); +int tdb_printfreelist(struct tdb_context *tdb); +int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); + +extern TDB_DATA tdb_null; + +#ifdef __cplusplus +} +#endif + +#endif /* tdb.h */ diff --git a/lib/libext2fs/source/unlink.c b/lib/libext2fs/source/unlink.c new file mode 100644 index 0000000..7ffeb9e --- /dev/null +++ b/lib/libext2fs/source/unlink.c @@ -0,0 +1,99 @@ +/* + * unlink.c --- delete links in a ext2fs directory + * + * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + struct ext2_dir_entry *prev; + int done; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int unlink_proc(struct ext2_dir_entry *dirent, + int offset, + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *prev; + + prev = ls->prev; + ls->prev = dirent; + + if (ls->name) { + if ((dirent->name_len & 0xFF) != ls->namelen) + return 0; + if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) + return 0; + } + if (ls->inode) { + if (dirent->inode != ls->inode) + return 0; + } else { + if (!dirent->inode) + return 0; + } + + if (offset) + prev->rec_len += dirent->rec_len; + else + dirent->inode = 0; + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, + const char *name, ext2_ino_t ino, + int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + struct link_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!name && !ino) + return EXT2_ET_INVALID_ARGUMENT; + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = 0; + ls.done = 0; + ls.prev = 0; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, unlink_proc, &ls); + if (retval) + return retval; + + return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; +} + diff --git a/lib/libext2fs/source/valid_blk.c b/lib/libext2fs/source/valid_blk.c new file mode 100644 index 0000000..ec3edd8 --- /dev/null +++ b/lib/libext2fs/source/valid_blk.c @@ -0,0 +1,55 @@ +/* + * valid_blk.c --- does the inode have valid blocks? + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This function returns 1 if the inode's block entries actually + * contain block entries. + */ +int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) +{ + /* + * Only directories, regular files, and some symbolic links + * have valid block entries. + */ + if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && + !LINUX_S_ISLNK(inode->i_mode)) + return 0; + + /* + * If the symbolic link is a "fast symlink", then the symlink + * target is stored in the block entries. + */ + if (LINUX_S_ISLNK (inode->i_mode)) { + if (ext2fs_file_acl_block(inode) == 0) { + /* With no EA block, we can rely on i_blocks */ + if (inode->i_blocks == 0) + return 0; + } else { + /* With an EA block, life gets more tricky */ + if (inode->i_size >= EXT2_N_BLOCKS*4) + return 1; /* definitely using i_block[] */ + if (inode->i_size > 4 && inode->i_block[1] == 0) + return 1; /* definitely using i_block[] */ + return 0; /* Probably a fast symlink */ + } + } + return 1; +} diff --git a/lib/libext2fs/source/version.c b/lib/libext2fs/source/version.c new file mode 100644 index 0000000..e7f223b --- /dev/null +++ b/lib/libext2fs/source/version.c @@ -0,0 +1,54 @@ +/* + * version.c --- Return the version of the ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +static const char *lib_version = ""; +static const char *lib_date = ""; + +int ext2fs_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0, dot_count = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') { + if (dot_count++) + break; + else + continue; + } + if (!isdigit((int)*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + + +int ext2fs_get_library_version(const char **ver_string, + const char **date_string) +{ + if (ver_string) + *ver_string = lib_version; + if (date_string) + *date_string = lib_date; + + return ext2fs_parse_version_string(lib_version); +} diff --git a/lib/libext2fs/source/wii_release/alloc.d b/lib/libext2fs/source/wii_release/alloc.d new file mode 100644 index 0000000..6e57efd --- /dev/null +++ b/lib/libext2fs/source/wii_release/alloc.d @@ -0,0 +1,31 @@ +alloc.o: c:/progging/cfgMod/lib/libext2fs/source/alloc.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/alloc_sb.d b/lib/libext2fs/source/wii_release/alloc_sb.d new file mode 100644 index 0000000..5328777 --- /dev/null +++ b/lib/libext2fs/source/wii_release/alloc_sb.d @@ -0,0 +1,31 @@ +alloc_sb.o: c:/progging/cfgMod/lib/libext2fs/source/alloc_sb.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/alloc_stats.d b/lib/libext2fs/source/wii_release/alloc_stats.d new file mode 100644 index 0000000..f47f1dc --- /dev/null +++ b/lib/libext2fs/source/wii_release/alloc_stats.d @@ -0,0 +1,31 @@ +alloc_stats.o: c:/progging/cfgMod/lib/libext2fs/source/alloc_stats.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/alloc_tables.d b/lib/libext2fs/source/wii_release/alloc_tables.d new file mode 100644 index 0000000..3132fe0 --- /dev/null +++ b/lib/libext2fs/source/wii_release/alloc_tables.d @@ -0,0 +1,34 @@ +alloc_tables.o: c:/progging/cfgMod/lib/libext2fs/source/alloc_tables.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: diff --git a/lib/libext2fs/source/wii_release/badblocks.d b/lib/libext2fs/source/wii_release/badblocks.d new file mode 100644 index 0000000..346eee7 --- /dev/null +++ b/lib/libext2fs/source/wii_release/badblocks.d @@ -0,0 +1,34 @@ +badblocks.o: c:/progging/cfgMod/lib/libext2fs/source/badblocks.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/bb_compat.d b/lib/libext2fs/source/wii_release/bb_compat.d new file mode 100644 index 0000000..41142be --- /dev/null +++ b/lib/libext2fs/source/wii_release/bb_compat.d @@ -0,0 +1,34 @@ +bb_compat.o: c:/progging/cfgMod/lib/libext2fs/source/bb_compat.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/bb_inode.d b/lib/libext2fs/source/wii_release/bb_inode.d new file mode 100644 index 0000000..214e87f --- /dev/null +++ b/lib/libext2fs/source/wii_release/bb_inode.d @@ -0,0 +1,31 @@ +bb_inode.o: c:/progging/cfgMod/lib/libext2fs/source/bb_inode.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/bitmaps.d b/lib/libext2fs/source/wii_release/bitmaps.d new file mode 100644 index 0000000..f9ff35e --- /dev/null +++ b/lib/libext2fs/source/wii_release/bitmaps.d @@ -0,0 +1,34 @@ +bitmaps.o: c:/progging/cfgMod/lib/libext2fs/source/bitmaps.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: diff --git a/lib/libext2fs/source/wii_release/bitops.d b/lib/libext2fs/source/wii_release/bitops.d new file mode 100644 index 0000000..94c323d --- /dev/null +++ b/lib/libext2fs/source/wii_release/bitops.d @@ -0,0 +1,31 @@ +bitops.o: c:/progging/cfgMod/lib/libext2fs/source/bitops.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/blkmap64_ba.d b/lib/libext2fs/source/wii_release/blkmap64_ba.d new file mode 100644 index 0000000..fbaa401 --- /dev/null +++ b/lib/libext2fs/source/wii_release/blkmap64_ba.d @@ -0,0 +1,37 @@ +blkmap64_ba.o: c:/progging/cfgMod/lib/libext2fs/source/blkmap64_ba.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/bmap64.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/bmap64.h: diff --git a/lib/libext2fs/source/wii_release/blknum.d b/lib/libext2fs/source/wii_release/blknum.d new file mode 100644 index 0000000..bc6b069 --- /dev/null +++ b/lib/libext2fs/source/wii_release/blknum.d @@ -0,0 +1,31 @@ +blknum.o: c:/progging/cfgMod/lib/libext2fs/source/blknum.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/block.d b/lib/libext2fs/source/wii_release/block.d new file mode 100644 index 0000000..16a12ae --- /dev/null +++ b/lib/libext2fs/source/wii_release/block.d @@ -0,0 +1,31 @@ +block.o: c:/progging/cfgMod/lib/libext2fs/source/block.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/bmap.d b/lib/libext2fs/source/wii_release/bmap.d new file mode 100644 index 0000000..29c5e2f --- /dev/null +++ b/lib/libext2fs/source/wii_release/bmap.d @@ -0,0 +1,31 @@ +bmap.o: c:/progging/cfgMod/lib/libext2fs/source/bmap.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/bmove.d b/lib/libext2fs/source/wii_release/bmove.d new file mode 100644 index 0000000..46b5f0f --- /dev/null +++ b/lib/libext2fs/source/wii_release/bmove.d @@ -0,0 +1,34 @@ +bmove.o: c:/progging/cfgMod/lib/libext2fs/source/bmove.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/brel_ma.d b/lib/libext2fs/source/wii_release/brel_ma.d new file mode 100644 index 0000000..844514c --- /dev/null +++ b/lib/libext2fs/source/wii_release/brel_ma.d @@ -0,0 +1,34 @@ +brel_ma.o: c:/progging/cfgMod/lib/libext2fs/source/brel_ma.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/brel.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/brel.h: diff --git a/lib/libext2fs/source/wii_release/check_desc.d b/lib/libext2fs/source/wii_release/check_desc.d new file mode 100644 index 0000000..48b6ac0 --- /dev/null +++ b/lib/libext2fs/source/wii_release/check_desc.d @@ -0,0 +1,31 @@ +check_desc.o: c:/progging/cfgMod/lib/libext2fs/source/check_desc.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/closefs.d b/lib/libext2fs/source/wii_release/closefs.d new file mode 100644 index 0000000..f57fda4 --- /dev/null +++ b/lib/libext2fs/source/wii_release/closefs.d @@ -0,0 +1,34 @@ +closefs.o: c:/progging/cfgMod/lib/libext2fs/source/closefs.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/com_err.d b/lib/libext2fs/source/wii_release/com_err.d new file mode 100644 index 0000000..7a486f2 --- /dev/null +++ b/lib/libext2fs/source/wii_release/com_err.d @@ -0,0 +1,187 @@ +com_err.o: c:/progging/cfgMod/lib/libext2fs/source/com_err.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: diff --git a/lib/libext2fs/source/wii_release/crc16.d b/lib/libext2fs/source/wii_release/crc16.d new file mode 100644 index 0000000..e85f4ec --- /dev/null +++ b/lib/libext2fs/source/wii_release/crc16.d @@ -0,0 +1,7 @@ +crc16.o: c:/progging/cfgMod/lib/libext2fs/source/crc16.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/crc16.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/crc16.h: diff --git a/lib/libext2fs/source/wii_release/csum.d b/lib/libext2fs/source/wii_release/csum.d new file mode 100644 index 0000000..58ef70e --- /dev/null +++ b/lib/libext2fs/source/wii_release/csum.d @@ -0,0 +1,34 @@ +csum.o: c:/progging/cfgMod/lib/libext2fs/source/csum.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/crc16.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/crc16.h: diff --git a/lib/libext2fs/source/wii_release/dblist.d b/lib/libext2fs/source/wii_release/dblist.d new file mode 100644 index 0000000..5d3d5bf --- /dev/null +++ b/lib/libext2fs/source/wii_release/dblist.d @@ -0,0 +1,34 @@ +dblist.o: c:/progging/cfgMod/lib/libext2fs/source/dblist.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/dblist_dir.d b/lib/libext2fs/source/wii_release/dblist_dir.d new file mode 100644 index 0000000..1b1c05a --- /dev/null +++ b/lib/libext2fs/source/wii_release/dblist_dir.d @@ -0,0 +1,34 @@ +dblist_dir.o: c:/progging/cfgMod/lib/libext2fs/source/dblist_dir.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/dir_iterate.d b/lib/libext2fs/source/wii_release/dir_iterate.d new file mode 100644 index 0000000..4058f6b --- /dev/null +++ b/lib/libext2fs/source/wii_release/dir_iterate.d @@ -0,0 +1,34 @@ +dir_iterate.o: c:/progging/cfgMod/lib/libext2fs/source/dir_iterate.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/dirblock.d b/lib/libext2fs/source/wii_release/dirblock.d new file mode 100644 index 0000000..94c65f2 --- /dev/null +++ b/lib/libext2fs/source/wii_release/dirblock.d @@ -0,0 +1,31 @@ +dirblock.o: c:/progging/cfgMod/lib/libext2fs/source/dirblock.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/dirhash.d b/lib/libext2fs/source/wii_release/dirhash.d new file mode 100644 index 0000000..2d00317 --- /dev/null +++ b/lib/libext2fs/source/wii_release/dirhash.d @@ -0,0 +1,31 @@ +dirhash.o: c:/progging/cfgMod/lib/libext2fs/source/dirhash.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/disc_cache.d b/lib/libext2fs/source/wii_release/disc_cache.d new file mode 100644 index 0000000..3048543 --- /dev/null +++ b/lib/libext2fs/source/wii_release/disc_cache.d @@ -0,0 +1,169 @@ +disc_cache.o: c:/progging/cfgMod/lib/libext2fs/source/disc_cache.c \ + c:/devkitPro/libogc/include/ogc/lwp_watchdog.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/bit_ops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/devkitPro/libogc/include/ogc/lwp_watchdog.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/bit_ops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/dupfs.d b/lib/libext2fs/source/wii_release/dupfs.d new file mode 100644 index 0000000..863c724 --- /dev/null +++ b/lib/libext2fs/source/wii_release/dupfs.d @@ -0,0 +1,34 @@ +dupfs.o: c:/progging/cfgMod/lib/libext2fs/source/dupfs.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/expanddir.d b/lib/libext2fs/source/wii_release/expanddir.d new file mode 100644 index 0000000..966783f --- /dev/null +++ b/lib/libext2fs/source/wii_release/expanddir.d @@ -0,0 +1,31 @@ +expanddir.o: c:/progging/cfgMod/lib/libext2fs/source/expanddir.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/ext2.d b/lib/libext2fs/source/wii_release/ext2.d new file mode 100644 index 0000000..11237d4 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext2.d @@ -0,0 +1,196 @@ +ext2.o: c:/progging/cfgMod/lib/libext2fs/source/ext2.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h \ + c:/progging/cfgMod/lib/libext2fs/source/partitions.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h: + +c:/progging/cfgMod/lib/libext2fs/source/partitions.h: diff --git a/lib/libext2fs/source/wii_release/ext2_frag.d b/lib/libext2fs/source/wii_release/ext2_frag.d new file mode 100644 index 0000000..fd789f6 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext2_frag.d @@ -0,0 +1,190 @@ +ext2_frag.o: c:/progging/cfgMod/lib/libext2fs/source/ext2_frag.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_frag.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_frag.h: diff --git a/lib/libext2fs/source/wii_release/ext2_internal.d b/lib/libext2fs/source/wii_release/ext2_internal.d new file mode 100644 index 0000000..3073b53 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext2_internal.d @@ -0,0 +1,199 @@ +ext2_internal.o: c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2dir.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2file.h \ + c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2dir.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2file.h: + +c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h: diff --git a/lib/libext2fs/source/wii_release/ext2dir.d b/lib/libext2fs/source/wii_release/ext2dir.d new file mode 100644 index 0000000..ad95997 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext2dir.d @@ -0,0 +1,190 @@ +ext2dir.o: c:/progging/cfgMod/lib/libext2fs/source/ext2dir.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2dir.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2dir.h: diff --git a/lib/libext2fs/source/wii_release/ext2file.d b/lib/libext2fs/source/wii_release/ext2file.d new file mode 100644 index 0000000..cbd5626 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext2file.d @@ -0,0 +1,190 @@ +ext2file.o: c:/progging/cfgMod/lib/libext2fs/source/ext2file.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2file.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2file.h: diff --git a/lib/libext2fs/source/wii_release/ext_attr.d b/lib/libext2fs/source/wii_release/ext_attr.d new file mode 100644 index 0000000..82da163 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ext_attr.d @@ -0,0 +1,31 @@ +ext_attr.o: c:/progging/cfgMod/lib/libext2fs/source/ext_attr.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/extent.d b/lib/libext2fs/source/wii_release/extent.d new file mode 100644 index 0000000..348fcf9 --- /dev/null +++ b/lib/libext2fs/source/wii_release/extent.d @@ -0,0 +1,37 @@ +extent.o: c:/progging/cfgMod/lib/libext2fs/source/extent.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/e2image.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/e2image.h: diff --git a/lib/libext2fs/source/wii_release/fileio.d b/lib/libext2fs/source/wii_release/fileio.d new file mode 100644 index 0000000..bceeb65 --- /dev/null +++ b/lib/libext2fs/source/wii_release/fileio.d @@ -0,0 +1,31 @@ +fileio.o: c:/progging/cfgMod/lib/libext2fs/source/fileio.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/finddev.d b/lib/libext2fs/source/wii_release/finddev.d new file mode 100644 index 0000000..72ade74 --- /dev/null +++ b/lib/libext2fs/source/wii_release/finddev.d @@ -0,0 +1,31 @@ +finddev.o: c:/progging/cfgMod/lib/libext2fs/source/finddev.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/flushb.d b/lib/libext2fs/source/wii_release/flushb.d new file mode 100644 index 0000000..5522922 --- /dev/null +++ b/lib/libext2fs/source/wii_release/flushb.d @@ -0,0 +1,31 @@ +flushb.o: c:/progging/cfgMod/lib/libext2fs/source/flushb.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/freefs.d b/lib/libext2fs/source/wii_release/freefs.d new file mode 100644 index 0000000..b3f9c9b --- /dev/null +++ b/lib/libext2fs/source/wii_release/freefs.d @@ -0,0 +1,34 @@ +freefs.o: c:/progging/cfgMod/lib/libext2fs/source/freefs.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/gekko_io.d b/lib/libext2fs/source/wii_release/gekko_io.d new file mode 100644 index 0000000..ee43c0c --- /dev/null +++ b/lib/libext2fs/source/wii_release/gekko_io.d @@ -0,0 +1,193 @@ +gekko_io.o: c:/progging/cfgMod/lib/libext2fs/source/gekko_io.c \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libext2fs/source/gekko_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/disc_cache.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_internal.h: diff --git a/lib/libext2fs/source/wii_release/gen_bitmap.d b/lib/libext2fs/source/wii_release/gen_bitmap.d new file mode 100644 index 0000000..3257323 --- /dev/null +++ b/lib/libext2fs/source/wii_release/gen_bitmap.d @@ -0,0 +1,34 @@ +gen_bitmap.o: c:/progging/cfgMod/lib/libext2fs/source/gen_bitmap.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: diff --git a/lib/libext2fs/source/wii_release/gen_bitmap64.d b/lib/libext2fs/source/wii_release/gen_bitmap64.d new file mode 100644 index 0000000..64251e6 --- /dev/null +++ b/lib/libext2fs/source/wii_release/gen_bitmap64.d @@ -0,0 +1,37 @@ +gen_bitmap64.o: c:/progging/cfgMod/lib/libext2fs/source/gen_bitmap64.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/bmap64.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/bmap64.h: diff --git a/lib/libext2fs/source/wii_release/get_pathname.d b/lib/libext2fs/source/wii_release/get_pathname.d new file mode 100644 index 0000000..f7d2186 --- /dev/null +++ b/lib/libext2fs/source/wii_release/get_pathname.d @@ -0,0 +1,31 @@ +get_pathname.o: c:/progging/cfgMod/lib/libext2fs/source/get_pathname.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/getsectsize.d b/lib/libext2fs/source/wii_release/getsectsize.d new file mode 100644 index 0000000..cd74144 --- /dev/null +++ b/lib/libext2fs/source/wii_release/getsectsize.d @@ -0,0 +1,31 @@ +getsectsize.o: c:/progging/cfgMod/lib/libext2fs/source/getsectsize.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/getsize.d b/lib/libext2fs/source/wii_release/getsize.d new file mode 100644 index 0000000..0925beb --- /dev/null +++ b/lib/libext2fs/source/wii_release/getsize.d @@ -0,0 +1,31 @@ +getsize.o: c:/progging/cfgMod/lib/libext2fs/source/getsize.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/i_block.d b/lib/libext2fs/source/wii_release/i_block.d new file mode 100644 index 0000000..cdaa7dd --- /dev/null +++ b/lib/libext2fs/source/wii_release/i_block.d @@ -0,0 +1,31 @@ +i_block.o: c:/progging/cfgMod/lib/libext2fs/source/i_block.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/icount.d b/lib/libext2fs/source/wii_release/icount.d new file mode 100644 index 0000000..24f5af1 --- /dev/null +++ b/lib/libext2fs/source/wii_release/icount.d @@ -0,0 +1,34 @@ +icount.o: c:/progging/cfgMod/lib/libext2fs/source/icount.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/tdb.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/tdb.h: diff --git a/lib/libext2fs/source/wii_release/imager.d b/lib/libext2fs/source/wii_release/imager.d new file mode 100644 index 0000000..58b30e5 --- /dev/null +++ b/lib/libext2fs/source/wii_release/imager.d @@ -0,0 +1,31 @@ +imager.o: c:/progging/cfgMod/lib/libext2fs/source/imager.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/ind_block.d b/lib/libext2fs/source/wii_release/ind_block.d new file mode 100644 index 0000000..9bc3de5 --- /dev/null +++ b/lib/libext2fs/source/wii_release/ind_block.d @@ -0,0 +1,31 @@ +ind_block.o: c:/progging/cfgMod/lib/libext2fs/source/ind_block.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/initialize.d b/lib/libext2fs/source/wii_release/initialize.d new file mode 100644 index 0000000..225d519 --- /dev/null +++ b/lib/libext2fs/source/wii_release/initialize.d @@ -0,0 +1,31 @@ +initialize.o: c:/progging/cfgMod/lib/libext2fs/source/initialize.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/inline.d b/lib/libext2fs/source/wii_release/inline.d new file mode 100644 index 0000000..1b9260f --- /dev/null +++ b/lib/libext2fs/source/wii_release/inline.d @@ -0,0 +1,31 @@ +inline.o: c:/progging/cfgMod/lib/libext2fs/source/inline.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/inode.d b/lib/libext2fs/source/wii_release/inode.d new file mode 100644 index 0000000..d5fab90 --- /dev/null +++ b/lib/libext2fs/source/wii_release/inode.d @@ -0,0 +1,37 @@ +inode.o: c:/progging/cfgMod/lib/libext2fs/source/inode.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/e2image.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/e2image.h: diff --git a/lib/libext2fs/source/wii_release/inode_io.d b/lib/libext2fs/source/wii_release/inode_io.d new file mode 100644 index 0000000..0ca97b6 --- /dev/null +++ b/lib/libext2fs/source/wii_release/inode_io.d @@ -0,0 +1,31 @@ +inode_io.o: c:/progging/cfgMod/lib/libext2fs/source/inode_io.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/io_manager.d b/lib/libext2fs/source/wii_release/io_manager.d new file mode 100644 index 0000000..8e247fd --- /dev/null +++ b/lib/libext2fs/source/wii_release/io_manager.d @@ -0,0 +1,31 @@ +io_manager.o: c:/progging/cfgMod/lib/libext2fs/source/io_manager.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/irel_ma.d b/lib/libext2fs/source/wii_release/irel_ma.d new file mode 100644 index 0000000..455e898 --- /dev/null +++ b/lib/libext2fs/source/wii_release/irel_ma.d @@ -0,0 +1,34 @@ +irel_ma.o: c:/progging/cfgMod/lib/libext2fs/source/irel_ma.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/irel.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/irel.h: diff --git a/lib/libext2fs/source/wii_release/ismounted.d b/lib/libext2fs/source/wii_release/ismounted.d new file mode 100644 index 0000000..0ca237e --- /dev/null +++ b/lib/libext2fs/source/wii_release/ismounted.d @@ -0,0 +1,31 @@ +ismounted.o: c:/progging/cfgMod/lib/libext2fs/source/ismounted.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/link.d b/lib/libext2fs/source/wii_release/link.d new file mode 100644 index 0000000..09e1f62 --- /dev/null +++ b/lib/libext2fs/source/wii_release/link.d @@ -0,0 +1,31 @@ +link.o: c:/progging/cfgMod/lib/libext2fs/source/link.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/llseek.d b/lib/libext2fs/source/wii_release/llseek.d new file mode 100644 index 0000000..3d93e28 --- /dev/null +++ b/lib/libext2fs/source/wii_release/llseek.d @@ -0,0 +1,31 @@ +llseek.o: c:/progging/cfgMod/lib/libext2fs/source/llseek.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/lookup.d b/lib/libext2fs/source/wii_release/lookup.d new file mode 100644 index 0000000..794c4d6 --- /dev/null +++ b/lib/libext2fs/source/wii_release/lookup.d @@ -0,0 +1,31 @@ +lookup.o: c:/progging/cfgMod/lib/libext2fs/source/lookup.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/mkdir.d b/lib/libext2fs/source/wii_release/mkdir.d new file mode 100644 index 0000000..b2f49e6 --- /dev/null +++ b/lib/libext2fs/source/wii_release/mkdir.d @@ -0,0 +1,31 @@ +mkdir.o: c:/progging/cfgMod/lib/libext2fs/source/mkdir.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/mkjournal.d b/lib/libext2fs/source/wii_release/mkjournal.d new file mode 100644 index 0000000..a1adee4 --- /dev/null +++ b/lib/libext2fs/source/wii_release/mkjournal.d @@ -0,0 +1,49 @@ +mkjournal.o: c:/progging/cfgMod/lib/libext2fs/source/mkjournal.c \ + c:/devkitPro/libogc/include/network.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/jfs_user.h \ + c:/progging/cfgMod/lib/libext2fs/source/kernel-jbd.h \ + c:/progging/cfgMod/lib/libext2fs/source/jfs_compat.h \ + c:/progging/cfgMod/lib/libext2fs/source/kernel-list.h + +c:/devkitPro/libogc/include/network.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/jfs_user.h: + +c:/progging/cfgMod/lib/libext2fs/source/kernel-jbd.h: + +c:/progging/cfgMod/lib/libext2fs/source/jfs_compat.h: + +c:/progging/cfgMod/lib/libext2fs/source/kernel-list.h: diff --git a/lib/libext2fs/source/wii_release/namei.d b/lib/libext2fs/source/wii_release/namei.d new file mode 100644 index 0000000..6539b3a --- /dev/null +++ b/lib/libext2fs/source/wii_release/namei.d @@ -0,0 +1,31 @@ +namei.o: c:/progging/cfgMod/lib/libext2fs/source/namei.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/native.d b/lib/libext2fs/source/wii_release/native.d new file mode 100644 index 0000000..0836370 --- /dev/null +++ b/lib/libext2fs/source/wii_release/native.d @@ -0,0 +1,31 @@ +native.o: c:/progging/cfgMod/lib/libext2fs/source/native.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/newdir.d b/lib/libext2fs/source/wii_release/newdir.d new file mode 100644 index 0000000..793f012 --- /dev/null +++ b/lib/libext2fs/source/wii_release/newdir.d @@ -0,0 +1,31 @@ +newdir.o: c:/progging/cfgMod/lib/libext2fs/source/newdir.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/openfs.d b/lib/libext2fs/source/wii_release/openfs.d new file mode 100644 index 0000000..17de8ea --- /dev/null +++ b/lib/libext2fs/source/wii_release/openfs.d @@ -0,0 +1,34 @@ +openfs.o: c:/progging/cfgMod/lib/libext2fs/source/openfs.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/e2image.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/e2image.h: diff --git a/lib/libext2fs/source/wii_release/progress.d b/lib/libext2fs/source/wii_release/progress.d new file mode 100644 index 0000000..0f57dc0 --- /dev/null +++ b/lib/libext2fs/source/wii_release/progress.d @@ -0,0 +1,34 @@ +progress.o: c:/progging/cfgMod/lib/libext2fs/source/progress.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: diff --git a/lib/libext2fs/source/wii_release/punch.d b/lib/libext2fs/source/wii_release/punch.d new file mode 100644 index 0000000..469a757 --- /dev/null +++ b/lib/libext2fs/source/wii_release/punch.d @@ -0,0 +1,31 @@ +punch.o: c:/progging/cfgMod/lib/libext2fs/source/punch.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/read_bb.d b/lib/libext2fs/source/wii_release/read_bb.d new file mode 100644 index 0000000..371f5ed --- /dev/null +++ b/lib/libext2fs/source/wii_release/read_bb.d @@ -0,0 +1,31 @@ +read_bb.o: c:/progging/cfgMod/lib/libext2fs/source/read_bb.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/read_bb_file.d b/lib/libext2fs/source/wii_release/read_bb_file.d new file mode 100644 index 0000000..d824530 --- /dev/null +++ b/lib/libext2fs/source/wii_release/read_bb_file.d @@ -0,0 +1,31 @@ +read_bb_file.o: c:/progging/cfgMod/lib/libext2fs/source/read_bb_file.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/res_gdt.d b/lib/libext2fs/source/wii_release/res_gdt.d new file mode 100644 index 0000000..167a7b4 --- /dev/null +++ b/lib/libext2fs/source/wii_release/res_gdt.d @@ -0,0 +1,31 @@ +res_gdt.o: c:/progging/cfgMod/lib/libext2fs/source/res_gdt.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/rw_bitmaps.d b/lib/libext2fs/source/wii_release/rw_bitmaps.d new file mode 100644 index 0000000..8e55611 --- /dev/null +++ b/lib/libext2fs/source/wii_release/rw_bitmaps.d @@ -0,0 +1,34 @@ +rw_bitmaps.o: c:/progging/cfgMod/lib/libext2fs/source/rw_bitmaps.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libext2fs/source/e2image.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libext2fs/source/e2image.h: diff --git a/lib/libext2fs/source/wii_release/sparse.d b/lib/libext2fs/source/wii_release/sparse.d new file mode 100644 index 0000000..7b1171c --- /dev/null +++ b/lib/libext2fs/source/wii_release/sparse.d @@ -0,0 +1,34 @@ +sparse.o: c:/progging/cfgMod/lib/libext2fs/source/sparse.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fsP.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/swapfs.d b/lib/libext2fs/source/wii_release/swapfs.d new file mode 100644 index 0000000..72bb047 --- /dev/null +++ b/lib/libext2fs/source/wii_release/swapfs.d @@ -0,0 +1,31 @@ +swapfs.o: c:/progging/cfgMod/lib/libext2fs/source/swapfs.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/tdb.d b/lib/libext2fs/source/wii_release/tdb.d new file mode 100644 index 0000000..1ee1d8c --- /dev/null +++ b/lib/libext2fs/source/wii_release/tdb.d @@ -0,0 +1,4 @@ +tdb.o: c:/progging/cfgMod/lib/libext2fs/source/tdb.c \ + c:/progging/cfgMod/lib/libext2fs/source/tdb.h + +c:/progging/cfgMod/lib/libext2fs/source/tdb.h: diff --git a/lib/libext2fs/source/wii_release/unlink.d b/lib/libext2fs/source/wii_release/unlink.d new file mode 100644 index 0000000..a6c4fce --- /dev/null +++ b/lib/libext2fs/source/wii_release/unlink.d @@ -0,0 +1,31 @@ +unlink.o: c:/progging/cfgMod/lib/libext2fs/source/unlink.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/valid_blk.d b/lib/libext2fs/source/wii_release/valid_blk.d new file mode 100644 index 0000000..8a1954f --- /dev/null +++ b/lib/libext2fs/source/wii_release/valid_blk.d @@ -0,0 +1,31 @@ +valid_blk.o: c:/progging/cfgMod/lib/libext2fs/source/valid_blk.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/version.d b/lib/libext2fs/source/wii_release/version.d new file mode 100644 index 0000000..b76d2a5 --- /dev/null +++ b/lib/libext2fs/source/wii_release/version.d @@ -0,0 +1,31 @@ +version.o: c:/progging/cfgMod/lib/libext2fs/source/version.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/wii_release/write_bb_file.d b/lib/libext2fs/source/wii_release/write_bb_file.d new file mode 100644 index 0000000..f1fde7f --- /dev/null +++ b/lib/libext2fs/source/wii_release/write_bb_file.d @@ -0,0 +1,31 @@ +write_bb_file.o: c:/progging/cfgMod/lib/libext2fs/source/write_bb_file.c \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h \ + c:/progging/cfgMod/lib/libext2fs/source/com_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h \ + c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h \ + c:/progging/cfgMod/lib/libext2fs/source/bitops.h \ + c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h + +c:/progging/cfgMod/lib/libext2fs/source/ext2_fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_types.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2fs.h: + +c:/progging/cfgMod/lib/libext2fs/source/com_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext3_extents.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_io.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_err.h: + +c:/progging/cfgMod/lib/libext2fs/source/ext2_ext_attr.h: + +c:/progging/cfgMod/lib/libext2fs/source/bitops.h: + +c:/progging/cfgMod/lib/libext2fs/source/mem_allocate.h: diff --git a/lib/libext2fs/source/write_bb_file.c b/lib/libext2fs/source/write_bb_file.c new file mode 100644 index 0000000..70bcf08 --- /dev/null +++ b/lib/libext2fs/source/write_bb_file.c @@ -0,0 +1,34 @@ +/* + * write_bb_file.c --- write a list of bad blocks to a FILE * + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags EXT2FS_ATTR((unused)), + FILE *f) +{ + badblocks_iterate bb_iter; + blk_t blk; + errcode_t retval; + + retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); + if (retval) + return retval; + + while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { + fprintf(f, "%u\n", blk); + } + ext2fs_badblocks_list_iterate_end(bb_iter); + return 0; +} diff --git a/lib/libfat/include/fat.h b/lib/libfat/include/fat.h new file mode 100644 index 0000000..b0ffc13 --- /dev/null +++ b/lib/libfat/include/fat.h @@ -0,0 +1,104 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 - 2009 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#include + +#if defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include "nds/disc_io.h" +# else +# include "disc_io.h" +# endif +#endif + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/lib/libfat/include/libfatversion.h b/lib/libfat/include/libfatversion.h new file mode 100644 index 0000000..6064d5c --- /dev/null +++ b/lib/libfat/include/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 0 +#define _LIBFAT_PATCH_ 9 + +#define _LIBFAT_STRING "libFAT Release 1.0.9" + +#endif // __LIBFATVERSION_H__ diff --git a/lib/libfat/src/Makefile b/lib/libfat/src/Makefile new file mode 100644 index 0000000..b7df0aa --- /dev/null +++ b/lib/libfat/src/Makefile @@ -0,0 +1,118 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro) +endif + +export TOPDIR := $(CURDIR) + +export LIBFAT_MAJOR := 1 +export LIBFAT_MINOR := 0 +export LIBFAT_PATCH := 9 + +export VERSTRING := $(LIBFAT_MAJOR).$(LIBFAT_MINOR).$(LIBFAT_PATCH) + + +default: wii-release + +all: release dist + +release: include/libfatversion.h nds-release gba-release cube-release wii-release + +ogc-release: cube-release wii-release + +nds-release: + $(MAKE) -C nds BUILD=release + +gba-release: + $(MAKE) -C gba BUILD=release + +cube-release: + $(MAKE) -C libogc PLATFORM=cube BUILD=cube_release + +wii-release: + $(MAKE) -C libogc PLATFORM=wii BUILD=wii_release + +debug: nds-debug gba-debug cube-debug wii-debug + +ogc-debug: cube-debug wii-debug + +nds-debug: + $(MAKE) -C nds BUILD=debug + +gba-debug: + $(MAKE) -C gba BUILD=debug + + +cube-debug: + $(MAKE) -C libogc PLATFORM=cube BUILD=wii_debug + +wii-debug: + $(MAKE) -C libogc PLATFORM=wii BUILD=cube_debug + +#clean: nds-clean gba-clean ogc-clean +clean: ogc-clean + +nds-clean: + $(MAKE) -C nds clean + +gba-clean: + $(MAKE) -C gba clean + +ogc-clean: + $(MAKE) -C libogc clean + +dist-bin: nds-dist-bin gba-dist-bin ogc-dist-bin + +nds-dist-bin: nds-release distribute/$(VERSTRING) + $(MAKE) -C nds dist-bin + +gba-dist-bin: gba-release distribute/$(VERSTRING) + $(MAKE) -C gba dist-bin + +ogc-dist-bin: ogc-release distribute/$(VERSTRING) + $(MAKE) -C libogc dist-bin + +dist-src: distribute/$(VERSTRING) + @tar --exclude=.svn --exclude=*CVS* -cvjf distribute/$(VERSTRING)/libfat-src-$(VERSTRING).tar.bz2 \ + source include Makefile \ + nds/Makefile nds/include \ + gba/Makefile gba/include \ + libogc/Makefile libogc/include + +dist: include/libfatversion.h dist-bin dist-src + +distribute/$(VERSTRING): + @[ -d $@ ] || mkdir -p $@ + +include/libfatversion.h : Makefile + @echo "#ifndef __LIBFATVERSION_H__" > $@ + @echo "#define __LIBFATVERSION_H__" >> $@ + @echo >> $@ + @echo "#define _LIBFAT_MAJOR_ $(LIBFAT_MAJOR)" >> $@ + @echo "#define _LIBFAT_MINOR_ $(LIBFAT_MINOR)" >> $@ + @echo "#define _LIBFAT_PATCH_ $(LIBFAT_PATCH)" >> $@ + @echo >> $@ + @echo '#define _LIBFAT_STRING "libFAT Release '$(LIBFAT_MAJOR).$(LIBFAT_MINOR).$(LIBFAT_PATCH)'"' >> $@ + @echo >> $@ + @echo "#endif // __LIBFATVERSION_H__" >> $@ + +# install: nds-install gba-install ogc-install + +nds-install: nds-release + $(MAKE) -C nds install + +gba-install: gba-release + $(MAKE) -C gba install + +ogc-install: cube-release wii-release + $(MAKE) -C libogc install + +include $(DEVKITPPC)/wii_rules +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../include +DEST_LIB := ../lib$(DKV) + +install: wii-release + mkdir -p $(DEST_LIB) $(DEST_INC) + cp libogc/lib/wii/libfat.a $(DEST_LIB) + cp include/*.h $(DEST_INC) + diff --git a/lib/libfat/src/gba/Makefile b/lib/libfat/src/gba/Makefile new file mode 100644 index 0000000..d9c6039 --- /dev/null +++ b/lib/libfat/src/gba/Makefile @@ -0,0 +1,160 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) +endif + +include $(DEVKITARM)/gba_rules + + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# LIB is where the built library will be placed +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD ?= release +SOURCES := ../source +INCLUDES := ../include +DATA := +LIB := $(TOPDIR)/gba/lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O2\ + -mcpu=arm7tdmi -mtune=arm7tdmi\ + -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DGBA +CXXFLAGS := $(CFLAGS) + +ASFLAGS := -g $(ARCH) + +ifneq ($(BUILD),debug) +export GBABIN := $(LIB)/libfat.a +else +export GBABIN := $(LIB)/libfatd.a +CFLAGS += -DFAT_DEBUG +endif + + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := +#-lnds9 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBGBA) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR ?= $(CURDIR)/.. + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AR := $(PREFIX)ar +export OBJCOPY := $(PREFIX)objcopy + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr debug release $(LIB) + +all: $(GBABIN) + +dist-bin: + @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-gba-$(VERSTRING).tar.bz2 include lib + +install: + cp lib/libfat.a $(DEVKITPRO)/libgba/lib + cp include/fat.h $(DEVKITPRO)/libgba/include + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(GBABIN) : $(OFILES) $(LIB) + @rm -f "$(GBABIN)" + @$(AR) rcs "$(GBABIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIB): + mkdir $(LIB) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libfat/src/gba/include/fat.h b/lib/libfat/src/gba/include/fat.h new file mode 100644 index 0000000..b0a9b0d --- /dev/null +++ b/lib/libfat/src/gba/include/fat.h @@ -0,0 +1,81 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "disc_io.h" + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/lib/libfat/src/include/fat.h b/lib/libfat/src/include/fat.h new file mode 100644 index 0000000..b0ffc13 --- /dev/null +++ b/lib/libfat/src/include/fat.h @@ -0,0 +1,104 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 - 2009 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#include + +#if defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include "nds/disc_io.h" +# else +# include "disc_io.h" +# endif +#endif + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/lib/libfat/src/include/libfatversion.h b/lib/libfat/src/include/libfatversion.h new file mode 100644 index 0000000..6064d5c --- /dev/null +++ b/lib/libfat/src/include/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 0 +#define _LIBFAT_PATCH_ 9 + +#define _LIBFAT_STRING "libFAT Release 1.0.9" + +#endif // __LIBFATVERSION_H__ diff --git a/lib/libfat/src/libogc/Makefile b/lib/libfat/src/libogc/Makefile new file mode 100644 index 0000000..50af584 --- /dev/null +++ b/lib/libfat/src/libogc/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC) +endif + +ifeq ($(PLATFORM),wii) +include $(DEVKITPPC)/wii_rules +endif + +ifeq ($(PLATFORM),cube) +include $(DEVKITPPC)/gamecube_rules +endif + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# LIBDIR is where the built library will be placed +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD ?= wii_release +SOURCES := ../source +INCLUDES := ../include +DATA := +LIBDIR := $(TOPDIR)/libogc/lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -g -Os -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +ASFLAGS := -g + +ifneq ($(BUILD),debug) +export CUBEBIN := $(LIBDIR)/$(PLATFORM)/libfat.a +else +export CUBEBIN := $(LIBDIR)/$(PLATFORM)/libfatd.a +CFLAGS += -DFAT_DEBUG +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR ?= $(CURDIR)/.. + + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) + +all: $(CUBEBIN) + +dist-bin: + @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-ogc-$(VERSTRING).tar.bz2 include lib + +install: + cp lib/wii/libfat.a $(DEVKITPRO)/libogc/lib/wii + cp lib/cube/libfat.a $(DEVKITPRO)/libogc/lib/cube + cp include/fat.h $(DEVKITPRO)/libogc/include + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(CUBEBIN) : $(OFILES) $(LIBDIR)/$(PLATFORM) + @rm -f "$(CUBEBIN)" + @$(AR) rcs "$(CUBEBIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIBDIR)/$(PLATFORM): + mkdir -p $(LIBDIR)/$(PLATFORM) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libfat/src/libogc/include/fat.h b/lib/libfat/src/libogc/include/fat.h new file mode 100644 index 0000000..31efbe0 --- /dev/null +++ b/lib/libfat/src/libogc/include/fat.h @@ -0,0 +1,86 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/lib/libfat/src/libogc/wii_release/cache.d b/lib/libfat/src/libogc/wii_release/cache.d new file mode 100644 index 0000000..180cd80 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/cache.d @@ -0,0 +1,181 @@ +cache.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: diff --git a/lib/libfat/src/libogc/wii_release/directory.d b/lib/libfat/src/libogc/wii_release/directory.d new file mode 100644 index 0000000..b9504f5 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/directory.d @@ -0,0 +1,185 @@ +directory.o: \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h: diff --git a/lib/libfat/src/libogc/wii_release/disc.d b/lib/libfat/src/libogc/wii_release/disc.d new file mode 100644 index 0000000..6b9fdce --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/disc.d @@ -0,0 +1,172 @@ +disc.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/devkitPro/libogc/include/sdcard/wiisd_io.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/sdcard/gcsd.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/devkitPro/libogc/include/sdcard/wiisd_io.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/sdcard/gcsd.h: diff --git a/lib/libfat/src/libogc/wii_release/fat_frag.d b/lib/libfat/src/libogc/wii_release/fat_frag.d new file mode 100644 index 0000000..b1526e7 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/fat_frag.d @@ -0,0 +1,187 @@ +fat_frag.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/fat_frag.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h: diff --git a/lib/libfat/src/libogc/wii_release/fatdir.d b/lib/libfat/src/libogc/wii_release/fatdir.d new file mode 100644 index 0000000..a29cb67 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/fatdir.d @@ -0,0 +1,187 @@ +fatdir.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatdir.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatdir.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatdir.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h: diff --git a/lib/libfat/src/libogc/wii_release/fatfile.d b/lib/libfat/src/libogc/wii_release/fatfile.d new file mode 100644 index 0000000..c809c95 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/fatfile.d @@ -0,0 +1,187 @@ +fatfile.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h: diff --git a/lib/libfat/src/libogc/wii_release/file_allocation_table.d b/lib/libfat/src/libogc/wii_release/file_allocation_table.d new file mode 100644 index 0000000..d87d3c9 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/file_allocation_table.d @@ -0,0 +1,179 @@ +file_allocation_table.o: \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h: diff --git a/lib/libfat/src/libogc/wii_release/filetime.d b/lib/libfat/src/libogc/wii_release/filetime.d new file mode 100644 index 0000000..59bd7e5 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/filetime.d @@ -0,0 +1,163 @@ +filetime.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/filetime.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: diff --git a/lib/libfat/src/libogc/wii_release/libfat.d b/lib/libfat/src/libogc/wii_release/libfat.d new file mode 100644 index 0000000..076986a --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/libfat.d @@ -0,0 +1,184 @@ +libfat.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/libfat.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatdir.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatdir.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h: diff --git a/lib/libfat/src/libogc/wii_release/lock.d b/lib/libfat/src/libogc/wii_release/lock.d new file mode 100644 index 0000000..580017a --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/lock.d @@ -0,0 +1,160 @@ +lock.o: c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: diff --git a/lib/libfat/src/libogc/wii_release/partition.d b/lib/libfat/src/libogc/wii_release/partition.d new file mode 100644 index 0000000..5909959 --- /dev/null +++ b/lib/libfat/src/libogc/wii_release/partition.d @@ -0,0 +1,188 @@ +partition.o: \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.c \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h \ + c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/partition.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/common.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../include/fat.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/cache.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/disc.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/lock.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/bit_ops.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/file_allocation_table.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/directory.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/mem_allocate.h: + +c:/progging/cfgMod/lib/libfat/src/libogc/../source/fatfile.h: diff --git a/lib/libfat/src/nds/Makefile b/lib/libfat/src/nds/Makefile new file mode 100644 index 0000000..d9135c6 --- /dev/null +++ b/lib/libfat/src/nds/Makefile @@ -0,0 +1,131 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# LIB is where the built library will be placed +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD ?= release +SOURCES := ../source source +INCLUDES := ../include +DATA := +LIB := $(TOPDIR)/nds/lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O2\ + -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 -DNDS +CXXFLAGS := $(CFLAGS) + +ASFLAGS := -g $(ARCH) + +ifneq ($(BUILD),debug) +export ARM9BIN := $(LIB)/libfat.a +else +export ARM9BIN := $(LIB)/libfatd.a +CFLAGS += -DFAT_DEBUG +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR ?= $(CURDIR)/.. + + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr debug release $(LIB) + +all: $(ARM9BIN) + +dist-bin: + @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-nds-$(VERSTRING).tar.bz2 include lib + +install: + cp lib/libfat.a $(DEVKITPRO)/libnds/lib + cp include/fat.h $(DEVKITPRO)/libnds/include + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM9BIN) : $(OFILES) $(LIB) + @rm -f "$(ARM9BIN)" + @$(AR) rcs "$(ARM9BIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIB): + mkdir $(LIB) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libfat/src/nds/include/fat.h b/lib/libfat/src/nds/include/fat.h new file mode 100644 index 0000000..5272b80 --- /dev/null +++ b/lib/libfat/src/nds/include/fat.h @@ -0,0 +1,86 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS +#define NDS +#endif + +#include +#include "nds/disc_io.h" + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/lib/libfat/src/source/bit_ops.h b/lib/libfat/src/source/bit_ops.h new file mode 100644 index 0000000..762be0b --- /dev/null +++ b/lib/libfat/src/source/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/lib/libfat/src/source/cache.c b/lib/libfat/src/source/cache.c new file mode 100644 index 0000000..07576b9 --- /dev/null +++ b/lib/libfat/src/source/cache.c @@ -0,0 +1,323 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "common.h" +#include "cache.h" +#include "disc.h" + +#include "mem_allocate.h" +#include "bit_ops.h" +#include "file_allocation_table.h" + +#define CACHE_FREE UINT_MAX + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector) { + CACHE* cache; + unsigned int i; + CACHE_ENTRY* cacheEntries; + + if (numberOfPages < 2) { + numberOfPages = 2; + } + + if (sectorsPerPage < 8) { + sectorsPerPage = 8; + } + + cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->bytesPerSector = bytesPerSector; + + + cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + _FAT_mem_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * bytesPerSector ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _FAT_cache_destructor (CACHE* cache) { + unsigned int i; + // Clear out cache before destroying it + _FAT_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + _FAT_mem_free (cache->cacheEntries[i].cache); + } + _FAT_mem_free (cache->cacheEntries); + _FAT_mem_free (cache); +} + + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + + +static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector) +{ + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + CACHE_ENTRY *entry; + uint8_t *dest = (uint8_t *)buffer; + + while(numSectors>0) { + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector)); + + dest += (secs_to_read*cache->bytesPerSector); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + offset),size); + + return true; +} + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _FAT_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector); + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); + + entry->dirty = true; + return true; +} + + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + CACHE_ENTRY* entry; + const uint8_t *src = (const uint8_t *)buffer; + + while(numSectors>0) + { + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector)); + + src += (secs_to_write*cache->bytesPerSector); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _FAT_cache_flush (CACHE* cache) { + unsigned int i; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _FAT_cache_invalidate (CACHE* cache) { + unsigned int i; + _FAT_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/lib/libfat/src/source/cache.h b/lib/libfat/src/source/cache.h new file mode 100644 index 0000000..07bb14c --- /dev/null +++ b/lib/libfat/src/source/cache.h @@ -0,0 +1,128 @@ +/* + cache.h + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include "common.h" +#include "disc.h" + +typedef struct { + sec_t sector; + unsigned int count; + unsigned int last_access; + bool dirty; + uint8_t* cache; +} CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + unsigned int bytesPerSector; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the cache +If the sector is not in the cache, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache, zeroing the sector first +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the cache +*/ +bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the cache +*/ +static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) { + return _FAT_cache_readPartialSector (cache, buffer, sector, 0, cache->bytesPerSector); +} + +/* +Write a full sector to the cache +*/ +static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) { + return _FAT_cache_writePartialSector (cache, buffer, sector, 0, cache->bytesPerSector); +} + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the cache +*/ +bool _FAT_cache_flush (CACHE* cache); + +/* +Clear out the contents of the cache without writing any dirty sectors first +*/ +void _FAT_cache_invalidate (CACHE* cache); + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector); + +void _FAT_cache_destructor (CACHE* cache); + +#endif // _CACHE_H + diff --git a/lib/libfat/src/source/common.h b/lib/libfat/src/source/common.h new file mode 100644 index 0000000..c5c5632 --- /dev/null +++ b/lib/libfat/src/source/common.h @@ -0,0 +1,78 @@ +/* + common.h + Common definitions and included files for the FATlib + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +// Platform specific includes +#if defined(__gamecube__) || defined (__wii__) + #include + #include + #include +#elif defined(NDS) + #include + #include + #include +#elif defined(GBA) + #include + #include +#endif + +// Platform specific options +#if defined (__wii__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (__gamecube__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (NDS) + #define DEFAULT_CACHE_PAGES 16 + #define DEFAULT_SECTORS_PAGE 8 + #define USE_RTC_TIME +#elif defined (GBA) + #define DEFAULT_CACHE_PAGES 2 + #define DEFAULT_SECTORS_PAGE 8 + #define LIMIT_SECTORS 128 +#endif + +#endif // _COMMON_H diff --git a/lib/libfat/src/source/directory.c b/lib/libfat/src/source/directory.c new file mode 100644 index 0000000..c09debe --- /dev/null +++ b/lib/libfat/src/source/directory.c @@ -0,0 +1,1126 @@ +/* + directory.c + Reading, writing and manipulation of the directory structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "directory.h" +#include "common.h" +#include "partition.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" + +// Directory entry codes +#define DIR_ENTRY_LAST 0x00 +#define DIR_ENTRY_FREE 0xE5 + +#define LAST_LFN_POS (19*13) +#define LAST_LFN_POS_CORRECTION (MAX_LFN_LENGTH-15) + +typedef unsigned short ucs2_t; + +// Long file name directory entry +enum LFN_offset { + LFN_offset_ordinal = 0x00, // Position within LFN + LFN_offset_char0 = 0x01, + LFN_offset_char1 = 0x03, + LFN_offset_char2 = 0x05, + LFN_offset_char3 = 0x07, + LFN_offset_char4 = 0x09, + LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN + LFN_offset_reserved1 = 0x0C, // Always 0x00 + LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias) + LFN_offset_char5 = 0x0E, + LFN_offset_char6 = 0x10, + LFN_offset_char7 = 0x12, + LFN_offset_char8 = 0x14, + LFN_offset_char9 = 0x16, + LFN_offset_char10 = 0x18, + LFN_offset_reserved2 = 0x1A, // Always 0x0000 + LFN_offset_char11 = 0x1C, + LFN_offset_char12 = 0x1E +}; +static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; + +#define LFN_END 0x40 +#define LFN_DEL 0x80 + +static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] "; +static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|"; + +/* +Returns number of UCS-2 characters needed to encode an LFN +Returns -1 if it is an invalid LFN +*/ +#define ABOVE_UCS_RANGE 0xF0 +static int _FAT_directory_lfnLength (const char* name) { + unsigned int i; + size_t nameLength; + int ucsLength; + const char* tempName = name; + + nameLength = strnlen(name, MAX_FILENAME_LENGTH); + // Make sure the name is short enough to be valid + if ( nameLength >= MAX_FILENAME_LENGTH) { + return -1; + } + // Make sure it doesn't contain any invalid characters + if (strpbrk (name, ILLEGAL_LFN_CHARACTERS) != NULL) { + return -1; + } + // Make sure the name doesn't contain any control codes or codes not representable in UCS-2 + for (i = 0; i < nameLength; i++) { + if (name[i] < 0x20 || name[i] >= ABOVE_UCS_RANGE) { + return -1; + } + } + // Convert to UCS-2 and get the resulting length + ucsLength = mbsrtowcs(NULL, &tempName, MAX_LFN_LENGTH, NULL); + if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) { + return -1; + } + + // Otherwise it is valid + return ucsLength; +} + +/* +Convert a multibyte encoded string into a NUL-terminated UCS-2 string, storing at most len characters +return number of characters stored +*/ +static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len) { + mbstate_t ps = {0}; + wchar_t tempChar; + int bytes; + size_t count = 0; + + while (count < len-1 && src != '\0') { + bytes = mbrtowc (&tempChar, src, MB_CUR_MAX, &ps); + if (bytes > 0) { + *dst = (ucs2_t)tempChar; + src += bytes; + dst++; + count++; + } else if (bytes == 0) { + break; + } else { + return -1; + } + } + *dst = '\0'; + + return count; +} + +/* +Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars +return number of chars stored, or (size_t)-1 on error +*/ +static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) { + mbstate_t ps = {0}; + size_t count = 0; + int bytes; + char buff[MB_CUR_MAX]; + int i; + + while (count < len - 1 && *src != '\0') { + bytes = wcrtomb (buff, *src, &ps); + if (bytes < 0) { + return -1; + } + if (count + bytes < len && bytes > 0) { + for (i = 0; i < bytes; i++) { + *dst++ = buff[i]; + } + src++; + count += bytes; + } else { + break; + } + } + *dst = L'\0'; + + return count; +} + +/* +Case-independent comparison of two multibyte encoded strings +*/ +static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t len1) { + wchar_t wc1, wc2; + mbstate_t ps1 = {0}; + mbstate_t ps2 = {0}; + size_t b1 = 0; + size_t b2 = 0; + + if (len1 == 0) { + return 0; + } + + do { + s1 += b1; + s2 += b2; + b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1); + b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2); + if ((int)b1 < 0 || (int)b2 < 0) { + break; + } + len1 -= b1; + } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0); + + return towlower(wc1) - towlower(wc2); +} + + +static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) { + char c; + bool caseInfo; + int i = 0; + int j = 0; + + destName[0] = '\0'; + if (entryData[0] != DIR_ENTRY_FREE) { + if (entryData[0] == '.') { + destName[0] = '.'; + if (entryData[1] == '.') { + destName[1] = '.'; + destName[2] = '\0'; + } else { + destName[1] = '\0'; + } + } else { + // Copy the filename from the dirEntry to the string + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_BASE; + for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) { + c = entryData[DIR_ENTRY_name + i]; + destName[i] = (caseInfo ? tolower((unsigned char)c) : c); + } + // Copy the extension from the dirEntry to the string + if (entryData[DIR_ENTRY_extension] != ' ') { + destName[i++] = '.'; + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_EXT; + for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) { + c = entryData[DIR_ENTRY_extension + j]; + destName[i++] = (caseInfo ? tolower((unsigned char)c) : c); + } + } + destName[i] = '\0'; + } + } + + return (destName[0] != '\0'); +} + +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) { + if (partition->filesysType == FS_FAT32) { + // Only use high 16 bits of start cluster when we are certain they are correctly defined + return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16); + } else { + return u8array_to_u16(entryData,DIR_ENTRY_cluster); + } +} + +static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) { + DIR_ENTRY_POSITION position = *entryPosition; + uint32_t tempCluster; + + // Increment offset, wrapping at the end of a sector + ++ position.offset; + if (position.offset == partition->bytesPerSector / DIR_ENTRY_DATA_SIZE) { + position.offset = 0; + // Increment sector when wrapping + ++ position.sector; + // But wrap at the end of a cluster + if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) { + position.sector = 0; + // Move onto the next cluster, making sure there is another cluster to go to + tempCluster = _FAT_fat_nextCluster(partition, position.cluster); + if (tempCluster == CLUSTER_EOF) { + if (extendDirectory) { + tempCluster = _FAT_fat_linkFreeClusterCleared (partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempCluster)) { + return false; // This will only happen if the disc is full + } + } else { + return false; // Got to the end of the directory, not extending it + } + } + position.cluster = tempCluster; + } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) { + return false; // Got to end of root directory, can't extend it + } + } + *entryPosition = position; + return true; +} + +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[0x20]; + ucs2_t lfn[MAX_LFN_LENGTH]; + bool notFound, found; + int lfnPos; + uint8_t lfnChkSum, chkSum; + bool lfnExists; + int i; + + lfnChkSum = 0; + + entryStart = entry->dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryStart.cluster = partition->rootDirCluster; + } + + entryEnd = entryStart; + + lfnExists = false; + + found = false; + notFound = false; + + while (!found && !notFound) { + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + notFound = true; + } + + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) { + // It's an LFN + if (entryData[LFN_offset_ordinal] & LFN_DEL) { + lfnExists = false; + } else if (entryData[LFN_offset_ordinal] & LFN_END) { + // Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight) + entryStart = entryEnd; // This is the start of a directory entry + lfnExists = true; + lfnPos = (entryData[LFN_offset_ordinal] & ~LFN_END) * 13; + if (lfnPos > MAX_LFN_LENGTH - 1) { + lfnPos = MAX_LFN_LENGTH - 1; + } + lfn[lfnPos] = '\0'; // Set end of lfn to null character + lfnChkSum = entryData[LFN_offset_checkSum]; + } + if (lfnChkSum != entryData[LFN_offset_checkSum]) { + lfnExists = false; + } + if (lfnExists) { + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + if (lfnPos > LAST_LFN_POS) { + // Force it within the buffer. Will corrupt the filename but prevent buffer overflows + lfnPos = LAST_LFN_POS; + } + for (i = 0; i < 13; i++) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) { + // This is a volume name, don't bother with it + } else if (entryData[0] == DIR_ENTRY_LAST) { + notFound = true; + } else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) { + if (lfnExists) { + // Calculate file checksum + chkSum = 0; + for (i=0; i < 11; i++) { + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; + } + if (chkSum != lfnChkSum) { + lfnExists = false; + entry->filename[0] = '\0'; + } + } + + if (lfnExists) { + if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) { + // Failed to convert the file name to UTF-8. Maybe the wrong locale is set? + return false; + } + } else { + entryStart = entryEnd; + _FAT_directory_entryGetAlias (entryData, entry->filename); + } + found = true; + } + } + + // If no file is found, return false + if (notFound) { + return false; + } else { + // Fill in the directory entry struct + entry->dataStart = entryStart; + entry->dataEnd = entryEnd; + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + return true; + } +} + +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + entry->dataStart.cluster = dirCluster; + entry->dataStart.sector = 0; + entry->dataStart.offset = -1; // Start before the beginning of the directory + + entry->dataEnd = entry->dataStart; + + return _FAT_directory_getNextEntry (partition, entry); +} + +bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { + entry->dataStart.cluster = 0; + entry->dataStart.sector = 0; + entry->dataStart.offset = 0; + + entry->dataEnd = entry->dataStart; + + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); + entry->filename[0] = '.'; + + memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE); + memset (entry->entryData, ' ', 11); + entry->entryData[0] = '.'; + + entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + + u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster); + u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16); + + return true; +} + +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { + DIR_ENTRY entry; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + int i; + bool end; + + _FAT_directory_getRootEntry(partition, &entry); + + entryEnd = entry.dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryEnd.cluster = partition->rootDirCluster; + } + + label[0]='\0'; + label[11]='\0'; + end = false; + //this entry should be among the first 3 entries in the root directory table, if not, then system can have trouble displaying the right volume label + while(!end) { + if(!_FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) + { //error reading + return false; + } + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { + for (i = 0; i < 11; i++) { + label[i] = entryData[DIR_ENTRY_name + i]; + } + return true; + } else if (entryData[0] == DIR_ENTRY_LAST) { + end = true; + } + + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + end = true; + } + } + return false; +} + +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + ucs2_t lfn[MAX_LFN_LENGTH]; + int i; + int lfnPos; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, + entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Copy the entry data and stop, since this is the last section of the directory entry + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + finished = true; + } else { + // Copy the long file name data + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + if (lfnPos > LAST_LFN_POS) { + lfnPos = LAST_LFN_POS_CORRECTION; + } + for (i = 0; i < 13; i++) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + + if (!entryStillValid) { + return false; + } + + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Since the entry doesn't have a long file name, extract the short filename + if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) { + return false; + } + } else { + // Encode the long file name into a multibyte string + if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) { + return false; + } + } + + return true; +} + + + +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) { + size_t dirnameLength; + const char* pathPosition; + const char* nextPathPosition; + uint32_t dirCluster; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + bool found, notFound; + + pathPosition = path; + + found = false; + notFound = false; + + if (pathEnd == NULL) { + // Set pathEnd to the end of the path string + pathEnd = strchr (path, '\0'); + } + + if (pathPosition[0] == DIR_SEPARATOR) { + // Start at root directory + dirCluster = partition->rootDirCluster; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // If the path is only specifying a directory in the form of "/" return it + if (pathPosition >= pathEnd) { + _FAT_directory_getRootEntry (partition, entry); + found = true; + } + } else { + // Start in current working directory + dirCluster = partition->cwdCluster; + } + + // If the path is only specifying a directory in the form "." + // and this is the root directory, return it + if ((dirCluster == partition->rootDirCluster) && (strcmp(".", pathPosition) == 0)) { + _FAT_directory_getRootEntry (partition, entry); + found = true; + } + + while (!found && !notFound) { + // Get the name of the next required subdirectory within the path + nextPathPosition = strchr (pathPosition, DIR_SEPARATOR); + if (nextPathPosition != NULL) { + dirnameLength = nextPathPosition - pathPosition; + } else { + dirnameLength = strlen(pathPosition); + } + + if (dirnameLength > MAX_FILENAME_LENGTH) { + // The path is too long to bother with + return false; + } + + // Look for the directory within the path + foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster); + + while (foundFile && !found && !notFound) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(entry->filename, MAX_FILENAME_LENGTH)) + && (_FAT_directory_mbsncasecmp(pathPosition, entry->filename, dirnameLength) == 0)) { + found = true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (entry->entryData, alias); + if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH)) + && (strncasecmp(pathPosition, alias, dirnameLength) == 0)) { + found = true; + } + + if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) { + // Make sure that we aren't trying to follow a file instead of a directory in the path + found = false; + } + + if (!found) { + foundFile = _FAT_directory_getNextEntry (partition, entry); + } + } + + if (!foundFile) { + // Check that the search didn't get to the end of the directory + notFound = true; + found = false; + } else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) { + // Check that we reached the end of the path + found = true; + } else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) { + dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData); + pathPosition = nextPathPosition; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // The requested directory was found + if (pathPosition >= pathEnd) { + found = true; + } else { + found = false; + } + } + } + + if (found && !notFound) { + if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && + _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) + { + // On FAT32 it should specify an actual cluster for the root entry, + // not cluster 0 as on FAT16 + _FAT_directory_getRootEntry (partition, entry); + } + return true; + } else { + return false; + } +} + +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + entryData[0] = DIR_ENTRY_FREE; + _FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { + finished = true; + } + } + + if (!entryStillValid) { + return false; + } + + return true; +} + +static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) { + DIR_ENTRY_POSITION gapStart; + DIR_ENTRY_POSITION gapEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + size_t dirEntryRemain; + bool endOfDirectory, entryStillValid; + + // Scan Dir for free entry + gapEnd.offset = 0; + gapEnd.sector = 0; + gapEnd.cluster = dirCluster; + + gapStart = gapEnd; + + entryStillValid = true; + dirEntryRemain = size; + endOfDirectory = false; + + while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if (entryData[0] == DIR_ENTRY_LAST) { + gapStart = gapEnd; + -- dirEntryRemain; + endOfDirectory = true; + } else if (entryData[0] == DIR_ENTRY_FREE) { + if (dirEntryRemain == size) { + gapStart = gapEnd; + } + -- dirEntryRemain; + } else { + dirEntryRemain = size; + } + + if (!endOfDirectory && (dirEntryRemain > 0)) { + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + } + } + + // Make sure the scanning didn't fail + if (!entryStillValid) { + return false; + } + + // Save the start entry, since we know it is valid + entry->dataStart = gapStart; + + if (endOfDirectory) { + memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE); + dirEntryRemain += 1; // Increase by one to take account of End Of Directory Marker + while ((dirEntryRemain > 0) && entryStillValid) { + // Get the gapEnd before incrementing it, so the second to last one is saved + entry->dataEnd = gapEnd; + // Increment gapEnd, moving onto the next entry + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + -- dirEntryRemain; + // Fill the entry with blanks + _FAT_cache_writePartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + if (!entryStillValid) { + return false; + } + } else { + entry->dataEnd = gapEnd; + } + + return true; +} + +static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) { + DIR_ENTRY tempEntry; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + size_t dirnameLength; + + dirnameLength = strnlen(name, MAX_FILENAME_LENGTH); + + if (dirnameLength >= MAX_FILENAME_LENGTH) { + return false; + } + + // Make sure the entry doesn't already exist + foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster); + + while (foundFile) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(tempEntry.filename, MAX_FILENAME_LENGTH)) + && (_FAT_directory_mbsncasecmp(name, tempEntry.filename, dirnameLength) == 0)) { + return true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (tempEntry.entryData, alias); + if ((strncasecmp(name, alias, MAX_ALIAS_LENGTH) == 0)) { + return true; + } + foundFile = _FAT_directory_getNextEntry (partition, &tempEntry); + } + return false; +} + +/* +Creates an alias for a long file name. If the alias is not an exact match for the +filename, it returns the number of characters in the alias. If the two names match, +it returns 0. If there was an error, it returns -1. +*/ +static int _FAT_directory_createAlias (char* alias, const char* lfn) { + bool lossyConversion = false; // Set when the alias had to be modified to be valid + int lfnPos = 0; + int aliasPos = 0; + wchar_t lfnChar; + int oemChar; + mbstate_t ps = {0}; + int bytesUsed = 0; + const char* lfnExt; + int aliasExtLen; + + // Strip leading periods + while (lfn[lfnPos] == '.') { + lfnPos ++; + lossyConversion = true; + } + + // Primary portion of alias + while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, MAX_FILENAME_LENGTH - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in filename + lossyConversion = true; + lfnPos += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnPos += bytesUsed; + } + + if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + // Name was more than 8 characters long + lossyConversion = true; + } + + // Alias extension + lfnExt = strrchr (lfn, '.'); + if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) { + // More than one period in name + lossyConversion = true; + } + if (lfnExt != NULL && lfnExt[1] != '\0') { + lfnExt++; + alias[aliasPos] = '.'; + aliasPos++; + memset (&ps, 0, sizeof(ps)); + for (aliasExtLen = 0; aliasExtLen < MAX_ALIAS_EXT_LENGTH && *lfnExt != '\0'; aliasExtLen++) { + bytesUsed = mbrtowc(&lfnChar, lfnExt, MAX_FILENAME_LENGTH - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in alias + lossyConversion = true; + lfnExt += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnExt += bytesUsed; + } + if (*lfnExt != '\0') { + // Extension was more than 3 characters long + lossyConversion = true; + } + } + + alias[aliasPos] = '\0'; + if (lossyConversion) { + return aliasPos; + } else { + return 0; + } +} + +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + size_t entrySize; + uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE]; + int i,j; // Must be signed for use when decrementing in for loop + char *tmpCharPtr; + DIR_ENTRY_POSITION curEntryPos; + bool entryStillValid; + uint8_t aliasCheckSum = 0; + char alias [MAX_ALIAS_LENGTH]; + int aliasLen; + int lfnLen; + + // Make sure the filename is not 0 length + if (strnlen (entry->filename, MAX_FILENAME_LENGTH) < 1) { + return false; + } + + // Make sure the filename is at least a valid LFN + lfnLen = _FAT_directory_lfnLength (entry->filename); + if (lfnLen < 0) { + return false; + } + + // Remove trailing spaces + for (i = strlen (entry->filename) - 1; (i > 0) && (entry->filename[i] == ' '); --i) { + entry->filename[i] = '\0'; + } + // Remove leading spaces + for (i = 0; (i < (int)strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ; + if (i > 0) { + memmove (entry->filename, entry->filename + i, strlen (entry->filename + i)); + } + + // Remove junk in filename + i = strlen (entry->filename); + memset (entry->filename + i, '\0', MAX_FILENAME_LENGTH - i); + + // Make sure the entry doesn't already exist + if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) { + return false; + } + + // Clear out alias, so we can generate a new one + memset (entry->entryData, ' ', 11); + + if ( strncmp(entry->filename, ".", MAX_FILENAME_LENGTH) == 0) { + // "." entry + entry->entryData[0] = '.'; + entrySize = 1; + } else if ( strncmp(entry->filename, "..", MAX_FILENAME_LENGTH) == 0) { + // ".." entry + entry->entryData[0] = '.'; + entry->entryData[1] = '.'; + entrySize = 1; + } else { + // Normal file name + aliasLen = _FAT_directory_createAlias (alias, entry->filename); + if (aliasLen < 0) { + return false; + } else if (aliasLen == 0) { + // It's a normal short filename + entrySize = 1; + } else { + // It's a long filename with an alias + entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1; + + // Generate full alias for all cases except when the alias is simply an upper case version of the LFN + // and there isn't already a file with that name + if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 || + _FAT_directory_entryExists (partition, alias, dirCluster)) + { + // expand primary part to 8 characters long by padding the end with underscores + i = MAX_ALIAS_PRI_LENGTH - 1; + // Move extension to last 3 characters + while (alias[i] != '.' && i > 0) i--; + if (i > 0) { + j = MAX_ALIAS_LENGTH - MAX_ALIAS_EXT_LENGTH - 2; // 1 char for '.', one for NUL, 3 for extension + memmove (alias + j, alias + i, strlen(alias) - i); + // Pad primary component + memset (alias + i, '_', j - i); + alias[MAX_ALIAS_LENGTH-1]=0; + } + + // Generate numeric tail + for (i = 1; i <= MAX_NUMERIC_TAIL; i++) { + j = i; + tmpCharPtr = alias + MAX_ALIAS_PRI_LENGTH - 1; + while (j > 0) { + *tmpCharPtr = '0' + (j % 10); // ASCII numeric value + tmpCharPtr--; + j /= 10; + } + *tmpCharPtr = '~'; + if (!_FAT_directory_entryExists (partition, alias, dirCluster)) { + break; + } + } + if (i > MAX_NUMERIC_TAIL) { + // Couldn't get a valid alias + return false; + } + } + } + + // Copy alias or short file name into directory entry data + for (i = 0, j = 0; (j < 8) && (alias[i] != '.') && (alias[i] != '\0'); i++, j++) { + entry->entryData[j] = alias[i]; + } + while (j < 8) { + entry->entryData[j] = ' '; + ++ j; + } + if (alias[i] == '.') { + // Copy extension + ++ i; + while ((alias[i] != '\0') && (j < 11)) { + entry->entryData[j] = alias[i]; + ++ i; + ++ j; + } + } + while (j < 11) { + entry->entryData[j] = ' '; + ++ j; + } + + // Generate alias checksum + for (i=0; i < ALIAS_ENTRY_LENGTH; i++) { + // NOTE: The operation is an unsigned char rotate right + aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i]; + } + } + + // Find or create space for the entry + if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) { + return false; + } + + // Write out directory entry + curEntryPos = entry->dataStart; + + { + // lfn is only pushed onto the stack here, reducing overall stack usage + ucs2_t lfn[MAX_LFN_LENGTH] = {0}; + _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH); + + for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i ) + { + if (i > 1) { + // Long filename entry + lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0); + for (j = 0; j < 13; j++) { + if (lfn [(i - 2) * 13 + j] == '\0') { + if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character + } + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], lfn [(i - 2) * 13 + j]); + } + } + + lfnEntry[LFN_offset_checkSum] = aliasCheckSum; + lfnEntry[LFN_offset_flag] = ATTRIB_LFN; + lfnEntry[LFN_offset_reserved1] = 0; + u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0); + _FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } else { + // Alias & file data + _FAT_cache_writePartialSector (partition->cache, entry->entryData, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + } + } + + return true; +} + +bool _FAT_directory_chdir (PARTITION* partition, const char* path) { + DIR_ENTRY entry; + + if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) { + return false; + } + + if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) { + return false; + } + + partition->cwdCluster = _FAT_directory_entryGetCluster (partition, entry.entryData); + + return true; +} + +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) { + // Fill in the stat struct + // Some of the values are faked for the sake of compatibility + st->st_dev = _FAT_disc_hostType(partition->disc); // The device is the 32bit ioType value + st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(partition, entry->entryData)); // The file serial number is the start cluster + st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) | + (S_IRUSR | S_IRGRP | S_IROTH) | + (_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte + st->st_nlink = 1; // Always one hard link on a FAT file + st->st_uid = 1; // Faked for FAT + st->st_gid = 2; // Faked for FAT + st->st_rdev = st->st_dev; + st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size + st->st_atime = _FAT_filetime_to_time_t ( + 0, + u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate) + ); + st->st_spare1 = 0; + st->st_mtime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate) + ); + st->st_spare2 = 0; + st->st_ctime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate) + ); + st->st_spare3 = 0; + st->st_blksize = partition->bytesPerSector; // Prefered file I/O block size + st->st_blocks = (st->st_size + partition->bytesPerSector - 1) / partition->bytesPerSector; // File size in blocks + st->st_spare4[0] = 0; + st->st_spare4[1] = 0; +} diff --git a/lib/libfat/src/source/directory.h b/lib/libfat/src/source/directory.h new file mode 100644 index 0000000..ac66c78 --- /dev/null +++ b/lib/libfat/src/source/directory.h @@ -0,0 +1,181 @@ +/* + directory.h + Reading, writing and manipulation of the directory structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _DIRECTORY_H +#define _DIRECTORY_H + +#include + +#include "common.h" +#include "partition.h" + +#define DIR_ENTRY_DATA_SIZE 0x20 +#define MAX_LFN_LENGTH 256 +#define MAX_FILENAME_LENGTH 768 // 256 UCS-2 characters encoded into UTF-8 can use up to 768 UTF-8 chars +#define MAX_ALIAS_LENGTH 13 +#define LFN_ENTRY_LENGTH 13 +#define ALIAS_ENTRY_LENGTH 11 +#define MAX_ALIAS_EXT_LENGTH 3 +#define MAX_ALIAS_PRI_LENGTH 8 +#define MAX_NUMERIC_TAIL 999999 +#define FAT16_ROOT_DIR_CLUSTER 0 + +#define DIR_SEPARATOR '/' + +// File attributes +#define ATTRIB_ARCH 0x20 // Archive +#define ATTRIB_DIR 0x10 // Directory +#define ATTRIB_LFN 0x0F // Long file name +#define ATTRIB_VOL 0x08 // Volume +#define ATTRIB_SYS 0x04 // System +#define ATTRIB_HID 0x02 // Hidden +#define ATTRIB_RO 0x01 // Read only + +#define CASE_LOWER_EXT 0x10 // WinNT lowercase extension +#define CASE_LOWER_BASE 0x08 // WinNT lowercase basename + +typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE; + +typedef struct { + uint32_t cluster; + sec_t sector; + int32_t offset; +} DIR_ENTRY_POSITION; + +typedef struct { + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry + char filename[MAX_FILENAME_LENGTH]; +} DIR_ENTRY; + +// Directory entry offsets +enum DIR_ENTRY_offset { + DIR_ENTRY_name = 0x00, + DIR_ENTRY_extension = 0x08, + DIR_ENTRY_attributes = 0x0B, + DIR_ENTRY_caseInfo = 0x0C, + DIR_ENTRY_cTime_ms = 0x0D, + DIR_ENTRY_cTime = 0x0E, + DIR_ENTRY_cDate = 0x10, + DIR_ENTRY_aDate = 0x12, + DIR_ENTRY_clusterHigh = 0x14, + DIR_ENTRY_mTime = 0x16, + DIR_ENTRY_mDate = 0x18, + DIR_ENTRY_cluster = 0x1A, + DIR_ENTRY_fileSize = 0x1C +}; + +/* +Returns true if the file specified by entry is a directory +*/ +static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0); +} + +static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0); +} + +static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) { + return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') || + ((entry->filename[1] == '.') && entry->filename[2] == '\0'))); +} + +/* +Reads the first directory entry from the directory starting at dirCluster +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Reads the next directory entry after the one already pointed to by entry +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Gets the directory entry corrsponding to the supplied path +entry will be destroyed even if no directory entry is found +pathEnd specifies the end of the path string, for cutting strings short if needed + specify NULL to use the full length of path + pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR + after pathEND. +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); + +/* +Changes the current directory to the one specified by path +Returns true on success, false on failure +*/ +bool _FAT_directory_chdir (PARTITION* partition, const char* path); + +/* +Removes the directory entry specified by entry +Assumes that entry is valid +Returns true on success, false on failure +*/ +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Add a directory entry to the directory specified by dirCluster +The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are +updated with the new directory entry position and alias. +Returns true on success, false on failure +*/ +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Get the start cluster of a file from it's entry data +*/ +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); + +/* +Fill in the file name and entry data of DIR_ENTRY* entry. +Assumes that the entry's dataStart and dataEnd are correct +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry); + +/* +Fill in a stat struct based on a file entry +*/ +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); + +/* +Get volume label +*/ +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); + +#endif // _DIRECTORY_H diff --git a/lib/libfat/src/source/disc.c b/lib/libfat/src/source/disc.c new file mode 100644 index 0000000..5f626b6 --- /dev/null +++ b/lib/libfat/src/source/disc.c @@ -0,0 +1,111 @@ +/* + disc.c + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2008 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "disc.h" + +/* +The list of interfaces consists of a series of name/interface pairs. +The interface is returned via a simple function. This allows for +platforms where the interface has to be "assembled" before it can +be used, like DLDI on the NDS. For cases where a simple struct +is available, wrapper functions are used. +The list is terminated by a NULL/NULL entry. +*/ + +/* ====================== Wii ====================== */ +#if defined (__wii__) +#include +#include +#include + +static const DISC_INTERFACE* get_io_wiisd (void) { + return &__io_wiisd; +} +static const DISC_INTERFACE* get_io_usbstorage (void) { + return &__io_usbstorage; +} + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} + +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_wiisd}, + {"usb", get_io_usbstorage}, + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ==================== Gamecube ==================== */ +#elif defined (__gamecube__) +#include + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ====================== NDS ====================== */ +#elif defined (NDS) +#include + +static const DISC_INTERFACE* get_io_dsisd (void) { + return &__io_dsisd; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_dsisd}, + {"fat", dldiGetInternal}, + {NULL, NULL} +}; + +/* ====================== GBA ====================== */ +#elif defined (GBA) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", discGetInterface}, + {NULL, NULL} +}; + +#endif + diff --git a/lib/libfat/src/source/disc.h b/lib/libfat/src/source/disc.h new file mode 100644 index 0000000..5c955f9 --- /dev/null +++ b/lib/libfat/src/source/disc.h @@ -0,0 +1,110 @@ +/* + disc.h + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ +#ifndef _DISC_H +#define _DISC_H + +#include "common.h" + +/* +A list of all default devices to try at startup, +terminated by a {NULL,NULL} entry. +*/ +typedef struct { + const char* name; + const DISC_INTERFACE* (*getInterface)(void); +} INTERFACE_ID; +extern const INTERFACE_ID _FAT_disc_interfaces[]; + +/* +Check if a disc is inserted +Return true if a disc is inserted and ready, false otherwise +*/ +static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) { + return disc->isInserted(); +} + +/* +Read numSectors sectors from a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to fill +*/ +static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) { + return disc->readSectors (sector, numSectors, buffer); +} + +/* +Write numSectors sectors to a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to read from +*/ +static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) { + return disc->writeSectors (sector, numSectors, buffer); +} + +/* +Reset the card back to a ready state +*/ +static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) { + return disc->clearStatus(); +} + +/* +Initialise the disc to a state ready for data reading or writing +*/ +static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) { + return disc->startup(); +} + +/* +Put the disc in a state ready for power down. +Complete any pending writes and disable the disc if necessary +*/ +static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) { + return disc->shutdown(); +} + +/* +Return a 32 bit value unique to each type of interface +*/ +static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) { + return disc->ioType; +} + +/* +Return a 32 bit value that specifies the capabilities of the disc +*/ +static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) { + return disc->features; +} + +#endif // _DISC_H diff --git a/lib/libfat/src/source/fat_frag.c b/lib/libfat/src/source/fat_frag.c new file mode 100644 index 0000000..fcd7cbe --- /dev/null +++ b/lib/libfat/src/source/fat_frag.c @@ -0,0 +1,96 @@ +/* + fat_frag.c + + get list of sector fragments used by a file + + Copyright (c) 2008-2011 oggzee + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#include "fatfile.h" + +#include +#include +#include +#include +#include + +#include "cache.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + +typedef int (*_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); + +int _FAT_get_fragments (const char *path, _frag_append_t append_fragment, void *callback_data) +{ + struct _reent r; + FILE_STRUCT file; + PARTITION* partition; + u32 cluster; + u32 sector; + u32 offset; // in sectors + u32 size; // in sectors + int ret = -1; + int fd; + + fd = _FAT_open_r (&r, &file, path, O_RDONLY, 0); + if (fd == -1) return -1; + if (fd != (int)&file) return -1; + + partition = file.partition; + _FAT_lock(&partition->lock); + + size = file.filesize / partition->bytesPerSector; + cluster = file.startCluster; + offset = 0; + + do { + if (!_FAT_fat_isValidCluster(partition, cluster)) { + // invalid cluster + goto out; + } + // add cluster to fileinfo + sector = _FAT_fat_clusterToSector(partition, cluster); + if (append_fragment(callback_data, offset, sector, partition->sectorsPerCluster)) { + // too many fragments + goto out; + } + offset += partition->sectorsPerCluster; + cluster = _FAT_fat_nextCluster (partition, cluster); + } while (offset < size); + + // set size + append_fragment(callback_data, size, 0, 0); + // success + ret = 0; + + out: + _FAT_unlock(&partition->lock); + _FAT_close_r(&r, fd); + return ret; +} + diff --git a/lib/libfat/src/source/fatdir.c b/lib/libfat/src/source/fatdir.c new file mode 100644 index 0000000..fe0e781 --- /dev/null +++ b/lib/libfat/src/source/fatdir.c @@ -0,0 +1,619 @@ +/* + fatdir.c + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "fatdir.h" + +#include "cache.h" +#include "file_allocation_table.h" +#include "partition.h" +#include "directory.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + + +int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) { + PARTITION* partition = NULL; + DIR_ENTRY dirEntry; + + // Get the partition this file is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + // Fill in the stat struct + _FAT_directory_entryStat (partition, &dirEntry, st); + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) { + r->_errno = ENOTSUP; + return -1; +} + +int _FAT_unlink_r (struct _reent *r, const char *path) { + PARTITION* partition = NULL; + DIR_ENTRY dirEntry; + DIR_ENTRY dirContents; + uint32_t cluster; + bool nextEntry; + bool errorOccured = false; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (partition->readOnly) { + r->_errno = EROFS; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + + + // If this is a directory, make sure it is empty + if (_FAT_directory_isDirectory (&dirEntry)) { + nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster); + + while (nextEntry) { + if (!_FAT_directory_isDot (&dirContents)) { + // The directory had something in it that isn't a reference to itself or it's parent + _FAT_unlock(&partition->lock); + r->_errno = EPERM; + return -1; + } + nextEntry = _FAT_directory_getNextEntry (partition, &dirContents); + } + } + + if (_FAT_fat_isValidCluster(partition, cluster)) { + // Remove the cluster chain for this file + if (!_FAT_fat_clearLinks (partition, cluster)) { + r->_errno = EIO; + errorOccured = true; + } + } + + // Remove the directory entry for this file + if (!_FAT_directory_removeEntry (partition, &dirEntry)) { + r->_errno = EIO; + errorOccured = true; + } + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(partition->cache)) { + r->_errno = EIO; + errorOccured = true; + } + + _FAT_unlock(&partition->lock); + if (errorOccured) { + return -1; + } else { + return 0; + } +} + +int _FAT_chdir_r (struct _reent *r, const char *path) { + PARTITION* partition = NULL; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Try changing directory + if (_FAT_directory_chdir (partition, path)) { + // Successful + _FAT_unlock(&partition->lock); + return 0; + } else { + // Failed + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } +} + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) { + PARTITION* partition = NULL; + DIR_ENTRY oldDirEntry; + DIR_ENTRY newDirEntry; + const char *pathEnd; + uint32_t dirCluster; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (oldName); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + _FAT_lock(&partition->lock); + + // Make sure the same partition is used for the old and new names + if (partition != _FAT_partition_getPartitionFromPath (newName)) { + _FAT_unlock(&partition->lock); + r->_errno = EXDEV; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (partition->readOnly) { + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (oldName, ':') != NULL) { + oldName = strchr (oldName, ':') + 1; + } + if (strchr (oldName, ':') != NULL) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + if (strchr (newName, ':') != NULL) { + newName = strchr (newName, ':') + 1; + } + if (strchr (newName, ':') != NULL) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + // Make sure there is no existing file / directory with the new name + if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + // Create the new file entry + // Get the directory it has to go in + pathEnd = strrchr (newName, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + dirCluster = partition->cwdCluster; + pathEnd = newName; + } else { + // Path was specified -- get the right dirCluster + // Recycling newDirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) || + !_FAT_directory_isDirectory(&newDirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + + // Copy the entry data + memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY)); + + // Set the new name + strncpy (newDirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1); + + // Write the new entry + if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // Remove the old entry + if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush (partition->cache)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) { + PARTITION* partition = NULL; + bool fileExists; + DIR_ENTRY dirEntry; + const char* pathEnd; + uint32_t parentCluster, dirCluster; + uint8_t newEntryData[DIR_ENTRY_DATA_SIZE]; + + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file/directory on the disc + fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); + + // Make sure it doesn't exist + if (fileExists) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + if (partition->readOnly) { + // We can't write to a read-only partition + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Get the directory it has to go in + pathEnd = strrchr (path, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + parentCluster = partition->cwdCluster; + pathEnd = path; + } else { + // Path was specified -- get the right parentCluster + // Recycling dirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) || + !_FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + // Create the entry data + strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1); + memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE); + + // Set the creation time and date + dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0; + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); + + // Set the directory attribute + dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + + // Get a cluster for the new directory + dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, dirCluster)) { + // No space left on disc for the cluster + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); + + // Write the new directory's entry to it's parent + if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // Create the dot entry within the directory + memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE); + memset (newEntryData, ' ', 11); + newEntryData[DIR_ENTRY_name] = '.'; + newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster); + u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); + + // Write it to the directory, erasing that sector in the process + _FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData, + _FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE); + + + // Create the double dot entry within the directory + + // if ParentDir == Rootdir then ".."" always link to Cluster 0 + if(parentCluster == partition->rootDirCluster) + parentCluster = FAT16_ROOT_DIR_CLUSTER; + + newEntryData[DIR_ENTRY_name + 1] = '.'; + u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster); + u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16); + + // Write it to the directory + _FAT_cache_writePartialSector ( partition->cache, newEntryData, + _FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(partition->cache)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + PARTITION* partition = NULL; + unsigned int freeClusterCount; + + // Get the partition of the requested path + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + _FAT_lock(&partition->lock); + + if(memcmp(&buf->f_flag, "SCAN", 4) == 0) + { + //Special command was given to sync the numberFreeCluster + _FAT_partition_createFSinfo(partition); + } + + if(partition->filesysType == FS_FAT32) + freeClusterCount = partition->fat.numberFreeCluster; + else + freeClusterCount = _FAT_fat_freeClusterCount (partition); + + // FAT clusters = POSIX blocks + buf->f_bsize = partition->bytesPerCluster; // File system block size. + buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size. + + buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize. + buf->f_bfree = freeClusterCount; // Total number of free blocks. + buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process. + + // Treat requests for info on inodes as clusters + buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers. + buf->f_ffree = freeClusterCount; // Total number of free file serial numbers. + buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process. + + // File system ID. 32bit ioType value + buf->f_fsid = _FAT_disc_hostType(partition->disc); + + // Bit mask of f_flag values. + buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */ + | (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ; + // Maximum filename length. + buf->f_namemax = MAX_FILENAME_LENGTH; + + _FAT_unlock(&partition->lock); + return 0; +} + +DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) { + DIR_ENTRY dirEntry; + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + bool fileExists; + + state->partition = _FAT_partition_getPartitionFromPath (path); + if (state->partition == NULL) { + r->_errno = ENODEV; + return NULL; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return NULL; + } + + _FAT_lock(&state->partition->lock); + + // Get the start cluster of the directory + fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL); + + if (!fileExists) { + _FAT_unlock(&state->partition->lock); + r->_errno = ENOENT; + return NULL; + } + + // Make sure it is a directory + if (! _FAT_directory_isDirectory (&dirEntry)) { + _FAT_unlock(&state->partition->lock); + r->_errno = ENOTDIR; + return NULL; + } + + // Save the start cluster for use when resetting the directory data + state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData); + + // Get the first entry for use with a call to dirnext + state->validEntry = + _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); + + // We are now using this entry + state->inUse = true; + _FAT_unlock(&state->partition->lock); + return (DIR_ITER*) state; +} + +int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + _FAT_lock(&state->partition->lock); + + // Make sure we are still using this entry + if (!state->inUse) { + _FAT_unlock(&state->partition->lock); + r->_errno = EBADF; + return -1; + } + + // Get the first entry for use with a call to dirnext + state->validEntry = + _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); + + _FAT_unlock(&state->partition->lock); + return 0; +} + +int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + _FAT_lock(&state->partition->lock); + + // Make sure we are still using this entry + if (!state->inUse) { + _FAT_unlock(&state->partition->lock); + r->_errno = EBADF; + return -1; + } + + // Make sure there is another file to report on + if (! state->validEntry) { + _FAT_unlock(&state->partition->lock); + r->_errno = ENOENT; + return -1; + } + + // Get the filename + strncpy (filename, state->currentEntry.filename, MAX_FILENAME_LENGTH); + // Get the stats, if requested + if (filestat != NULL) { + _FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat); + } + + // Look for the next entry for use next time + state->validEntry = + _FAT_directory_getNextEntry (state->partition, &(state->currentEntry)); + + _FAT_unlock(&state->partition->lock); + return 0; +} + +int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + // We are no longer using this entry + _FAT_lock(&state->partition->lock); + state->inUse = false; + _FAT_unlock(&state->partition->lock); + + return 0; +} diff --git a/lib/libfat/src/source/fatdir.h b/lib/libfat/src/source/fatdir.h new file mode 100644 index 0000000..426dd30 --- /dev/null +++ b/lib/libfat/src/source/fatdir.h @@ -0,0 +1,73 @@ +/* + fatdir.h + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _FATDIR_H +#define _FATDIR_H + +#include +#include +#include +#include +#include "common.h" +#include "directory.h" + +typedef struct { + PARTITION* partition; + DIR_ENTRY currentEntry; + uint32_t startCluster; + bool inUse; + bool validEntry; +} DIR_STATE_STRUCT; + +extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); + +extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); + +extern int _FAT_unlink_r (struct _reent *r, const char *name); + +extern int _FAT_chdir_r (struct _reent *r, const char *name); + +extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); + +extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode); + +extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* +Directory iterator functions +*/ +extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path); +extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState); + + +#endif // _FATDIR_H diff --git a/lib/libfat/src/source/fatfile.c b/lib/libfat/src/source/fatfile.c new file mode 100644 index 0000000..e2a99f6 --- /dev/null +++ b/lib/libfat/src/source/fatfile.c @@ -0,0 +1,1133 @@ +/* + fatfile.c + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. + + 2009-10-23 oggzee: fixes for cluster aligned file size (write, truncate, seek) +*/ + + +#include "fatfile.h" + +#include +#include +#include +#include +#include + +#include "cache.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + PARTITION* partition = NULL; + bool fileExists; + DIR_ENTRY dirEntry; + const char* pathEnd; + uint32_t dirCluster; + FILE_STRUCT* file = (FILE_STRUCT*) fileStruct; + partition = _FAT_partition_getPartitionFromPath (path); + + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + // Determine which mode the file is openned for + if ((flags & 0x03) == O_RDONLY) { + // Open the file for read-only access + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + // Open file for write only access + file->read = false; + file->write = true; + file->append = false; + } else if ((flags & 0x03) == O_RDWR) { + // Open file for read/write access + file->read = true; + file->write = true; + file->append = false; + } else { + r->_errno = EACCES; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (file->write && partition->readOnly) { + r->_errno = EROFS; + return -1; + } + + // Search for the file on the disc + _FAT_lock(&partition->lock); + fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); + + // The file shouldn't exist if we are trying to create it + if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + // It should not be a directory if we're openning a file, + if (fileExists && _FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EISDIR; + return -1; + } + + // We haven't modified the file yet + file->modified = false; + + // If the file doesn't exist, create it if we're allowed to + if (!fileExists) { + if (flags & O_CREAT) { + if (partition->readOnly) { + // We can't write to a read-only partition + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + // Create the file + // Get the directory it has to go in + pathEnd = strrchr (path, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + dirCluster = partition->cwdCluster; + pathEnd = path; + } else { + // Path was specified -- get the right dirCluster + // Recycling dirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) || + !_FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + dirCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + // Create the entry data + strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1); + memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE); + + // Set the creation time and date + dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0; + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC()); + + if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // File entry is modified + file->modified = true; + } else { + // file doesn't exist, and we aren't creating it + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + } + + file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize); + + /* Allow LARGEFILEs with undefined results + // Make sure that the file size can fit in the available space + if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) { + r->_errno = EFBIG; + return -1; + } + */ + + // Make sure we aren't trying to write to a read-only file + if (file->write && !_FAT_directory_isWritable(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Associate this file with a particular partition + file->partition = partition; + + file->startCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) { + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + file->filesize = 0; + // File is modified since we just cut it all off + file->modified = true; + } + + // Remember the position of this file's directory entry + file->dirEntryStart = dirEntry.dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + file->dirEntryEnd = dirEntry.dataEnd; + + // Reset read/write pointer + file->currentPosition = 0; + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + + if (flags & O_APPEND) { + file->append = true; + + // Set append pointer to the end of the file + file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + file->appendPosition.byte = file->filesize % partition->bytesPerSector; + + // Check if the end of the file is on the end of a cluster + if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){ + // Set flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + file->appendPosition.byte = 0; + } + } else { + file->append = false; + // Use something sane for the append pointer, so the whole file struct contains known values + file->appendPosition = file->rwPosition; + } + + file->inUse = true; + + // Insert this file into the double-linked list of open files + partition->openFileCount += 1; + if (partition->firstOpenFile) { + file->nextOpenFile = partition->firstOpenFile; + partition->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + partition->firstOpenFile = file; + + _FAT_unlock(&partition->lock); + + return (int) file; +} + +/* +Synchronizes the file data to disc. +Does no locking of its own -- lock the partition before calling. +Returns 0 on success, an error code on failure. +*/ +int _FAT_syncToDisc (FILE_STRUCT* file) { + uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE]; + + if (!file || !file->inUse) { + return EBADF; + } + + if (file->write && file->modified) { + // Load the old entry + _FAT_cache_readPartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Write new data to the directory entry + // File size + u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize); + + // Start cluster + u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster); + u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16); + + // Modification time and date + u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); + + // Access date + u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); + + // Set archive attribute + dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH; + + // Write the new entry + _FAT_cache_writePartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(file->partition->cache)) { + return EIO; + } + } + + file->modified = false; + + return 0; +} + + +int _FAT_close_r (struct _reent *r, int fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + if (file->write) { + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + } + + file->inUse = false; + + // Remove this file from the double-linked list of open files + file->partition->openFileCount -= 1; + if (file->nextOpenFile) { + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + } + if (file->prevOpenFile) { + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + } else { + file->partition->firstOpenFile = file->nextOpenFile; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} + +ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + return 0; + } + + // Make sure we can actually read from the file + if ((file == NULL) || !file->inUse || !file->read) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Don't try to read if the read pointer is past the end of file + if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) { + r->_errno = EOVERFLOW; + _FAT_unlock(&partition->lock); + return 0; + } + + // Don't read past end of file + if (len + file->currentPosition > file->filesize) { + r->_errno = EOVERFLOW; + len = file->filesize - file->currentPosition; + } + + remain = len; + position = file->rwPosition; + cache = file->partition->cache; + + // Align to sector + tempVar = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && flagNoError) + { + _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + + position.byte += tempVar; + if (position.byte >= partition->bytesPerSector) { + position.byte = 0; + position.sector++; + } + } + + // align to cluster + // tempVar is number of sectors to read + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + if ((tempVar > 0) && flagNoError) { + if (! _FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Move onto next cluster + // It should get to here without reading anything if a cluster is due to be allocated + if ((position.sector >= partition->sectorsPerCluster) && flagNoError) { + tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + } else if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = tempNextCluster; + } + } + + // Read in whole clusters, contiguous blocks at a time + while ((remain >= partition->bytesPerCluster) && flagNoError) { + uint32_t chunkEnd; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = 0; + + do { + chunkEnd = nextChunkStart; + nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd); + chunkSize += partition->bytesPerCluster; + } while ((nextChunkStart == chunkEnd + 1) && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && +#endif + (chunkSize + partition->bytesPerCluster <= remain)); + + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + chunkSize / partition->bytesPerSector, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + // Advance to next cluster + if ((remain == 0) && (nextChunkStart == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + position.cluster = chunkEnd; + } else if (!_FAT_fat_isValidCluster(partition, nextChunkStart)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = nextChunkStart; + } + } + + // Read remaining sectors + tempVar = remain / partition->bytesPerSector; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Last remaining sector + // Check if anything is left + if ((remain > 0) && flagNoError) { + _FAT_cache_readPartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte += remain; + remain = 0; + } + + // Length read is the wanted length minus the stuff not read + len = len - remain; + + // Update file information + file->rwPosition = position; + file->currentPosition += len; + + _FAT_unlock(&partition->lock); + return len; +} + +// if current position is on the cluster border and more data has to be written +// then get next cluster or allocate next cluster +// this solves the over-allocation problems when file size is aligned to cluster size +// return true on succes, false on error +static bool _FAT_check_position_for_next_cluster(struct _reent *r, + FILE_POSITION *position, PARTITION* partition, size_t remain, bool *flagNoError) +{ + uint32_t tempNextCluster; + // do nothing if no more data to write + if (remain == 0) return true; + if (flagNoError && *flagNoError == false) return false; + if ((remain < 0) || (position->sector > partition->sectorsPerCluster)) { + // invalid arguments - internal error + r->_errno = EINVAL; + goto err; + } + if (position->sector == partition->sectorsPerCluster) { + // need to advance to next cluster + tempNextCluster = _FAT_fat_nextCluster(partition, position->cluster); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) { + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position->cluster); + } + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + goto err; + } + position->sector = 0; + position->cluster = tempNextCluster; + } + return true; +err: + if (flagNoError) *flagNoError = false; + return false; +} + +/* +Extend a file so that the size is the same as the rwPosition +*/ +static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { + PARTITION* partition = file->partition; + CACHE* cache = file->partition->cache; + FILE_POSITION position; + uint8_t zeroBuffer [partition->bytesPerSector]; + memset(zeroBuffer, 0, partition->bytesPerSector); + uint32_t remain; + uint32_t tempNextCluster; + unsigned int sector; + + position.byte = file->filesize % partition->bytesPerSector; + position.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + // It is assumed that there is always a startCluster + // This will be true when _FAT_file_extend_r is called from _FAT_write_r + position.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + + remain = file->currentPosition - file->filesize; + + if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) { + // Get a new cluster on the edge of a cluster boundary + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + position.sector = 0; + } + + if (remain + position.byte < partition->bytesPerSector) { + // Only need to clear to the end of the sector + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain); + position.byte += remain; + } else { + if (position.byte > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, + partition->bytesPerSector - position.byte); + remain -= (partition->bytesPerSector - position.byte); + position.byte = 0; + position.sector ++; + } + + while (remain >= partition->bytesPerSector) { + if (position.sector >= partition->sectorsPerCluster) { + position.sector = 0; + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + } + + sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector; + _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer); + + remain -= partition->bytesPerSector; + position.sector ++; + } + + if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL)) { + // error already marked + return false; + } + + if (remain > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte = remain; + } + } + + file->rwPosition = position; + file->filesize = file->currentPosition; + return true; +} + +ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + bool flagAppending = false; + + // Make sure we can actually write to the file + if ((file == NULL) || !file->inUse || !file->write) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + cache = file->partition->cache; + _FAT_lock(&partition->lock); + + // Only write up to the maximum file size, taking into account wrap-around of ints + if (len + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) { + len = FILE_MAX_SIZE - file->filesize; + } + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + _FAT_unlock(&partition->lock); + return 0; + } + + remain = len; + + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + // Appending starts at the begining for a 0 byte file + file->appendPosition.cluster = file->startCluster; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + + if (file->append) { + position = file->appendPosition; + flagAppending = true; + } else { + // If the write pointer is past the end of the file, extend the file to that size + if (file->currentPosition > file->filesize) { + if (!_FAT_file_extend_r (r, file)) { + _FAT_unlock(&partition->lock); + return -1; + } + } + + // Write at current read pointer + position = file->rwPosition; + + // If it is writing past the current end of file, set appending flag + if (len + file->currentPosition > file->filesize) { + flagAppending = true; + } + } + + // Move onto next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Align to sector + tempVar = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && flagNoError) { + // Write partial sector to disk + _FAT_cache_writePartialSector (cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + position.byte += tempVar; + + + // Move onto next sector + if (position.byte >= partition->bytesPerSector) { + position.byte = 0; + position.sector ++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Write whole clusters + while ((remain >= partition->bytesPerCluster) && flagNoError) { + // allocate next cluster + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + if (!flagNoError) break; + // set indexes to the current position + uint32_t chunkEnd = position.cluster; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = partition->bytesPerCluster; + FILE_POSITION next_position = position; + + // group consecutive clusters + while (flagNoError && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && +#endif + (chunkSize + partition->bytesPerCluster < remain)) + { + // pretend to use up all sectors in next_position + next_position.sector = partition->sectorsPerCluster; + // get or allocate next cluster + _FAT_check_position_for_next_cluster(r, &next_position, partition, + remain - chunkSize, &flagNoError); + if (!flagNoError) break; // exit loop on error + nextChunkStart = next_position.cluster; + if (nextChunkStart != chunkEnd + 1) break; // exit loop if not consecutive + chunkEnd = nextChunkStart; + chunkSize += partition->bytesPerCluster; + } + + if ( !_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / partition->bytesPerSector, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + if ((chunkEnd != nextChunkStart) && _FAT_fat_isValidCluster(partition, nextChunkStart)) { + // new cluster is already allocated (because it was not consecutive) + position.cluster = nextChunkStart; + position.sector = 0; + } else { + // Allocate a new cluster when next writing the file + position.cluster = chunkEnd; + position.sector = partition->sectorsPerCluster; + } + } + + // allocate next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Write remaining sectors + tempVar = remain / partition->bytesPerSector; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Last remaining sector + if ((remain > 0) && flagNoError) { + if (flagAppending) { + _FAT_cache_eraseWritePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } else { + _FAT_cache_writePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } + position.byte += remain; + remain = 0; + } + + + // Amount written is the originally requested amount minus stuff remaining + len = len - remain; + + // Update file information + file->modified = true; + if (file->append) { + // Appending doesn't affect the read pointer + file->appendPosition = position; + file->filesize += len; + } else { + // Writing also shifts the read pointer + file->rwPosition = position; + file->currentPosition += len; + if (file->filesize < file->currentPosition) { + file->filesize = file->currentPosition; + } + } + _FAT_unlock(&partition->lock); + + return len; +} + + +off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + uint32_t cluster, nextCluster; + int clusCount; + off_t newPosition; + uint32_t position; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + switch (dir) { + case SEEK_SET: + newPosition = pos; + break; + case SEEK_CUR: + newPosition = (off_t)file->currentPosition + pos; + break; + case SEEK_END: + newPosition = (off_t)file->filesize + pos; + break; + default: + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + if ((pos > 0) && (newPosition < 0)) { + _FAT_unlock(&partition->lock); + r->_errno = EOVERFLOW; + return -1; + } + + // newPosition can only be larger than the FILE_MAX_SIZE on platforms where + // off_t is larger than 32 bits. + if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + position = (uint32_t)newPosition; + + // Only change the read/write position if it is within the bounds of the current filesize, + // or at the very edge of the file + if (position <= file->filesize && file->startCluster != CLUSTER_FREE) { + // Calculate where the correct cluster is + // how many clusters from start of file + clusCount = position / partition->bytesPerCluster; + cluster = file->startCluster; + if (position >= file->currentPosition) { + // start from current cluster + int currentCount = file->currentPosition / partition->bytesPerCluster; + if (file->rwPosition.sector == partition->sectorsPerCluster) { + currentCount--; + } + clusCount -= currentCount; + cluster = file->rwPosition.cluster; + } + // Calculate the sector and byte of the current position, + // and store them + file->rwPosition.sector = (position % partition->bytesPerCluster) / partition->bytesPerSector; + file->rwPosition.byte = position % partition->bytesPerSector; + + nextCluster = _FAT_fat_nextCluster (partition, cluster); + while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { + clusCount--; + cluster = nextCluster; + nextCluster = _FAT_fat_nextCluster (partition, cluster); + } + + // Check if ran out of clusters and it needs to allocate a new one + if (clusCount > 0) { + if ((clusCount == 1) && (file->filesize == position) && (file->rwPosition.sector == 0)) { + // Set flag to allocate a new cluster + file->rwPosition.sector = partition->sectorsPerCluster; + file->rwPosition.byte = 0; + } else { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + } + + file->rwPosition.cluster = cluster; + } + + // Save position + file->currentPosition = position; + + _FAT_unlock(&partition->lock); + return position; +} + + + +int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + DIR_ENTRY fileEntry; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Get the file's entry data + fileEntry.dataStart = file->dirEntryStart; + fileEntry.dataEnd = file->dirEntryEnd; + + if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + // Fill in the stat struct + _FAT_directory_entryStat (partition, &fileEntry, st); + + // Fix stats that have changed since the file was openned + st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster + st->st_size = file->filesize; // File size + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + int ret=0; + uint32_t newSize = (uint32_t)len; + + if (len < 0) { + // Trying to truncate to a negative size + r->_errno = EINVAL; + return -1; + } + + if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) { + // Trying to extend the file beyond what FAT supports + r->_errno = EFBIG; + return -1; + } + + if (!file || !file->inUse) { + // invalid file + r->_errno = EBADF; + return -1; + } + + if (!file->write) { + // Read-only file + r->_errno = EINVAL; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + if (newSize > file->filesize) { + // Expanding the file + FILE_POSITION savedPosition; + uint32_t savedOffset; + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + // Save the read/write pointer + savedPosition = file->rwPosition; + savedOffset = file->currentPosition; + // Set the position to the new size + file->currentPosition = newSize; + // Extend the file to the new position + if (!_FAT_file_extend_r (r, file)) { + ret = -1; + } + // Set the append position to the new rwPointer + if (file->append) { + file->appendPosition = file->rwPosition; + } + // Restore the old rwPointer; + file->rwPosition = savedPosition; + file->currentPosition = savedOffset; + } else if (newSize < file->filesize){ + // Shrinking the file + if (len == 0) { + // Cutting the file down to nothing, clear all clusters used + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + + file->appendPosition.cluster = CLUSTER_FREE; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + } else { + // Trimming the file down to the required size + unsigned int chainLength; + uint32_t lastCluster; + + // Drop the unneeded end of the cluster chain. + // If the end falls on a cluster boundary, drop that cluster too, + // then set a flag to allocate a cluster as needed + chainLength = ((newSize-1) / partition->bytesPerCluster) + 1; + lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength); + + if (file->append) { + file->appendPosition.byte = newSize % partition->bytesPerSector; + // Does the end of the file fall on the edge of a cluster? + if (newSize % partition->bytesPerCluster == 0) { + // Set a flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + } else { + file->appendPosition.sector = (newSize % partition->bytesPerCluster) / partition->bytesPerSector; + } + file->appendPosition.cluster = lastCluster; + } + } + } else { + // Truncating to same length, so don't do anything + } + + file->filesize = newSize; + file->modified = true; + + _FAT_unlock(&partition->lock); + return ret; +} + +int _FAT_fsync_r (struct _reent *r, int fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} diff --git a/lib/libfat/src/source/fatfile.h b/lib/libfat/src/source/fatfile.h new file mode 100644 index 0000000..5e4648d --- /dev/null +++ b/lib/libfat/src/source/fatfile.h @@ -0,0 +1,105 @@ +/* + fatfile.h + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + + +#ifndef _FATFILE_H +#define _FATFILE_H + +#include +#include + +#include "common.h" +#include "partition.h" +#include "directory.h" + +#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B + +typedef struct { + u32 cluster; + sec_t sector; + s32 byte; +} FILE_POSITION; + +struct _FILE_STRUCT; + +struct _FILE_STRUCT { + uint32_t filesize; + uint32_t startCluster; + uint32_t currentPosition; + FILE_POSITION rwPosition; + FILE_POSITION appendPosition; + DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry + PARTITION* partition; + struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files + struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files + bool read; + bool write; + bool append; + bool inUse; + bool modified; +}; + +typedef struct _FILE_STRUCT FILE_STRUCT; + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); + +int _FAT_close_r (struct _reent *r, int fd); + +ssize_t _FAT_write_r (struct _reent *r,int fd, const char *ptr, size_t len); + +ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len); + +off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir); + +int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st); + +int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); + +int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); + +int _FAT_unlink_r (struct _reent *r, const char *name); + +int _FAT_chdir_r (struct _reent *r, const char *name); + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); + +int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len); + +int _FAT_fsync_r (struct _reent *r, int fd); + +/* +Synchronizes the file data to disc. +Does no locking of its own -- lock the partition before calling. +Returns 0 on success, an error code on failure. +*/ +extern int _FAT_syncToDisc (FILE_STRUCT* file); + +#endif // _FATFILE_H diff --git a/lib/libfat/src/source/file_allocation_table.c b/lib/libfat/src/source/file_allocation_table.c new file mode 100644 index 0000000..72f8aa7 --- /dev/null +++ b/lib/libfat/src/source/file_allocation_table.c @@ -0,0 +1,393 @@ +/* + file_allocation_table.c + Reading, writing and manipulation of the FAT structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "file_allocation_table.h" +#include "partition.h" +#include "mem_allocate.h" +#include + +/* +Gets the cluster linked from input cluster +*/ +uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) +{ + uint32_t nextCluster = CLUSTER_FREE; + sec_t sector; + int offset; + + if (cluster == CLUSTER_FREE) { + return CLUSTER_FREE; + } + + switch (partition->filesysType) + { + case FS_UNKNOWN: + return CLUSTER_ERROR; + break; + + case FS_FAT12: + { + u32 nextCluster_h; + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8)); + + offset++; + + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + nextCluster_h = 0; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8)); + nextCluster |= (nextCluster_h << 8); + + if (cluster & 0x01) { + nextCluster = nextCluster >> 4; + } else { + nextCluster &= 0x0FFF; + } + + if (nextCluster >= 0x0FF7) + { + nextCluster = CLUSTER_EOF; + } + + break; + } + case FS_FAT16: + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16)); + + if (nextCluster >= 0xFFF7) { + nextCluster = CLUSTER_EOF; + } + break; + + case FS_FAT32: + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32)); + + if (nextCluster >= 0x0FFFFFF7) { + nextCluster = CLUSTER_EOF; + } + break; + + default: + return CLUSTER_ERROR; + break; + } + + return nextCluster; +} + +/* +writes value into the correct offset within a partition's FAT, based +on the cluster number. +*/ +static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) { + sec_t sector; + int offset; + uint32_t oldValue; + + if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) + { + return false; + } + + switch (partition->filesysType) + { + case FS_UNKNOWN: + return false; + break; + + case FS_FAT12: + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + if (cluster & 0x01) { + + _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); + + value = (value << 4) | (oldValue & 0x0F); + + _FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8)); + + offset++; + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + + _FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8)); + + } else { + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); + + offset++; + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + + _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); + + value = ((value >> 8) & 0x0F) | (oldValue & 0xF0); + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); + } + + break; + + case FS_FAT16: + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16)); + + break; + + case FS_FAT32: + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32)); + + break; + + default: + return false; + break; + } + + return true; +} + +/*----------------------------------------------------------------- +gets the first available free cluster, sets it +to end of file, links the input cluster to it then returns the +cluster number +If an error occurs, return CLUSTER_ERROR +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) { + uint32_t firstFree; + uint32_t curLink; + uint32_t lastCluster; + bool loopedAroundFAT = false; + + lastCluster = partition->fat.lastCluster; + + if (cluster > lastCluster) { + return CLUSTER_ERROR; + } + + // Check if the cluster already has a link, and return it if so + curLink = _FAT_fat_nextCluster(partition, cluster); + if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) { + return curLink; // Return the current link - don't allocate a new one + } + + // Get a free cluster + firstFree = partition->fat.firstFree; + // Start at first valid cluster + if (firstFree < CLUSTER_FIRST) { + firstFree = CLUSTER_FIRST; + } + + // Search until a free cluster is found + while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) { + firstFree++; + if (firstFree > lastCluster) { + if (loopedAroundFAT) { + // If couldn't get a free cluster then return an error + partition->fat.firstFree = firstFree; + return CLUSTER_ERROR; + } else { + // Try looping back to the beginning of the FAT + // This was suggested by loopy + firstFree = CLUSTER_FIRST; + loopedAroundFAT = true; + } + } + } + partition->fat.firstFree = firstFree; + if(partition->fat.numberFreeCluster) + partition->fat.numberFreeCluster--; + partition->fat.numberLastAllocCluster = firstFree; + + if ((cluster >= CLUSTER_FIRST) && (cluster <= lastCluster)) + { + // Update the linked from FAT entry + _FAT_fat_writeFatEntry (partition, cluster, firstFree); + } + // Create the linked to FAT entry + _FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF); + + return firstFree; +} + +/*----------------------------------------------------------------- +gets the first available free cluster, sets it +to end of file, links the input cluster to it, clears the new +cluster to 0 valued bytes, then returns the cluster number +If an error occurs, return CLUSTER_ERROR +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) { + uint32_t newCluster; + uint32_t i; + uint8_t *emptySector; + + // Link the cluster + newCluster = _FAT_fat_linkFreeCluster(partition, cluster); + + if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) { + return CLUSTER_ERROR; + } + + emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + + // Clear all the sectors within the cluster + memset (emptySector, 0, partition->bytesPerSector); + for (i = 0; i < partition->sectorsPerCluster; i++) { + _FAT_cache_writeSectors (partition->cache, + _FAT_fat_clusterToSector (partition, newCluster) + i, + 1, emptySector); + } + + _FAT_mem_free(emptySector); + + return newCluster; +} + + +/*----------------------------------------------------------------- +_FAT_fat_clearLinks +frees any cluster used by a file +-----------------------------------------------------------------*/ +bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) { + uint32_t nextCluster; + + if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) + return false; + + // If this clears up more space in the FAT before the current free pointer, move it backwards + if (cluster < partition->fat.firstFree) { + partition->fat.firstFree = cluster; + } + + while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR)) { + // Store next cluster before erasing the link + nextCluster = _FAT_fat_nextCluster (partition, cluster); + + // Erase the link + _FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE); + + if(partition->fat.numberFreeCluster < (partition->numberOfSectors/partition->sectorsPerCluster)) + partition->fat.numberFreeCluster++; + // Move onto next cluster + cluster = nextCluster; + } + + return true; +} + +/*----------------------------------------------------------------- +_FAT_fat_trimChain +Drop all clusters past the chainLength. +If chainLength is 0, all clusters are dropped. +If chainLength is 1, the first cluster is kept and the rest are +dropped, and so on. +Return the last cluster left in the chain. +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) { + uint32_t nextCluster; + + if (chainLength == 0) { + // Drop the entire chain + _FAT_fat_clearLinks (partition, startCluster); + return CLUSTER_FREE; + } else { + // Find the last cluster in the chain, and the one after it + chainLength--; + nextCluster = _FAT_fat_nextCluster (partition, startCluster); + while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { + chainLength--; + startCluster = nextCluster; + nextCluster = _FAT_fat_nextCluster (partition, startCluster); + } + + // Drop all clusters after the last in the chain + if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) { + _FAT_fat_clearLinks (partition, nextCluster); + } + + // Mark the last cluster in the chain as the end of the file + _FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF); + + return startCluster; + } +} + +/*----------------------------------------------------------------- +_FAT_fat_lastCluster +Trace the cluster links until the last one is found +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) { + while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) { + cluster = _FAT_fat_nextCluster(partition, cluster); + } + return cluster; +} + +/*----------------------------------------------------------------- +_FAT_fat_freeClusterCount +Return the number of free clusters available +-----------------------------------------------------------------*/ +unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) { + unsigned int count = 0; + uint32_t curCluster; + + for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) { + if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) { + count++; + } + } + + return count; +} + diff --git a/lib/libfat/src/source/file_allocation_table.h b/lib/libfat/src/source/file_allocation_table.h new file mode 100644 index 0000000..d9c4361 --- /dev/null +++ b/lib/libfat/src/source/file_allocation_table.h @@ -0,0 +1,70 @@ +/* + file_allocation_table.h + Reading, writing and manipulation of the FAT structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _FAT_H +#define _FAT_H + +#include "common.h" +#include "partition.h" + +#define CLUSTER_EOF_16 0xFFFF +#define CLUSTER_EOF 0x0FFFFFFF +#define CLUSTER_FREE 0x00000000 +#define CLUSTER_ROOT 0x00000000 +#define CLUSTER_FIRST 0x00000002 +#define CLUSTER_ERROR 0xFFFFFFFF + +#define CLUSTERS_PER_FAT12 4085 +#define CLUSTERS_PER_FAT16 65525 + + +uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster); + +uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster); +uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster); + +bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster); + +uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength); + +uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster); + +unsigned int _FAT_fat_freeClusterCount (PARTITION* partition); + +static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) { + return (cluster >= CLUSTER_FIRST) ? + ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : + partition->rootDirStart; +} + +static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) { + return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */); +} + +#endif // _FAT_H diff --git a/lib/libfat/src/source/filetime.c b/lib/libfat/src/source/filetime.c new file mode 100644 index 0000000..d297bf6 --- /dev/null +++ b/lib/libfat/src/source/filetime.c @@ -0,0 +1,107 @@ +/* + filetime.c + Conversion of file time and date values to various other types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include "filetime.h" +#include "common.h" + +#define MAX_HOUR 23 +#define MAX_MINUTE 59 +#define MAX_SECOND 59 + +#define MAX_MONTH 11 +#define MIN_MONTH 0 +#define MAX_DAY 31 +#define MIN_DAY 1 + +uint16_t _FAT_filetime_getTimeFromRTC (void) { +#ifdef USE_RTC_TIME + struct tm timeParts; + time_t epochTime; + + if (time(&epochTime) == (time_t)-1) { + return 0; + } + localtime_r(&epochTime, &timeParts); + + // Check that the values are all in range. + // If they are not, return 0 (no timestamp) + if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0; + if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0; + if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0; + + return ( + ((timeParts.tm_hour & 0x1F) << 11) | + ((timeParts.tm_min & 0x3F) << 5) | + ((timeParts.tm_sec >> 1) & 0x1F) + ); +#else + return 0; +#endif +} + + +uint16_t _FAT_filetime_getDateFromRTC (void) { +#ifdef USE_RTC_TIME + struct tm timeParts; + time_t epochTime; + + if (time(&epochTime) == (time_t)-1) { + return 0; + } + localtime_r(&epochTime, &timeParts); + + if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0; + if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0; + + return ( + (((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year) + (((timeParts.tm_mon + 1) & 0xF) << 5) | + (timeParts.tm_mday & 0x1F) + ); +#else + return 0; +#endif +} + +time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) { + struct tm timeParts; + + timeParts.tm_hour = t >> 11; + timeParts.tm_min = (t >> 5) & 0x3F; + timeParts.tm_sec = (t & 0x1F) << 1; + + timeParts.tm_mday = d & 0x1F; + timeParts.tm_mon = ((d >> 5) & 0x0F) - 1; + timeParts.tm_year = (d >> 9) + 80; + + timeParts.tm_isdst = 0; + + return mktime(&timeParts); +} diff --git a/lib/libfat/src/source/filetime.h b/lib/libfat/src/source/filetime.h new file mode 100644 index 0000000..3bfd8ed --- /dev/null +++ b/lib/libfat/src/source/filetime.h @@ -0,0 +1,41 @@ +/* + filetime.h + Conversion of file time and date values to various other types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _FILETIME_H +#define _FILETIME_H + +#include "common.h" +#include + +uint16_t _FAT_filetime_getTimeFromRTC (void); +uint16_t _FAT_filetime_getDateFromRTC (void); + +time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d); + + +#endif // _FILETIME_H diff --git a/lib/libfat/src/source/libfat.c b/lib/libfat/src/source/libfat.c new file mode 100644 index 0000000..9f8dd01 --- /dev/null +++ b/lib/libfat/src/source/libfat.c @@ -0,0 +1,249 @@ +/* + libfat.c + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "common.h" +#include "partition.h" +#include "fatfile.h" +#include "fatdir.h" +#include "lock.h" +#include "mem_allocate.h" +#include "disc.h" + +static const devoptab_t dotab_fat = { + "fat", + sizeof (FILE_STRUCT), + _FAT_open_r, + _FAT_close_r, + _FAT_write_r, + _FAT_read_r, + _FAT_seek_r, + _FAT_fstat_r, + _FAT_stat_r, + _FAT_link_r, + _FAT_unlink_r, + _FAT_chdir_r, + _FAT_rename_r, + _FAT_mkdir_r, + sizeof (DIR_STATE_STRUCT), + _FAT_diropen_r, + _FAT_dirreset_r, + _FAT_dirnext_r, + _FAT_dirclose_r, + _FAT_statvfs_r, + _FAT_ftruncate_r, + _FAT_fsync_r, + NULL, /* Device data */ + NULL, + NULL +}; + +bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) { + PARTITION* partition; + devoptab_t* devops; + char* nameCopy; + + if(!name || strlen(name) > 8 || !interface) + return false; + + if(!interface->startup()) + return false; + + if(!interface->isInserted()) + return false; + + char devname[10]; + sprintf(devname, "%s:", name); + if(FindDevice(devname) >= 0) + return true; + + devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); + if (!devops) { + return false; + } + // Use the space allocated at the end of the devoptab struct for storing the name + nameCopy = (char*)(devops+1); + + // Initialize the file system + partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); + if (!partition) { + _FAT_mem_free (devops); + return false; + } + + // Add an entry for this device to the devoptab table + memcpy (devops, &dotab_fat, sizeof(dotab_fat)); + strcpy (nameCopy, name); + devops->name = nameCopy; + devops->deviceData = partition; + + AddDevice (devops); + + return true; +} + +bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) { + return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE); +} + +void fatUnmount (const char* name) { + devoptab_t *devops; + PARTITION* partition; + + if(!name) + return; + + devops = (devoptab_t*)GetDeviceOpTab (name); + if (!devops) { + return; + } + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + if (RemoveDevice (name) == -1) { + return; + } + + partition = (PARTITION*)devops->deviceData; + _FAT_partition_destructor (partition); + _FAT_mem_free (devops); +} + +bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) { + int i; + int defaultDevice = -1; + const DISC_INTERFACE *disc; + + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + disc = _FAT_disc_interfaces[i].getInterface(); + if (fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE)) { + // The first device to successfully mount is set as the default + if (defaultDevice < 0) { + defaultDevice = i; + } + } + } + + if (defaultDevice < 0) { + // None of our devices mounted + return false; + } + + if (setAsDefaultDevice) { + char filePath[MAXPATHLEN * 2]; + strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name); + strcat (filePath, ":/"); +#ifdef ARGV_MAGIC + if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) { + // Check the app's path against each of our mounted devices, to see + // if we can support it. If so, change to that path. + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name, + strlen(_FAT_disc_interfaces[i].name))) + { + char *lastSlash; + strcpy(filePath, __system_argv->argv[0]); + lastSlash = strrchr( filePath, '/' ); + + if ( NULL != lastSlash) { + if ( *(lastSlash - 1) == ':') lastSlash++; + *lastSlash = 0; + } + } + } + } +#endif + chdir (filePath); + } + + return true; +} + +bool fatInitDefault (void) { + return fatInit (DEFAULT_CACHE_PAGES, true); +} + +void fatGetVolumeLabel (const char* name, char *label) { + devoptab_t *devops; + PARTITION* partition; + char *buf; + int namelen,i; + + if(!name || !label) + return; + + namelen = strlen(name); + buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); + strcpy(buf,name); + + if (name[namelen-1] == '/') { + buf[namelen-1]='\0'; + namelen--; + } + + if (name[namelen-1] != ':') { + buf[namelen]=':'; + buf[namelen+1]='\0'; + } + + devops = (devoptab_t*)GetDeviceOpTab(buf); + + for(i=0;buf[i]!='\0' && buf[i]!=':';i++); + if (!devops || strncasecmp(buf,devops->name,i)) { + _FAT_mem_free(buf); + return; + } + + _FAT_mem_free(buf); + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + partition = (PARTITION*)devops->deviceData; + + if(!_FAT_directory_getVolumeLabel(partition, label)) { + strncpy(label,partition->label,11); + label[11]='\0'; + } + if(!strncmp(label, "NO NAME", 7)) label[0]='\0'; +} diff --git a/lib/libfat/src/source/lock.c b/lib/libfat/src/source/lock.c new file mode 100644 index 0000000..59c3444 --- /dev/null +++ b/lib/libfat/src/source/lock.c @@ -0,0 +1,29 @@ +#include "common.h" + +#ifndef USE_LWP_LOCK + +#ifndef mutex_t +typedef int mutex_t; +#endif + +void __attribute__ ((weak)) _FAT_lock_init(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock_deinit(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_unlock(mutex_t *mutex) +{ + return; +} + +#endif // USE_LWP_LOCK diff --git a/lib/libfat/src/source/lock.h b/lib/libfat/src/source/lock.h new file mode 100644 index 0000000..de5723a --- /dev/null +++ b/lib/libfat/src/source/lock.h @@ -0,0 +1,72 @@ +/* + lock.h + + Copyright (c) 2008 Sven Peter + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. + +*/ + +#ifndef _LOCK_H +#define _LOCK_H + +#include "common.h" + +#ifdef USE_LWP_LOCK + +static inline void _FAT_lock_init(mutex_t *mutex) +{ + LWP_MutexInit(mutex, false); +} + +static inline void _FAT_lock_deinit(mutex_t *mutex) +{ + LWP_MutexDestroy(*mutex); +} + +static inline void _FAT_lock(mutex_t *mutex) +{ + LWP_MutexLock(*mutex); +} + +static inline void _FAT_unlock(mutex_t *mutex) +{ + LWP_MutexUnlock(*mutex); +} + +#else + +// We still need a blank lock type +#ifndef mutex_t +typedef int mutex_t; +#endif + +void _FAT_lock_init(mutex_t *mutex); +void _FAT_lock_deinit(mutex_t *mutex); +void _FAT_lock(mutex_t *mutex); +void _FAT_unlock(mutex_t *mutex); + +#endif // USE_LWP_LOCK + + +#endif // _LOCK_H + diff --git a/lib/libfat/src/source/mem_allocate.h b/lib/libfat/src/source/mem_allocate.h new file mode 100644 index 0000000..74d4745 --- /dev/null +++ b/lib/libfat/src/source/mem_allocate.h @@ -0,0 +1,62 @@ +/* + mem_allocate.h + Memory allocation and destruction calls + Replace these calls with custom allocators if + malloc is unavailable + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +#ifdef _FAT_SYS_MEM_ALLOC + +static inline void* _FAT_mem_allocate (size_t size) { + return malloc (size); +} + +static inline void* _FAT_mem_align (size_t size) { +#ifdef __wii__ + return memalign (32, size); +#else + return malloc (size); +#endif +} + +static inline void _FAT_mem_free (void* mem) { + free (mem); +} + +#else + +void* _FAT_mem_allocate (size_t size); +void* _FAT_mem_align (size_t size); +void _FAT_mem_free (void* mem); + +#endif + +#endif // _MEM_ALLOCATE_H diff --git a/lib/libfat/src/source/partition.c b/lib/libfat/src/source/partition.c new file mode 100644 index 0000000..938646e --- /dev/null +++ b/lib/libfat/src/source/partition.c @@ -0,0 +1,435 @@ +/* + partition.c + Functions for mounting and dismounting partitions + on various block devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "partition.h" +#include "bit_ops.h" +#include "file_allocation_table.h" +#include "directory.h" +#include "mem_allocate.h" +#include "fatfile.h" + +#include +#include +#include + +sec_t _FAT_startSector; + +/* +Data offsets +*/ + +// BIOS Parameter Block offsets +enum BPB { + BPB_jmpBoot = 0x00, + BPB_OEMName = 0x03, + // BIOS Parameter Block + BPB_bytesPerSector = 0x0B, + BPB_sectorsPerCluster = 0x0D, + BPB_reservedSectors = 0x0E, + BPB_numFATs = 0x10, + BPB_rootEntries = 0x11, + BPB_numSectorsSmall = 0x13, + BPB_mediaDesc = 0x15, + BPB_sectorsPerFAT = 0x16, + BPB_sectorsPerTrk = 0x18, + BPB_numHeads = 0x1A, + BPB_numHiddenSectors = 0x1C, + BPB_numSectors = 0x20, + // Ext BIOS Parameter Block for FAT16 + BPB_FAT16_driveNumber = 0x24, + BPB_FAT16_reserved1 = 0x25, + BPB_FAT16_extBootSig = 0x26, + BPB_FAT16_volumeID = 0x27, + BPB_FAT16_volumeLabel = 0x2B, + BPB_FAT16_fileSysType = 0x36, + // Bootcode + BPB_FAT16_bootCode = 0x3E, + // FAT32 extended block + BPB_FAT32_sectorsPerFAT32 = 0x24, + BPB_FAT32_extFlags = 0x28, + BPB_FAT32_fsVer = 0x2A, + BPB_FAT32_rootClus = 0x2C, + BPB_FAT32_fsInfo = 0x30, + BPB_FAT32_bkBootSec = 0x32, + // Ext BIOS Parameter Block for FAT32 + BPB_FAT32_driveNumber = 0x40, + BPB_FAT32_reserved1 = 0x41, + BPB_FAT32_extBootSig = 0x42, + BPB_FAT32_volumeID = 0x43, + BPB_FAT32_volumeLabel = 0x47, + BPB_FAT32_fileSysType = 0x52, + // Bootcode + BPB_FAT32_bootCode = 0x5A, + BPB_bootSig_55 = 0x1FE, + BPB_bootSig_AA = 0x1FF +}; + +// File system information block offsets +enum FSIB +{ + FSIB_SIG1 = 0x00, + FSIB_SIG2 = 0x1e4, + FSIB_numberOfFreeCluster = 0x1e8, + FSIB_numberLastAllocCluster = 0x1ec, + FSIB_bootSig_55 = 0x1FE, + FSIB_bootSig_AA = 0x1FF +}; + +static const char FAT_SIG[3] = {'F', 'A', 'T'}; +static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'}; +static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'}; + +sec_t FindFirstValidPartition_buf(const DISC_INTERFACE* disc, uint8_t *sectorBuffer) +{ + uint8_t part_table[16*4]; + uint8_t *ptr; + int i; + + // Read first sector of disc + if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) { + return 0; + } + + memcpy(part_table,sectorBuffer+0x1BE,16*4); + ptr = part_table; + + for(i=0;i<4;i++,ptr+=16) { + sec_t part_lba = u8array_to_u32(ptr, 0x8); + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + return part_lba; + } + + if(ptr[4]==0) continue; + + if(ptr[4]==0x0F) { + sec_t part_lba2=part_lba; + sec_t next_lba2=0; + int n; + + for(n=0;n<8;n++) // max 8 logic partitions + { + if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0; + + part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ; + next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6); + + if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0; + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + { + return part_lba2; + } + + if(next_lba2==0) break; + } + } else { + if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0; + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + return part_lba; + } + } + } + return 0; +} + +sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) +{ + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) return 0; + sec_t ret = FindFirstValidPartition_buf(disc, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +PARTITION* _FAT_partition_constructor_buf (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector, uint8_t *sectorBuffer) +{ + PARTITION* partition; + + // Read first sector of disc + if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + return NULL; + } + + // Make sure it is a valid MBR or boot sector + if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) { + return NULL; + } + + if (startSector != 0) { + // We're told where to start the partition, so just accept it + } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + // Check if there is a FAT string, which indicates this is a boot sector + startSector = 0; + } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + // Check for FAT32 + startSector = 0; + } else { + startSector = FindFirstValidPartition_buf(disc, sectorBuffer); + if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + return NULL; + } + } + + // Now verify that this is indeed a FAT partition + if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) && + memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + { + return NULL; + } + + partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION)); + if (partition == NULL) { + return NULL; + } + + _FAT_startSector = startSector; + + // Init the partition lock + _FAT_lock_init(&partition->lock); + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); + else + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); + partition->label[11] = '\0'; + + // Set partition's disc interface + partition->disc = disc; + + // Store required information about the file system + partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT); + if (partition->fat.sectorsPerFat == 0) { + partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32); + } + + partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall); + if (partition->numberOfSectors == 0) { + partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors); + } + + partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector); + if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) { + // Unsupported sector size + _FAT_mem_free(partition); + return NULL; + } + + partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster]; + partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster; + partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors); + + partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat); + partition->dataStart = partition->rootDirStart + + (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector); + + partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector; + + //FS info sector + partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1); + + // Store info about FAT + uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster; + partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1; + partition->fat.firstFree = CLUSTER_FIRST; + partition->fat.numberFreeCluster = 0; + partition->fat.numberLastAllocCluster = 0; + + if (clusterCount < CLUSTERS_PER_FAT12) { + partition->filesysType = FS_FAT12; // FAT12 volume + } else if (clusterCount < CLUSTERS_PER_FAT16) { + partition->filesysType = FS_FAT16; // FAT16 volume + } else { + partition->filesysType = FS_FAT32; // FAT32 volume + } + + if (partition->filesysType != FS_FAT32) { + partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER; + } else { + // Set up for the FAT32 way + partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus); + // Check if FAT mirroring is enabled + if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) { + // Use the active FAT + partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F)); + } + } + + // Create a cache to use + partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors, partition->bytesPerSector); + + // Set current directory to the root + partition->cwdCluster = partition->rootDirCluster; + + // Check if this disc is writable, and set the readOnly property appropriately + partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE); + + // There are currently no open files on this partition + partition->openFileCount = 0; + partition->firstOpenFile = NULL; + + _FAT_partition_readFSinfo(partition); + + return partition; +} + +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) +{ + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) return NULL; + PARTITION *ret = _FAT_partition_constructor_buf(disc, cacheSize, + sectorsPerPage, startSector, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +void _FAT_partition_destructor (PARTITION* partition) { + FILE_STRUCT* nextFile; + + _FAT_lock(&partition->lock); + + // Synchronize open files + nextFile = partition->firstOpenFile; + while (nextFile) { + _FAT_syncToDisc (nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Write out the fs info sector + _FAT_partition_writeFSinfo(partition); + + // Free memory used by the cache, writing it to disc at the same time + _FAT_cache_destructor (partition->cache); + + // Unlock the partition and destroy the lock + _FAT_unlock(&partition->lock); + _FAT_lock_deinit(&partition->lock); + + // Free memory used by the partition + _FAT_mem_free (partition); +} + +PARTITION* _FAT_partition_getPartitionFromPath (const char* path) { + const devoptab_t *devops; + + devops = GetDeviceOpTab (path); + + if (!devops) { + return NULL; + } + + return (PARTITION*)devops->deviceData; +} + +void _FAT_partition_createFSinfo(PARTITION * partition) +{ + if(partition->readOnly || partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + + int i; + for(i = 0; i < 4; ++i) + { + sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i]; + sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i]; + } + + partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition); + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + + sectorBuffer[FSIB_bootSig_55] = 0x55; + sectorBuffer[FSIB_bootSig_AA] = 0xAA; + + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_readFSinfo(PARTITION * partition) +{ + if(partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + // Read first sector of disc + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return; + } + + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 || + memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 || + u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) + { + //sector does not yet exist, create one! + _FAT_partition_createFSinfo(partition); + } else { + partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); + partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster); + } + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_writeFSinfo(PARTITION * partition) +{ + if(partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + // Read first sector of disc + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return; + } + + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) { + _FAT_mem_free(sectorBuffer); + return; + } + + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + + // Write first sector of disc + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + _FAT_mem_free(sectorBuffer); +} diff --git a/lib/libfat/src/source/partition.h b/lib/libfat/src/source/partition.h new file mode 100644 index 0000000..ec27a0e --- /dev/null +++ b/lib/libfat/src/source/partition.h @@ -0,0 +1,107 @@ +/* + partition.h + Functions for mounting and dismounting partitions + on various block devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _PARTITION_H +#define _PARTITION_H + +#include "common.h" +#include "cache.h" +#include "lock.h" + +#define MIN_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 + +// Filesystem type +typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; + +typedef struct { + sec_t fatStart; + uint32_t sectorsPerFat; + uint32_t lastCluster; + uint32_t firstFree; + uint32_t numberFreeCluster; + uint32_t numberLastAllocCluster; +} FAT; + +typedef struct { + const DISC_INTERFACE* disc; + CACHE* cache; + // Info about the partition + FS_TYPE filesysType; + uint64_t totalSize; + sec_t rootDirStart; + uint32_t rootDirCluster; + uint32_t numberOfSectors; + sec_t dataStart; + uint32_t bytesPerSector; + uint32_t sectorsPerCluster; + uint32_t bytesPerCluster; + uint32_t fsInfoSector; + FAT fat; + // Values that may change after construction + uint32_t cwdCluster; // Current working directory cluster + int openFileCount; + struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files + mutex_t lock; // A lock for partition operations + bool readOnly; // If this is set, then do not try writing to the disc + char label[12]; // Volume label +} PARTITION; + +/* +Mount the supplied device and return a pointer to the struct necessary to use it +*/ +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); + +/* +Dismount the device and free all structures used. +Will also attempt to synchronise all open files to disc. +*/ +void _FAT_partition_destructor (PARTITION* partition); + +/* +Return the partition specified in a path, as taken from the devoptab. +*/ +PARTITION* _FAT_partition_getPartitionFromPath (const char* path); + +/* +Create the fs info sector. +*/ +void _FAT_partition_createFSinfo(PARTITION * partition); + +/* +Read the fs info sector data. +*/ +void _FAT_partition_readFSinfo(PARTITION * partition); + +/* +Write the fs info sector data. +*/ +void _FAT_partition_writeFSinfo(PARTITION * partition); + +#endif // _PARTITION_H diff --git a/lib/libntfs/include/ntfs.h b/lib/libntfs/include/ntfs.h new file mode 100644 index 0000000..62f2f71 --- /dev/null +++ b/lib/libntfs/include/ntfs.h @@ -0,0 +1,148 @@ +/** + * ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LIBNTFS_H +#define _LIBNTFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* NTFS errno values */ +#define ENOPART 3000 /* No partition was found */ +#define EINVALPART 3001 /* Specified partition is invalid or not supported */ +#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */ +#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */ + +/* NTFS cache options */ +#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */ +#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */ + +/* NTFS mount flags */ +#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */ +#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */ +#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */ +#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */ +#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */ +#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */ +#define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */ +#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */ +#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES +#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE + +/** + * ntfs_md - NTFS mount descriptor + */ +typedef struct _ntfs_md { + char name[32]; /* Mount name (can be accessed as "name:/") */ + const DISC_INTERFACE *interface; /* Block device containing the mounted partition */ + sec_t startSector; /* Local block address to first sector of partition */ +} ntfs_md; + +/** + * Find all NTFS partitions on a block device. + * + * @param INTERFACE The block device to search + * @param PARTITIONS (out) A pointer to receive the array of partition start sectors + * + * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing PARTITIONS when finished with it + */ +extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions); + +/** + * Mount all NTFS partitions on all inserted block devices. + * + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note All device caches are setup using default values (see above) + */ +extern int ntfsMountAll (ntfs_md **mounts, u32 flags); + +/** + * Mount all NTFS partitions on a block devices. + * + * @param INTERFACE The block device to mount. + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note The device cache is setup using default values (see above) + */ +extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags); + +/** + * Mount a NTFS partition from a specific sector on a block device. + * + * @param NAME The name to mount the device under (can then be accessed as "NAME:/") + * @param INTERFACE The block device to mount + * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) + * @param CACHEPAGECOUNT The total number of pages in the device cache + * @param CACHEPAGESIZE The number of sectors per cache page + * @param FLAGS Additional mounting flags (see above) + * + * @return True if mount was successful, false if no partition was found or an error occurred (see errno) + * @note ntfsFindPartitions should be used first to locate the partitions start sector + */ +extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags); + +/** + * Unmount a NTFS partition. + * + * @param NAME The name of mount used in ntfsMountSimple() and ntfsMount() + * @param FORCE If true unmount even if the device is busy (may lead to data lose) + */ +extern void ntfsUnmount (const char *name, bool force); + +/** + * Get the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * + * @return The volumes name if successful or NULL if an error occurred (see errno) + */ +extern const char *ntfsGetVolumeName (const char *name); + +/** + * Set the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * @param VOLUMENAME The new volume name + * + * @return True if mount was successful, false if an error occurred (see errno) + * @note The mount must be write-enabled else this will fail + */ +extern bool ntfsSetVolumeName (const char *name, const char *volumeName); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBNTFS_H */ diff --git a/lib/libntfs/include/ntfs_frag.h b/lib/libntfs/include/ntfs_frag.h new file mode 100644 index 0000000..889abb1 --- /dev/null +++ b/lib/libntfs/include/ntfs_frag.h @@ -0,0 +1,15 @@ +#ifndef NTFS_FRAG_H_ +#define NTFS_FRAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); +int _NTFS_get_fragments (const char *path, _ntfs_frag_append_t append_fragment, void *callback_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libntfs/orig/AUTHORS b/lib/libntfs/orig/AUTHORS new file mode 100644 index 0000000..b0ae639 --- /dev/null +++ b/lib/libntfs/orig/AUTHORS @@ -0,0 +1,27 @@ + +Present authors of ntfs-3g in alphabetical order: + +Jean-Pierre Andre +Alon Bar-Lev +Dominique L Bouix +Csaba Henk +Bernhard Kaindl +Erik Larsson +Alejandro Pulver +Szabolcs Szakacsits +Miklos Szeredi + + +Past authors in alphabetical order: + +Anton Altaparmakov +Mario Emmenlauer +Yuval Fledel +Yura Pakhuchiy +Richard Russon + + +Nintendo GameCube/Wii port authors in alphabetical order: + +Rhys "Shareese" Koedijk +Dimok diff --git a/lib/libntfs/orig/COPYING b/lib/libntfs/orig/COPYING new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/lib/libntfs/orig/COPYING @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/lib/libntfs/orig/CREDITS b/lib/libntfs/orig/CREDITS new file mode 100644 index 0000000..3c1bc7c --- /dev/null +++ b/lib/libntfs/orig/CREDITS @@ -0,0 +1,50 @@ +The following people have contributed directly or indirectly +to the ntfs-3g project. + +Please let ntfs-3g-devel@lists.sf.net know if you believe +someone is missing, or if you prefer not to be listed. + +Dominique L Bouix +Csaba Henk +Max Khon +Auri Hautam�ki +Gergely Erdelyi +Anton Altaparmakov +Peter Boross +Don Bright +Mario Emmenlauer +Yuval Fledel +Kano from Kanotix +Roland Kletzing +Maarten Lankhorst +Gergely Madarasz +Patrick McLean +Florent Mertens +Yura Pakhuchiy +Miklos Szeredi +Bartosz Taudul +Zhanglinbao +Wade Fitzpatrick +Carsten Einig +Adam Cecile +Bruno Damour +Ales Fruman +Curt McDowell +Thomas Franken +Jonatan Lambert +Klaus Knopper +Zhanglinbao +Ismail Donmez +Laszlo Dvornik +Pallaghy Ajtony +Szabolcs Szakacsits +Jean-Pierre Andre +Alejandro Pulver +Erik Larsson +Alon Bar-Lev + +The following people have contributed directly or indirectly +to the Nintendo GameCube/Wii port of ntfs-3g. + +Michael "Chishm" Chisholm +rodries diff --git a/lib/libntfs/orig/LICENSE b/lib/libntfs/orig/LICENSE new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/lib/libntfs/orig/LICENSE @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/lib/libntfs/orig/Makefile b/lib/libntfs/orig/Makefile new file mode 100644 index 0000000..8745416 --- /dev/null +++ b/lib/libntfs/orig/Makefile @@ -0,0 +1,31 @@ + +default: cube-release wii-release + +all: debug release + +debug: cube-debug wii-debug + +release: cube-release wii-release + +cube-debug: + $(MAKE) -C source PLATFORM=cube BUILD=cube_debug + +wii-debug: + $(MAKE) -C source PLATFORM=wii BUILD=wii_debug + +cube-release: + $(MAKE) -C source PLATFORM=cube BUILD=cube_release + +wii-release: + $(MAKE) -C source PLATFORM=wii BUILD=wii_release + +clean: + $(MAKE) -C source clean + +install: cube-release wii-release + $(MAKE) -C source install + +run: install + $(MAKE) -C example + $(MAKE) -C example run + diff --git a/lib/libntfs/orig/READMII b/lib/libntfs/orig/READMII new file mode 100644 index 0000000..ca8f1ab --- /dev/null +++ b/lib/libntfs/orig/READMII @@ -0,0 +1,54 @@ + +INTRODUCTION +============ + +The NTFS-3G driver is an open source, freely available read/write NTFS driver +for Linux, FreeBSD, Mac OS X, NetBSD, Solaris and Haiku. It provides safe and +fast handling of the Windows XP, Windows Server 2003, Windows 2000, Windows +Vista, and Windows Server 2008 file systems. + +The purpose of the project is to develop, continuously quality test and +support a trustable, featureful and high performance solution for hardware +platforms and operating systems whose users need to reliably interoperate +with NTFS. Besides this practical goal, the project also aims to explore +the limits of the hybrid, kernel/user space filesystem driver approach, +performance, reliability and feature richness per invested effort wise. + +The driver is in STABLE status. The test methods, the test suites used +can be found at + + http://ntfs-3g.org/quality.html + +News, support answers, problem submission instructions, support and discussion +forums, performance numbers and other information are available on the project +web site at + + http://ntfs-3g.org + +For more details on the NTFS-3G project see the 'original' folder included +with this package. + +COMPILING AND INSTALLATION +========================== + +Make sure you have devKitPPC and the latest libogc installed. Then type: + + make + make install # or 'sudo make install' if you aren't root. + + +USAGE +===== + +NTFS related routines can be accessed by adding the following line to your +source file(s). + + #include + +When compiling you must also link against the libntfs. To do this add -lntfs +to the LIBS section of your application Makefile. For example: + + LIBS := -lwiiuse -lbte -lntfs -lfat -logc -lm + +For a more practical example of using NTFS in your application, +see the included 'example' folder. diff --git a/lib/libntfs/orig/example/Makefile b/lib/libntfs/orig/example/Makefile new file mode 100644 index 0000000..5dc4123 --- /dev/null +++ b/lib/libntfs/orig/example/Makefile @@ -0,0 +1,139 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lwiiuse -lbte -lntfs -lfat -logc -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +run: + wiiload $(TARGET).dol + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +%.elf.o : %.elf + @echo $(notdir $<) + $(bin2o) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.jpg.o : %.jpg +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/lib/libntfs/orig/example/data/ehcmodule.elf b/lib/libntfs/orig/example/data/ehcmodule.elf new file mode 100644 index 0000000000000000000000000000000000000000..84106b9fbe3bff1a30d57d793e428709a8da1ccb GIT binary patch literal 24620 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 literal 0 HcmV?d00001 diff --git a/lib/libntfs/orig/example/source/ehcmodule_elf.h b/lib/libntfs/orig/example/source/ehcmodule_elf.h new file mode 100644 index 0000000..e2729ca --- /dev/null +++ b/lib/libntfs/orig/example/source/ehcmodule_elf.h @@ -0,0 +1,3 @@ +extern const u8 ehcmodule_elf_end[]; +extern const u8 ehcmodule_elf[]; +extern const u32 ehcmodule_elf_size; diff --git a/lib/libntfs/orig/example/source/main.c b/lib/libntfs/orig/example/source/main.c new file mode 100644 index 0000000..c7734c4 --- /dev/null +++ b/lib/libntfs/orig/example/source/main.c @@ -0,0 +1,229 @@ +/** + * main.c - Directory listing example for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +static void *xfb = NULL; +static GXRModeObj *rmode = NULL; + +void list(const char *path, int depth) +{ + DIR *pdir; + struct dirent *pent; + struct stat st; + char indent[PATH_MAX] = {0}; + char new_path[PATH_MAX] = {0}; + + // Open the directory + pdir = opendir(path); + if (pdir) { + + // Make this our current directory + chdir(path); + + // Build a directory indent (for better readability) + memset(indent, ' ', depth * 2); + + // List the contents of the directory + while ((pent = readdir(pdir)) != NULL) { + if ((strcmp(pent->d_name, ".") == 0) || (strcmp(pent->d_name, "..") == 0)) + continue; + + // Get the entries stats + if (stat(pent->d_name, &st) == -1) + continue; + + // List the entry + if (S_ISDIR(st.st_mode)) { + printf(" D %s%s/\n", indent, pent->d_name); + + // List the directories contents + sprintf(new_path, "%s/%s", path, pent->d_name); + list(new_path, depth + 1); + chdir(path); + + } else if (S_ISREG(st.st_mode)) { + printf(" F %s%s (%lu)\n", indent, pent->d_name, (unsigned long int)st.st_size); + } else { + printf(" ? %s%s\n", indent, pent->d_name); + } + + } + + // Close the directory + closedir(pdir); + + } else { + printf("opendir(%s) failure.\n", path); + } + + return; +} + +//--------------------------------------------------------------------------------- +int main(int argc, char **argv) { +//--------------------------------------------------------------------------------- + + // Initialise the video system + VIDEO_Init(); + + // This function initialises the attached controllers + WPAD_Init(); + + // Obtain the preferred video mode from the system + // This will correspond to the settings in the Wii menu + rmode = VIDEO_GetPreferredMode(NULL); + + // Allocate memory for the display in the uncached region + xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + + // Initialise the console, required for printf + console_init(xfb, 20, 20, rmode->fbWidth, rmode->xfbHeight, rmode->fbWidth * VI_DISPLAY_PIX_SZ); + + // Set up the video registers with the chosen mode + VIDEO_Configure(rmode); + + // Tell the video hardware where our display memory is + VIDEO_SetNextFramebuffer(xfb); + + // Make the display visible + VIDEO_SetBlack(FALSE); + + // Flush the video register changes to the hardware + VIDEO_Flush(); + + // Wait for Video setup to complete + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + + + // The console understands VT terminal escape codes + // This positions the cursor on row 2, column 0 + // we can use variables for this with format codes too + // e.g. printf ("\x1b[%d;%dH", row, column ); + printf("\x1b[2;0H"); + + printf("\n"); + printf(" NTFS Directory Listing Example\n"); + printf("\n"); + printf(" - 'LEFT' and 'RIGHT' to select a volume\n"); + printf(" - 'A' to enumerate the selected volume\n"); + printf(" - 'HOME' to quit\n"); + printf("\n"); + + bool listed = false; + ntfs_md *mounts = NULL; + int mountCount = 0; + int mountIndex = 0; + int i; + + // Mount FAT devices + fatInitDefault(); + + // Mount all NTFS volumes on all inserted block devices + mountCount = ntfsMountAll(&mounts, NTFS_DEFAULT | NTFS_RECOVER); + if (mountCount == -1) + printf("Error whilst mounting devices (%i).\n", errno); + else if (mountCount == 0) + printf("No NTFS volumes were found and/or mounted.\n"); + else + printf("%i NTFS volumes(s) mounted!\n\n", mountCount); + + // List all mounted NTFS volumes + for (i = 0; i < mountCount; i++) + printf("%i - %s:/ (%s)\n", i + 1, mounts[i].name, ntfsGetVolumeName(mounts[i].name)); + + printf("\n"); + while (1) { + + // Call WPAD_ScanPads each loop, this reads the latest controller states + WPAD_ScanPads(); + + // WPAD_ButtonsDown tells us which buttons were pressed in this loop + // this is a "one shot" state which will not fire again until the button has been released + u32 pressed = WPAD_ButtonsDown(0); + + // Break from main loop + if (pressed & WPAD_BUTTON_HOME) break; + + // If there is at least one volume mounted and we have not yet listed one... + if (mountCount > 0 && !listed) { + + // Deincrement the selected volumes index + if (pressed & WPAD_BUTTON_LEFT) + mountIndex = MIN(MAX(mountIndex - 1, 0), mountCount - 1); + + // Increment the selected volumes index + if (pressed & WPAD_BUTTON_RIGHT) + mountIndex = MIN(MAX(mountIndex + 1, 0), mountCount - 1); + + // Enumerate the selected volumes contents + if (pressed & WPAD_BUTTON_A) { + printf("\n\n"); + + // List the volumes root directory + char path[PATH_MAX] = {0}; + strcpy(path, mounts[mountIndex].name); + strcat(path, ":/"); + list(path, 0); + listed = true; + + printf("\n"); + printf("Press 'HOME' to quit.\n\n"); + + } + + // If we have not listed a volume yet then prompt to select one + if(!listed) { + printf("\rSelect a NTFS volume: < %i >", mountIndex + 1); + } + + } + + // Wait for the next frame + VIDEO_WaitVSync(); + + } + + // Unmount all NTFS volumes and clean up + if (mounts) { + for (i = 0; i < mountCount; i++) + ntfsUnmount(mounts[i].name, true); + free(mounts); + } + + // We return to the launcher application via exit + exit(0); + + return 0; +} + diff --git a/lib/libntfs/orig/example/source/mload.c b/lib/libntfs/orig/example/source/mload.c new file mode 100644 index 0000000..ff6abe3 --- /dev/null +++ b/lib/libntfs/orig/example/source/mload.c @@ -0,0 +1,399 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mload.h" + +static const char mload_fs[] ATTRIBUTE_ALIGN(32) = "/dev/mload"; + +static s32 mload_fd = -1; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init() +{ +int n; + + if(mload_fd>=0) return 0; + + for(n=0;n<10;n++) // try 2.5 seconds + { + mload_fd=IOS_Open(mload_fs, 0); + + if(mload_fd>=0) break; + + usleep(250*1000); + } + +return mload_fd; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close() +{ +int ret; + + if(mload_fd<0) return -1; + + ret=IOS_Close(mload_fd); + + mload_fd=-1; + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id() +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_MLOAD_THREAD_ID, ":"); + + + iosDestroyHeap(hid); + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOAD_BASE, ":ii",starlet_base, size); + + + iosDestroyHeap(hid); + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len) +{ +int ret; +void *buf=NULL; +s32 hid = -1; + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(len+0x800); + + if(hid<0) return hid; + + buf= iosAlloc(hid, len); + + if(!buf) {ret= -1;goto out;} + + + memcpy(buf, addr,len); + + ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_LOAD_MODULE, ":d", buf, len); + + if(ret<0) goto out; + + ret=IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_MODULE, ":"); + + if(ret<0) {ret= -666;goto out;} + +out: + + iosDestroyHeap(hid); + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf) +{ +int n,m; +int p; +u8 *adr; +u32 elf=(u32) my_elf; + +if(elf & 3) return -1; // aligned to 4 please! + +elfheader *head=(void *) elf; +elfphentry *entries; + +if(head->ident0!=0x7F454C46) return -1; +if(head->ident1!=0x01020161) return -1; +if(head->ident2!=0x01000000) return -1; + +p=head->phoff; + +data_elf->start=(void *) head->entry; + +for(n=0; nphnum; n++) + { + entries=(void *) (elf+p); + p+=sizeof(elfphentry); + + if(entries->type == 4) + { + adr=(void *) (elf + entries->offset); + + if(getbe32(0)!=0) return -2; // bad info (sure) + + for(m=4; m < entries->memsz; m+=8) + { + switch(getbe32(m)) + { + case 0x9: + data_elf->start= (void *) getbe32(m+4); + break; + case 0x7D: + data_elf->prio= getbe32(m+4); + break; + case 0x7E: + data_elf->size_stack= getbe32(m+4); + break; + case 0x7F: + data_elf->stack= (void *) (getbe32(m+4)); + break; + + } + + } + + } + else + if(entries->type == 1 && entries->memsz != 0 && entries->vaddr!=0) + { + + if(mload_memset((void *) entries->vaddr, 0, entries->memsz)<0) return -1; + if(mload_seek(entries->vaddr, SEEK_SET)<0) return -1; + if(mload_write((void *) (elf + entries->offset), entries->filesz)<0) return -1; + + } + } + +return 0; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", starlet_addr,starlet_top_stack, stack_size, priority); + + + iosDestroyHeap(hid); + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_STOP_THREAD, "i:", id); + + + iosDestroyHeap(hid); + +return ret; + +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_CONTINUE_THREAD, "i:", id); + + + iosDestroyHeap(hid); + +return ret; + +} +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode) +{ + if(mload_init()<0) return -1; + return IOS_Seek(mload_fd, offset, mode); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size) +{ + if(mload_init()<0) return -1; + return IOS_Read(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size) +{ + if(mload_init()<0) return -1; + return IOS_Write(mload_fd, buf, size); +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, set, len); + + + iosDestroyHeap(hid); + +return ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data() +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return NULL; + + hid = iosCreateHeap(0x800); + + if(hid<0) return NULL; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_EHCI_DATA, ":"); + if(ret<0) return NULL; + + iosDestroyHeap(hid); + +return (void *) ret; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr) +{ +int ret; +s32 hid = -1; + + + if(mload_init()<0) return -1; + + hid = iosCreateHeap(0x800); + + if(hid<0) return hid; + + ret= IOS_IoctlvFormat(hid, mload_fd, MLOAD_SET_ES_IOCTLV, "i:", starlet_addr); + + + iosDestroyHeap(hid); + +return ret; +} + diff --git a/lib/libntfs/orig/example/source/mload.h b/lib/libntfs/orig/example/source/mload.h new file mode 100644 index 0000000..755ba88 --- /dev/null +++ b/lib/libntfs/orig/example/source/mload.h @@ -0,0 +1,194 @@ +/* mload.c (for PPC) (c) 2009, Hermes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MLOAD_H__ +#define __MLOAD_H__ + + +#include +#include +#include +#include +#include +#include "unistd.h" + +#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 +#define MLOAD_LOAD_MODULE 0x4D4C4480 +#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_RUN_THREAD 0x4D4C4482 + +#define MLOAD_STOP_THREAD 0x4D4C4484 +#define MLOAD_CONTINUE_THREAD 0x4D4C4485 + +#define MLOAD_GET_LOAD_BASE 0x4D4C4490 +#define MLOAD_MEMSET 0x4D4C4491 + +#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 + +#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 + +#ifdef __cplusplus +extern "C" { +#endif + + +// from IOS ELF stripper of neimod + +#define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) + +typedef struct +{ + u32 ident0; + u32 ident1; + u32 ident2; + u32 ident3; + u32 machinetype; + u32 version; + u32 entry; + u32 phoff; + u32 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shtrndx; +} elfheader; + +typedef struct +{ + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; +} elfphentry; + +typedef struct +{ + void *start; + int prio; + void *stack; + int size_stack; +} data_elf; + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to init/test if the device is running + +int mload_init(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to close the device (remember call it when rebooting the IOS!) + +int mload_close(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// to get the thread id of mload + +int mload_get_thread_id(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the base and the size of the memory readable/writable to load modules + +int mload_get_load_base(u32 *starlet_base, int *size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load and run a module from starlet (it need to allocate MEM2 to send the elf file) +// the module must be a elf made with stripios + +int mload_module(void *addr, int len); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// load a module from the PPC +// the module must be a elf made with stripios + +int mload_elf(void *my_elf, data_elf *data_elf); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// run one thread (you can use to load modules or binary files) + +int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// stops one starlet thread + +int mload_stop_thread(int id); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// continue one stopped starlet thread + +int mload_continue_thread(int id); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fix starlet address to read/write (uses SEEK_SET, etc as mode) + +int mload_seek(int offset, int mode); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// read bytes from starlet (it update the offset) + +int mload_read(void* buf, u32 size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// write bytes from starlet (it update the offset) + +int mload_write(const void * buf, u32 size); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// fill a block (similar to memset) + +int mload_memset(void *starlet_addr, int set, int len); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// get the ehci datas ( ehcmodule.elf uses this address) + +void * mload_get_ehci_data(); + +/*--------------------------------------------------------------------------------------------------------------*/ + +// set the dev/es ioctlv in routine + +int mload_set_ES_ioctlv_vector(void *starlet_addr); + +/*--------------------------------------------------------------------------------------------------------------*/ + + + +#ifdef __cplusplus + } +#endif + + +#endif diff --git a/lib/libntfs/orig/include/ntfs.h b/lib/libntfs/orig/include/ntfs.h new file mode 100644 index 0000000..62f2f71 --- /dev/null +++ b/lib/libntfs/orig/include/ntfs.h @@ -0,0 +1,148 @@ +/** + * ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LIBNTFS_H +#define _LIBNTFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* NTFS errno values */ +#define ENOPART 3000 /* No partition was found */ +#define EINVALPART 3001 /* Specified partition is invalid or not supported */ +#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */ +#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */ + +/* NTFS cache options */ +#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */ +#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */ + +/* NTFS mount flags */ +#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */ +#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */ +#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */ +#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */ +#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */ +#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */ +#define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */ +#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */ +#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES +#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE + +/** + * ntfs_md - NTFS mount descriptor + */ +typedef struct _ntfs_md { + char name[32]; /* Mount name (can be accessed as "name:/") */ + const DISC_INTERFACE *interface; /* Block device containing the mounted partition */ + sec_t startSector; /* Local block address to first sector of partition */ +} ntfs_md; + +/** + * Find all NTFS partitions on a block device. + * + * @param INTERFACE The block device to search + * @param PARTITIONS (out) A pointer to receive the array of partition start sectors + * + * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing PARTITIONS when finished with it + */ +extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions); + +/** + * Mount all NTFS partitions on all inserted block devices. + * + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note All device caches are setup using default values (see above) + */ +extern int ntfsMountAll (ntfs_md **mounts, u32 flags); + +/** + * Mount all NTFS partitions on a block devices. + * + * @param INTERFACE The block device to mount. + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note The device cache is setup using default values (see above) + */ +extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags); + +/** + * Mount a NTFS partition from a specific sector on a block device. + * + * @param NAME The name to mount the device under (can then be accessed as "NAME:/") + * @param INTERFACE The block device to mount + * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) + * @param CACHEPAGECOUNT The total number of pages in the device cache + * @param CACHEPAGESIZE The number of sectors per cache page + * @param FLAGS Additional mounting flags (see above) + * + * @return True if mount was successful, false if no partition was found or an error occurred (see errno) + * @note ntfsFindPartitions should be used first to locate the partitions start sector + */ +extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags); + +/** + * Unmount a NTFS partition. + * + * @param NAME The name of mount used in ntfsMountSimple() and ntfsMount() + * @param FORCE If true unmount even if the device is busy (may lead to data lose) + */ +extern void ntfsUnmount (const char *name, bool force); + +/** + * Get the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * + * @return The volumes name if successful or NULL if an error occurred (see errno) + */ +extern const char *ntfsGetVolumeName (const char *name); + +/** + * Set the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * @param VOLUMENAME The new volume name + * + * @return True if mount was successful, false if an error occurred (see errno) + * @note The mount must be write-enabled else this will fail + */ +extern bool ntfsSetVolumeName (const char *name, const char *volumeName); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBNTFS_H */ diff --git a/lib/libntfs/orig/source/Makefile b/lib/libntfs/orig/source/Makefile new file mode 100644 index 0000000..9cb768d --- /dev/null +++ b/lib/libntfs/orig/source/Makefile @@ -0,0 +1,130 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +ifeq ($(PLATFORM),wii) +include $(DEVKITPPC)/wii_rules +endif + +ifeq ($(PLATFORM),cube) +include $(DEVKITPPC)/gamecube_rules +endif + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +BUILD ?= wii_release +SOURCES := . +INCLUDES := ../include +LIBDIR := ../lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -Os -Wall -ffast-math -pipe $(MACHDEP) $(INCLUDE) -DHAVE_CONFIG_H +CXXFLAGS = $(CFLAGS) +ASFLAGS := -g +export NTFSBIN := $(LIBDIR)/$(PLATFORM)/libntfs.a + +ifeq ($(BUILD),cube_debug) +CFLAGS += -DDEBUG +CXXFLAGS += -DDEBUG +endif +ifeq ($(BUILD),wii_debug) +CFLAGS += -DDEBUG +CXXFLAGS += -DDEBUG +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) + +all: $(NTFSBIN) + +install: + cp ../include/ntfs.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii + cp ../lib/cube/libntfs.a $(DEVKITPRO)/libogc/lib/cube + +wii-install: + cp ../include/ntfs.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(NTFSBIN): $(OFILES) $(LIBDIR)/$(PLATFORM) + @rm -f "../$(NTFSBIN)" + @$(AR) rcs "../$(NTFSBIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIBDIR)/$(PLATFORM): + mkdir -p ../$(LIBDIR)/$(PLATFORM) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libntfs/orig/source/acls.c b/lib/libntfs/orig/source/acls.c new file mode 100644 index 0000000..6328420 --- /dev/null +++ b/lib/libntfs/orig/source/acls.c @@ -0,0 +1,4266 @@ +/** + * acls.c - General function to process NTFS ACLs + * + * This module is part of ntfs-3g library, but may also be + * integrated in tools running over Linux or Windows + * + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H + /* + * integration into ntfs-3g + */ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif +#include +#include +#include + +#include "types.h" +#include "layout.h" +#include "security.h" +#include "acls.h" +#include "misc.h" +#else + + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + + /* + * integration into secaudit/Win32 + */ +#ifdef WIN32 +#include +#include +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#else + /* + * integration into secaudit/STSC + */ +#ifdef STSC +#include +#undef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#else + /* + * integration into secaudit/Linux + */ +#include +#include +#include +#include +#endif /* STSC */ +#endif /* WIN32 */ +#include "secaudit.h" +#endif /* HAVE_CONFIG_H */ + +/* + * A few useful constants + */ + +/* + * null SID (S-1-0-0) + */ + +static const char nullsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 0, /* base */ + 0, 0, 0, 0 /* 1st level */ + }; + +static const SID *nullsid = (const SID*)nullsidbytes; + +/* + * SID for world (S-1-1-0) + */ + +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +const SID *worldsid = (const SID*)worldsidbytes; + +/* + * SID for administrator + */ + +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ +}; + +const SID *adminsid = (const SID*)adminsidbytes; + +/* + * SID for system + */ + +static const char systemsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 18, 0, 0, 0 /* 1st level */ + }; + +static const SID *systemsid = (const SID*)systemsidbytes; + +/* + * SID for generic creator-owner + * S-1-3-0 + */ + +static const char ownersidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +static const SID *ownersid = (const SID*)ownersidbytes; + +/* + * SID for generic creator-group + * S-1-3-1 + */ + +static const char groupsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 1, 0, 0, 0 /* 1st level */ +} ; + +static const SID *groupsid = (const SID*)groupsidbytes; + +/* + * Determine the size of a SID + */ + +int ntfs_sid_size(const SID * sid) +{ + return (sid->sub_authority_count * 4 + 8); +} + +/* + * Test whether two SID are equal + */ + +BOOL ntfs_same_sid(const SID *first, const SID *second) +{ + int size; + + size = ntfs_sid_size(first); + return ((ntfs_sid_size(second) == size) + && !memcmp(first, second, size)); +} + +/* + * Test whether a SID means "world user" + * Local users group also recognized as world + */ + +static int is_world_sid(const SID * usid) +{ + return ( + /* check whether S-1-1-0 : world */ + ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(1)) + && (usid->sub_authority[0] == const_cpu_to_le32(0))) + + /* check whether S-1-5-32-545 : local user */ + || ((usid->sub_authority_count == 2) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(32)) + && (usid->sub_authority[1] == const_cpu_to_le32(545))) + ); +} + +/* + * Test whether a SID means "some user (or group)" + * Currently we only check for S-1-5-21... but we should + * probably test for other configurations + */ + +BOOL ntfs_is_user_sid(const SID *usid) +{ + return ((usid->sub_authority_count == 5) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(21))); +} + +/* + * Determine the size of a security attribute + * whatever the order of fields + */ + +unsigned int ntfs_attr_size(const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + const SID *psid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int endsid; + unsigned int endacl; + unsigned int attrsz; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + /* + * First check group, which is the last field in all descriptors + * we build, and in most descriptors built by Windows + */ + attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + offgroup = le32_to_cpu(phead->group); + if (offgroup >= attrsz) { + /* find end of GSID */ + psid = (const SID*)&attr[offgroup]; + endsid = offgroup + ntfs_sid_size(psid); + if (endsid > attrsz) attrsz = endsid; + } + offowner = le32_to_cpu(phead->owner); + if (offowner >= attrsz) { + /* find end of USID */ + psid = (const SID*)&attr[offowner]; + endsid = offowner + ntfs_sid_size(psid); + attrsz = endsid; + } + offsacl = le32_to_cpu(phead->sacl); + if (offsacl >= attrsz) { + /* find end of SACL */ + psacl = (const ACL*)&attr[offsacl]; + endacl = offsacl + le16_to_cpu(psacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + + + /* find end of DACL */ + offdacl = le32_to_cpu(phead->dacl); + if (offdacl >= attrsz) { + pdacl = (const ACL*)&attr[offdacl]; + endacl = offdacl + le16_to_cpu(pdacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + return (attrsz); +} + +/* + * Do sanity checks on a SID read from storage + * (just check revision and number of authorities) + */ + +BOOL ntfs_valid_sid(const SID *sid) +{ + return ((sid->revision == SID_REVISION) + && (sid->sub_authority_count >= 1) + && (sid->sub_authority_count <= 8)); +} + +/* + * Check whether a SID is acceptable for an implicit + * mapping pattern. + * It should have been already checked it is a valid user SID. + * + * The last authority reference has to be >= 1000 (Windows usage) + * and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits + * from a gid an be inserted with no overflow. + */ + +BOOL ntfs_valid_pattern(const SID *sid) +{ + int cnt; + u32 auth; + le32 leauth; + + cnt = sid->sub_authority_count; + leauth = sid->sub_authority[cnt-1]; + auth = le32_to_cpu(leauth); + return ((auth >= 1000) && (auth <= 0x7fffffff)); +} + +/* + * Compute the uid or gid associated to a SID + * through an implicit mapping + * + * Returns 0 (root) if it does not match pattern + */ + +static u32 findimplicit(const SID *xsid, const SID *pattern, int parity) +{ + BIGSID defsid; + SID *psid; + u32 xid; /* uid or gid */ + int cnt; + u32 carry; + le32 leauth; + u32 uauth; + u32 xlast; + u32 rlast; + + memcpy(&defsid,pattern,ntfs_sid_size(pattern)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + xid = 0; + if (xsid->sub_authority_count == cnt) { + psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1]; + leauth = xsid->sub_authority[cnt-1]; + xlast = le32_to_cpu(leauth); + leauth = pattern->sub_authority[cnt-1]; + rlast = le32_to_cpu(leauth); + + if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) { + /* direct check for basic situation */ + if (ntfs_same_sid(psid,xsid)) + xid = ((xlast - rlast) >> 1) & 0x3fffffff; + else { + /* + * check whether part of mapping had to be + * recorded in a higher level authority + */ + carry = 1; + do { + leauth = psid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + 1; + psid->sub_authority[cnt-2] + = cpu_to_le32(uauth); + } while (!ntfs_same_sid(psid,xsid) + && (++carry < 4)); + if (carry < 4) + xid = (((xlast - rlast) >> 1) + & 0x3fffffff) | (carry << 30); + } + } + } + return (xid); +} + +/* + * Find usid mapped to a Linux user + * Returns NULL if not found + */ + +const SID *ntfs_find_usid(const struct MAPPING* usermapping, + uid_t uid, SID *defusid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!uid) + sid = adminsid; + else { + p = usermapping; + while (p && p->xid && ((uid_t)p->xid != uid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defusid, p->sid, ntfs_sid_size(p->sid)); + cnt = defusid->sub_authority_count; + leauth = defusid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff); + defusid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (uid & 0xc0000000) { + leauth = defusid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3); + defusid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defusid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +const SID *ntfs_find_gsid(const struct MAPPING* groupmapping, + gid_t gid, SID *defgsid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!gid) + sid = adminsid; + else { + p = groupmapping; + while (p && p->xid && ((gid_t)p->xid != gid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defgsid, p->sid, ntfs_sid_size(p->sid)); + cnt = defgsid->sub_authority_count; + leauth = defgsid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1; + defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (gid & 0xc0000000) { + leauth = defgsid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3); + defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defgsid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux owner mapped to a usid + * Returns 0 (root) if not found + */ + +uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid) +{ + uid_t uid; + const struct MAPPING *p; + + p = usermapping; + while (p && p->xid && !ntfs_same_sid(usid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + uid = findimplicit(usid,p->sid,0); + else + uid = (p ? p->xid : 0); + return (uid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) +{ + gid_t gid; + const struct MAPPING *p; + int gsidsz; + + gsidsz = ntfs_sid_size(gsid); + p = groupmapping; + while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + gid = findimplicit(gsid,p->sid,1); + else + gid = (p ? p->xid : 0); + return (gid); +} + +/* + * Check the validity of the ACEs in a DACL or SACL + */ + +static BOOL valid_acl(const ACL *pacl, unsigned int end) +{ + const ACCESS_ALLOWED_ACE *pace; + unsigned int offace; + unsigned int acecnt; + unsigned int acesz; + unsigned int nace; + BOOL ok; + + ok = TRUE; + acecnt = le16_to_cpu(pacl->ace_count); + offace = sizeof(ACL); + for (nace = 0; (nace < acecnt) && ok; nace++) { + /* be sure the beginning is within range */ + if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end) + ok = FALSE; + else { + pace = (const ACCESS_ALLOWED_ACE*) + &((const char*)pacl)[offace]; + acesz = le16_to_cpu(pace->size); + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + ok = FALSE; + offace += acesz; + } + } + return (ok); +} + +/* + * Do sanity checks on security descriptors read from storage + * basically, we make sure that every field holds within + * allocated storage + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + BOOL ok; + + ok = TRUE; + + /* + * first check overall size if within allocation range + * and a DACL is present + * and owner and group SID are valid + */ + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + offsacl = le32_to_cpu(phead->sacl); + offowner = le32_to_cpu(phead->owner); + offgroup = le32_to_cpu(phead->group); + pdacl = (const ACL*)&securattr[offdacl]; + psacl = (const ACL*)&securattr[offsacl]; + + /* + * size check occurs before the above pointers are used + * + * "DR Watson" standard directory on WinXP has an + * old revision and no DACL though SE_DACL_PRESENT is set + */ + if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (phead->revision == SECURITY_DESCRIPTOR_REVISION) + && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offowner + 2) < attrsz) + && (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offgroup + 2) < attrsz) + && (!offdacl + || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offdacl+sizeof(ACL) < attrsz))) + && (!offsacl + || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offsacl+sizeof(ACL) < attrsz))) + && !(phead->owner & const_cpu_to_le32(3)) + && !(phead->group & const_cpu_to_le32(3)) + && !(phead->dacl & const_cpu_to_le32(3)) + && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) + && ntfs_valid_sid((const SID*)&securattr[offowner]) + && ntfs_valid_sid((const SID*)&securattr[offgroup]) + /* + * if there is an ACL, as indicated by offdacl, + * require SE_DACL_PRESENT + * but "Dr Watson" has SE_DACL_PRESENT though no DACL + */ + && (!offdacl + || ((phead->control & SE_DACL_PRESENT) + && ((pdacl->revision == ACL_REVISION) + || (pdacl->revision == ACL_REVISION_DS)))) + /* same for SACL */ + && (!offsacl + || ((phead->control & SE_SACL_PRESENT) + && ((psacl->revision == ACL_REVISION) + || (psacl->revision == ACL_REVISION_DS))))) { + /* + * Check the DACL and SACL if present + */ + if ((offdacl && !valid_acl(pdacl,attrsz - offdacl)) + || (offsacl && !valid_acl(psacl,attrsz - offsacl))) + ok = FALSE; + } else + ok = FALSE; + return (ok); +} + +/* + * Copy the inheritable parts of an ACL + * + * Returns the size of the new ACL + * or zero if nothing is inheritable + */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir) +{ + unsigned int src; + unsigned int dst; + int oldcnt; + int newcnt; + unsigned int selection; + int nace; + int acesz; + int usidsz; + int gsidsz; + const ACCESS_ALLOWED_ACE *poldace; + ACCESS_ALLOWED_ACE *pnewace; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + + /* ACL header */ + + newacl->revision = ACL_REVISION; + newacl->alignment1 = 0; + newacl->alignment2 = const_cpu_to_le16(0); + src = dst = sizeof(ACL); + + selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE); + newcnt = 0; + oldcnt = le16_to_cpu(oldacl->ace_count); + for (nace = 0; nace < oldcnt; nace++) { + poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); + acesz = le16_to_cpu(poldace->size); + /* inheritance for access */ + if (poldace->flags & selection) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (pnewace->mask & GENERIC_ALL) { + pnewace->mask &= ~GENERIC_ALL; + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_WRITE + | DIR_EXEC; + else + /* + * The last flag is not defined for a file, + * however Windows sets it, so do the same + */ + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_WRITE + | FILE_EXEC + | cpu_to_le32(0x40); + } + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + dst += acesz; + newcnt++; + } + /* inheritance for further inheritance */ + if (fordir + && (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + } + dst += acesz; + newcnt++; + } + src += acesz; + } + /* + * Adjust header if something was inherited + */ + if (dst > sizeof(ACL)) { + newacl->ace_count = cpu_to_le16(newcnt); + newacl->size = cpu_to_le16(dst); + } else + dst = 0; + return (dst); +} + +#if POSIXACLS + +/* + * Do sanity checks on a Posix descriptor + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc) +{ + const struct POSIX_ACL *pacl; + int i; + BOOL ok; + u16 tag; + u32 id; + int perms; + struct { + u16 previous; + u32 previousid; + u16 tagsset; + mode_t mode; + int owners; + int groups; + int others; + } checks[2], *pchk; + + for (i=0; i<2; i++) { + checks[i].mode = 0; + checks[i].tagsset = 0; + checks[i].owners = 0; + checks[i].groups = 0; + checks[i].others = 0; + checks[i].previous = 0; + checks[i].previousid = 0; + } + ok = TRUE; + pacl = &pxdesc->acl; + /* + * header (strict for now) + */ + if ((pacl->version != POSIX_VERSION) + || (pacl->flags != 0) + || (pacl->filler != 0)) + ok = FALSE; + /* + * Reject multiple owner, group or other + * but do not require them to be present + * Also check the ACEs are in correct order + * which implies there is no duplicates + */ + for (i=0; iacccnt + pxdesc->defcnt; i++) { + if (i >= pxdesc->firstdef) + pchk = &checks[1]; + else + pchk = &checks[0]; + perms = pacl->ace[i].perms; + tag = pacl->ace[i].tag; + pchk->tagsset |= tag; + id = pacl->ace[i].id; + if (perms & ~7) ok = FALSE; + if ((tag < pchk->previous) + || ((tag == pchk->previous) + && (id <= pchk->previousid))) + ok = FALSE; + pchk->previous = tag; + pchk->previousid = id; + switch (tag) { + case POSIX_ACL_USER_OBJ : + if (pchk->owners++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + if (pchk->groups++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + case POSIX_ACL_OTHER : + if (pchk->others++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms; + break; + case POSIX_ACL_USER : + case POSIX_ACL_GROUP : + if (id == (u32)-1) + ok = FALSE; + break; + case POSIX_ACL_MASK : + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + default : + ok = FALSE; + break; + } + } + if ((pxdesc->acccnt > 0) + && ((checks[0].owners != 1) || (checks[0].groups != 1) + || (checks[0].others != 1))) + ok = FALSE; + /* do not check owner, group or other are present in */ + /* the default ACL, Windows does not necessarily set them */ + /* descriptor */ + if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef)) + ok = FALSE; + if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0)) + ok = FALSE; + /* check mode, unless null or no tag set */ + if (pxdesc->mode + && checks[0].tagsset + && (checks[0].mode != (pxdesc->mode & 0777))) + ok = FALSE; + /* check tagsset */ + if (pxdesc->tagsset != checks[0].tagsset) + ok = FALSE; + return (ok); +} + +/* + * Set standard header data into a Posix ACL + * The mode argument should provide the 3 upper bits of target mode + */ + +static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode) +{ + mode_t mode; + u16 tagsset; + struct POSIX_ACE *pace; + int i; + + mode = basemode & 07000; + tagsset = 0; + for (i=0; iacccnt; i++) { + pace = &pxdesc->acl.ace[i]; + tagsset |= pace->tag; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + mode |= (pace->perms & 7) << 6; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((pace->perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= pace->perms & 7; + break; + default : + break; + } + } + pxdesc->tagsset = tagsset; + pxdesc->mode = mode; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + return (mode); +} + +/* + * Sort ACEs in a Posix ACL + * This is useful for always getting reusable converted ACLs, + * it also helps in merging ACEs. + * Repeated tag+id are allowed and not merged here. + * + * Tags should be in ascending sequence and for a repeatable tag + * ids should be in ascending sequence. + */ + +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc) +{ + struct POSIX_ACL *pacl; + struct POSIX_ACE ace; + int i; + int offs; + BOOL done; + u16 tag; + u16 previous; + u32 id; + u32 previousid; + + + /* + * Check sequencing of tag+id in access ACE's + */ + pacl = &pxdesc->acl; + do { + done = TRUE; + previous = pacl->ace[0].tag; + previousid = pacl->ace[0].id; + for (i=1; iacccnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } while (!done); + /* + * Same for default ACEs + */ + do { + done = TRUE; + if ((pxdesc->defcnt) > 1) { + offs = pxdesc->firstdef; + previous = pacl->ace[offs].tag; + previousid = pacl->ace[offs].id; + for (i=offs+1; idefcnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } + } while (!done); +} + +/* + * Merge a new mode into a Posix descriptor + * The Posix descriptor is not reallocated, its size is unchanged + * + * returns 0 if ok + */ + +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode) +{ + int i; + BOOL maskfound; + struct POSIX_ACE *pace; + int todo; + + maskfound = FALSE; + todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER; + for (i=pxdesc->acccnt-1; i>=0; i--) { + pace = &pxdesc->acl.ace[i]; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + pace->perms = (mode >> 6) & 7; + todo &= ~POSIX_ACL_USER_OBJ; + break; + case POSIX_ACL_GROUP_OBJ : + if (!maskfound) + pace->perms = (mode >> 3) & 7; + todo &= ~POSIX_ACL_GROUP_OBJ; + break; + case POSIX_ACL_MASK : + pace->perms = (mode >> 3) & 7; + maskfound = TRUE; + break; + case POSIX_ACL_OTHER : + pace->perms = mode & 7; + todo &= ~POSIX_ACL_OTHER; + break; + default : + break; + } + } + pxdesc->mode = mode; + return (todo ? -1 : 0); +} + +/* + * Replace an access or default Posix ACL + * The resulting ACL is checked for validity + * + * Returns a new ACL or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt) +{ + struct POSIX_SECURITY *newpxdesc; + size_t newsize; + int offset; + int oldoffset; + int i; + + if (deflt) + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE); + else + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(newsize); + if (newpxdesc) { + if (deflt) { + offset = oldpxdesc->acccnt; + newpxdesc->acccnt = oldpxdesc->acccnt; + newpxdesc->defcnt = count; + newpxdesc->firstdef = offset; + /* copy access ACEs */ + for (i=0; iacccnt; i++) + newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i]; + /* copy default ACEs */ + for (i=0; iacl.ace[i + offset] = newacl->ace[i]; + } else { + offset = count; + newpxdesc->acccnt = count; + newpxdesc->defcnt = oldpxdesc->defcnt; + newpxdesc->firstdef = count; + /* copy access ACEs */ + for (i=0; iacl.ace[i] = newacl->ace[i]; + /* copy default ACEs */ + oldoffset = oldpxdesc->firstdef; + for (i=0; idefcnt; i++) + newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset]; + } + /* assume special flags unchanged */ + posix_header(newpxdesc, oldpxdesc->mode); + if (!ntfs_valid_posix(newpxdesc)) { + /* do not log, this is an application error */ + free(newpxdesc); + newpxdesc = (struct POSIX_SECURITY*)NULL; + errno = EINVAL; + } + } else + errno = ENOMEM; + return (newpxdesc); +} + +/* + * Build an inherited Posix descriptor from parent + * descriptor (if any) restricted to creation mode + * + * Returns the inherited descriptor or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t mask, BOOL isdir) +{ + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + int count; + int defcnt; + int size; + int i; + s16 tagsset; + + if (pxdesc && pxdesc->defcnt) { + if (isdir) + count = 2*pxdesc->defcnt + 3; + else + count = pxdesc->defcnt + 3; + } else + count = 3; + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE)); + if (pydesc) { + /* + * Copy inherited tags and adapt perms + * Use requested mode, ignoring umask + * (not possible with older versions of fuse) + */ + tagsset = 0; + defcnt = (pxdesc ? pxdesc->defcnt : 0); + for (i=defcnt-1; i>=0; i--) { + pyace = &pydesc->acl.ace[i]; + *pyace = pxdesc->acl.ace[pxdesc->firstdef + i]; + switch (pyace->tag) { + case POSIX_ACL_USER_OBJ : + pyace->perms &= (mode >> 6) & 7; + break; + case POSIX_ACL_GROUP_OBJ : + if (!(tagsset & POSIX_ACL_MASK)) + pyace->perms &= (mode >> 3) & 7; + break; + case POSIX_ACL_OTHER : + pyace->perms &= mode & 7; + break; + case POSIX_ACL_MASK : + pyace->perms &= (mode >> 3) & 7; + break; + default : + break; + } + tagsset |= pyace->tag; + } + pydesc->acccnt = defcnt; + /* + * If some standard tags were missing, append them from mode + * and sort the list + * Here we have to use the umask'ed mode + */ + if (~tagsset & (POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) { + i = defcnt; + /* owner was missing */ + if (!(tagsset & POSIX_ACL_USER_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 6) & 7; + tagsset |= POSIX_ACL_USER_OBJ; + i++; + } + /* owning group was missing */ + if (!(tagsset & POSIX_ACL_GROUP_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 3) & 7; + tagsset |= POSIX_ACL_GROUP_OBJ; + i++; + } + /* other was missing */ + if (!(tagsset & POSIX_ACL_OTHER)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_OTHER; + pyace->id = -1; + pyace->perms = mode & ~mask & 7; + tagsset |= POSIX_ACL_OTHER; + i++; + } + pydesc->acccnt = i; + pydesc->firstdef = i; + pydesc->defcnt = 0; + ntfs_sort_posix(pydesc); + } + + /* + * append as a default ACL if a directory + */ + pydesc->firstdef = pydesc->acccnt; + if (defcnt && isdir) { + size = sizeof(struct POSIX_ACE)*defcnt; + memcpy(&pydesc->acl.ace[pydesc->firstdef], + &pxdesc->acl.ace[pxdesc->firstdef],size); + pydesc->defcnt = defcnt; + } else { + pydesc->defcnt = 0; + } + /* assume special bits are not inherited */ + posix_header(pydesc, mode & 07000); + if (!ntfs_valid_posix(pydesc)) { + ntfs_log_error("Error building an inherited Posix desc\n"); + errno = EIO; + free(pydesc); + pydesc = (struct POSIX_SECURITY*)NULL; + } + } else + errno = ENOMEM; + return (pydesc); +} + +static int merge_lists_posix(struct POSIX_ACE *targetace, + const struct POSIX_ACE *firstace, + const struct POSIX_ACE *secondace, + int firstcnt, int secondcnt) +{ + int k; + + k = 0; + /* + * No list is exhausted : + * if same tag+id in both list : + * ignore ACE from second list + * else take the one with smaller tag+id + */ + while ((firstcnt > 0) && (secondcnt > 0)) + if ((firstace->tag == secondace->tag) + && (firstace->id == secondace->id)) { + secondace++; + secondcnt--; + } else + if ((firstace->tag < secondace->tag) + || ((firstace->tag == secondace->tag) + && (firstace->id < secondace->id))) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } else { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + /* + * One list is exhausted, copy the other one + */ + while (firstcnt > 0) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } + while (secondcnt > 0) { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + return (k); +} + +/* + * Merge two Posix ACLs + * The input ACLs have to be adequately sorted + * + * Returns the merged ACL, which is allocated and has to be freed by caller, + * or NULL if failed + */ + +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second) +{ + struct POSIX_SECURITY *pxdesc; + struct POSIX_ACE *targetace; + const struct POSIX_ACE *firstace; + const struct POSIX_ACE *secondace; + size_t size; + int k; + + size = sizeof(struct POSIX_SECURITY) + + (first->acccnt + first->defcnt + + second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE); + pxdesc = (struct POSIX_SECURITY*)malloc(size); + if (pxdesc) { + /* + * merge access ACEs + */ + firstace = first->acl.ace; + secondace = second->acl.ace; + targetace = pxdesc->acl.ace; + k = merge_lists_posix(targetace,firstace,secondace, + first->acccnt,second->acccnt); + pxdesc->acccnt = k; + /* + * merge default ACEs + */ + pxdesc->firstdef = k; + firstace = &first->acl.ace[first->firstdef]; + secondace = &second->acl.ace[second->firstdef]; + targetace = &pxdesc->acl.ace[k]; + k = merge_lists_posix(targetace,firstace,secondace, + first->defcnt,second->defcnt); + pxdesc->defcnt = k; + /* + * build header + */ + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + pxdesc->mode = 0; + pxdesc->tagsset = 0; + } else + errno = ENOMEM; + return (pxdesc); +} + +struct BUILD_CONTEXT { + BOOL isdir; + BOOL adminowns; + BOOL groupowns; + u16 selfuserperms; + u16 selfgrpperms; + u16 grpperms; + u16 othperms; + u16 mask; + u16 designates; + u16 withmask; + u16 rootspecial; +} ; + + + +static BOOL build_user_denials(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL rejected; + BOOL rootuser; + BOOL avoidmask; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each designated user (except root) + * WRITE_OWNER is inserted so that + * the mask can be identified + */ + if (!avoidmask && !rootuser) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns && !rootuser) { + if (!pset->groupowns) { + mixperms = pset->grpperms | pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } else { + mixperms = ~pset->grpperms & pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + +static BOOL build_user_grants(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + u16 perms; + u16 tag; + BOOL rejected; + BOOL rootuser; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid) + sidsz = ntfs_sid_size(sid); + else + rejected = TRUE; + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + sidsz = ntfs_sid_size(sid); + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->flags = flags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + return (!rejected); +} + + + /* a grant ACE for group */ + /* unless group-obj has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + +static BOOL build_group_denials_grant(ACL *pacl, + const SID *gsid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL avoidmask; + BOOL rootgroup; + BOOL rejected; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + rootgroup = FALSE; + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_GROUP_OBJ) + sid = gsid; + else + if (pxace->id) + sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + pxace->id, (SID*)&defsid); + else { + sid = adminsid; + rootgroup = TRUE; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each group + * WRITE_OWNER is inserted so that + * the mask can be identified + * Note : this mask may lead on Windows to + * deny rights to administrators belonging + * to some user group + */ + if ((!avoidmask && !rootgroup) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ))) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + if (!rejected + && (pset->adminowns + || pset->groupowns + || avoidmask + || rootgroup + || (perms != pset->othperms))) { + grants = WORLD_RIGHTS; + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns + && !pset->groupowns + && !rootgroup) { + mixperms = pset->othperms; + if (tag == POSIX_ACL_GROUP_OBJ) + mixperms |= pset->selfgrpperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + + /* now insert grants to group if more than world */ + if (pset->adminowns + || pset->groupowns + || (avoidmask && (pset->designates || pset->withmask)) + || (perms & ~pset->othperms) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ)) + || (tag == POSIX_ACL_GROUP)) { + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + + +/* + * Build an ACL composed of several ACE's + * returns size of ACL or zero if failed + * + * Three schemes are defined : + * + * 1) if root is neither owner nor group up to 7 ACE's are set up : + * - denials to owner (preventing grants to world or group to apply) + * + mask denials to designated user (unless mask allows all) + * + denials to designated user + * - grants to owner (always present - first grant) + * + grants to designated user + * + mask denial to group (unless mask allows all) + * - denials to group (preventing grants to world to apply) + * - grants to group (unless group has no more than world rights) + * + mask denials to designated group (unless mask allows all) + * + grants to designated group + * + denials to designated group + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * The same scheme is applied for Posix ACLs, with the mask represented + * as denials prepended to grants for designated users and groups + * + * This is inspired by an Internet Draft from Marius Aamodt Eriksen + * for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt) + * More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt) + * are not followed, as they ignore the Posix mask and lead to + * loss of compatibility with Linux implementations on other fs. + * + * Note that denials to group are located after grants to owner. + * This only occurs in the unfrequent situation where world + * has more rights than group and cannot be avoided if owner and other + * have some common right which is denied to group (eg for mode 745 + * executing has to be denied to group, but not to owner or world). + * This rare situation is processed by Windows correctly, but + * Windows utilities may want to change the order, with a + * consequence of applying the group denials to the Windows owner. + * The interpretation on Linux is not affected by the order change. + * + * 2) if root is either owner or group, two problems arise : + * - granting full rights to administrator (as needed to transpose + * to Windows rights bypassing granting to root) would imply + * Linux permissions to always be seen as rwx, no matter the chmod + * - there is no different SID to separate an administrator owner + * from an administrator group. Hence Linux permissions for owner + * would always be similar to permissions to group. + * + * as a work-around, up to 5 ACE's are set up if owner or group : + * - grants to owner, always present at first position + * - grants to group, always present + * - grants to world, unless none + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (owner, group and administrator are the same, + * as a consequence any denials would damage administrator rights) + * but on Linux, privileges to administrator are ignored (they + * are not needed as root has always full privileges), and + * neither grants to group are applied to owner, nor grants to + * world are applied to owner or group. + * + * 3) finally a similar situation arises when group is owner (they + * have the same SID), but is not root. + * In this situation up to 6 ACE's are set up : + * + * - denials to owner (preventing grants to world to apply) + * - grants to owner (always present) + * - grants to group (unless groups has same rights as world) + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (as owner and group are the same), but this has + * no impact on administrator rights + * + * Special flags (S_ISVTX, S_ISGID, S_ISUID) : + * an extra null ACE is inserted to hold these flags, using + * the same conventions as cygwin. + * + */ + +static int buildacls_posix(struct MAPPING* const mapping[], + char *secattr, int offs, const struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + struct BUILD_CONTEXT aceset[2], *pset; + BOOL adminowns; + BOOL groupowns; + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + const struct POSIX_ACE *pxace; + BOOL ok; + mode_t mode; + u16 tag; + u16 perms; + ACE_FLAGS flags; + int pos; + int i; + int k; + BIGSID defsid; + const SID *sid; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + mode = pxdesc->mode; + /* adminowns and groupowns are used for both lists */ + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + ok = TRUE; + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(0); + pacl->alignment2 = const_cpu_to_le16(0); + + /* + * Determine what is allowed to some group or world + * to prevent designated users or other groups to get + * rights from groups or world + * Do the same if owner and group appear as designated + * user or group + * Also get global mask + */ + for (k=0; k<2; k++) { + pset = &aceset[k]; + pset->selfuserperms = 0; + pset->selfgrpperms = 0; + pset->grpperms = 0; + pset->othperms = 0; + pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + pset->designates = 0; + pset->withmask = 0; + pset->rootspecial = 0; + pset->adminowns = adminowns; + pset->groupowns = groupowns; + pset->isdir = isdir; + } + + for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) { + if (i >= pxdesc->acccnt) { + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + switch (pxace->tag) { + case POSIX_ACL_USER : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,usid)) + pset->selfuserperms |= pxace->perms; + } else + /* root as designated user is processed apart */ + pset->rootspecial = TRUE; + break; + case POSIX_ACL_GROUP : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_GSID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,gsid)) + pset->selfgrpperms |= pxace->perms; + } else + /* root as designated group is processed apart */ + pset->rootspecial = TRUE; + /* fall through */ + case POSIX_ACL_GROUP_OBJ : + pset->grpperms |= pxace->perms; + break; + case POSIX_ACL_OTHER : + pset->othperms = pxace->perms; + break; + case POSIX_ACL_MASK : + pset->withmask++; + pset->mask = pxace->perms; + default : + break; + } + } + +if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) { +ntfs_log_error("** error : access and default not consecutive\n"); +return (0); +} + /* + * First insert all denials for owner and each + * designated user (with mask if needed) + */ + + pacl->ace_count = const_cpu_to_le16(0); + pacl->size = const_cpu_to_le16(sizeof(ACL)); + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* insert denial ACEs for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + + ok = build_user_denials(pacl, + usid, mapping, flags, pxace, pset); + break; + default : + break; + } + } + + /* + * for directories, insert a world execution denial + * inherited to plain files. + * This is to prevent Windows from granting execution + * of files through inheritance from parent directory + */ + + if (isdir && ok) { + pos = le16_to_cpu(pacl->size); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + + /* + * now insert (if needed) + * - grants to owner and designated users + * - mask and denials for all groups + * - grants to other + */ + + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* ACE for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + ok = build_user_grants(pacl,usid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_GROUP : + case POSIX_ACL_GROUP_OBJ : + + /* denials and grants for groups */ + + ok = build_group_denials_grant(pacl,gsid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_OTHER : + + /* grants for other users */ + + pos = le16_to_cpu(pacl->size); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + grants = WORLD_RIGHTS; + if (isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + break; + } + } + + if (!ok) { + errno = EINVAL; + pos = 0; + } else { + /* an ACE for administrators */ + /* always full access */ + + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (isdir) + flags = OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE; + else + flags = NO_PROPAGATE_INHERIT_ACE; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + } + return (ok ? pos : 0); +} + +#endif /* POSIXACLS */ + +static int buildacls(char *secattr, int offs, mode_t mode, int isdir, + const SID * usid, const SID * gsid) +{ + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + BOOL adminowns; + BOOL groupowns; + ACE_FLAGS gflags; + int pos; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + le32 denials; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(1); + pacl->alignment2 = const_cpu_to_le16(0); + pos = sizeof(ACL); + acecnt = 0; + + /* compute a grant ACE for owner */ + /* this ACE will be inserted after denial for owner */ + + grants = OWNER_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXUSR) + grants |= DIR_EXEC; + if (mode & S_IWUSR) + grants |= DIR_WRITE; + if (mode & S_IRUSR) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXUSR) + grants |= FILE_EXEC; + if (mode & S_IWUSR) + grants |= FILE_WRITE; + if (mode & S_IRUSR) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + if (!adminowns) { + if (!groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= DIR_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= DIR_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= FILE_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= FILE_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= FILE_READ; + } + } else { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= DIR_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= DIR_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= FILE_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= FILE_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(usidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + } + } + /* + * for directories, a world execution denial + * inherited to plain files + */ + + if (isdir) { + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + } + + + /* now insert grants to owner */ + pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(usidsz + 8); + pgace->flags = gflags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + + /* a grant ACE for group */ + /* unless group has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + + if (adminowns + || groupowns + || (((mode >> 3) ^ mode) & 7)) { + grants = WORLD_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXGRP) + grants |= DIR_EXEC; + if (mode & S_IWGRP) + grants |= DIR_WRITE; + if (mode & S_IRGRP) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXGRP) + grants |= FILE_EXEC; + if (mode & S_IWGRP) + grants |= FILE_WRITE; + if (mode & S_IRGRP) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + if (!adminowns && !groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + denials |= DIR_EXEC; + if (mode & S_IWOTH) + denials |= DIR_WRITE; + if (mode & S_IROTH) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + denials |= FILE_EXEC; + if (mode & S_IWOTH) + denials |= FILE_WRITE; + if (mode & S_IROTH) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(gsidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + if (adminowns + || groupowns + || ((mode >> 3) & ~mode & 7)) { + /* now insert grants to group */ + /* if more rights than other */ + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = gflags; + pgace->size = cpu_to_le16(gsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + /* an ACE for world users */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + grants = WORLD_RIGHTS; + if (isdir) { + pgace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + grants |= DIR_EXEC; + if (mode & S_IWOTH) + grants |= DIR_WRITE; + if (mode & S_IROTH) + grants |= DIR_READ; + } else { + pgace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + grants |= FILE_EXEC; + if (mode & S_IWOTH) + grants |= FILE_WRITE; + if (mode & S_IROTH) + grants |= FILE_READ; + } + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + + /* an ACE for administrators */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (pos); +} + +#if POSIXACLS + +/* + * Build a full security descriptor from a Posix ACL + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int k; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 3*(8 + gsidsz) /* three possible ACE for group and mask */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (pxdesc->mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + /* account for non-owning users and groups */ + for (k=0; kacccnt; k++) { + if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER) + || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP)) + newattrsz += 3*40; /* fixme : maximum size */ + } + /* account for default ACE's */ + newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls_posix(mapping,newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + pxdesc, isdir, usid, gsid); + if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz)) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* ACL failure (errno set) or overflow */ + free(newattr); + newattr = (char*)NULL; + if (aclsz) { + /* hope error was detected before overflowing */ + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } + } else + errno = ENOMEM; + return (newattr); +} + +#endif /* POSIXACLS */ + +/* + * Build a full security descriptor + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 2*(8 + gsidsz) /* two possible ACE for group */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls(newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + mode, isdir, usid, gsid); + if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* hope error was detected before overflowing */ + free(newattr); + newattr = (char*)NULL; + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } else + errno = ENOMEM; + return (newattr); +} + +/* + * Create a mode_t permission set + * from owner, group and world grants as represented in ACEs + */ + +static int merge_permissions(BOOL isdir, + le32 owner, le32 group, le32 world, le32 special) + +{ + int perm; + + perm = 0; + /* build owner permission */ + if (owner) { + if (isdir) { + /* exec if any of list, traverse */ + if (owner & DIR_GEXEC) + perm |= S_IXUSR; + /* write if any of addfile, adddir, delchild */ + if (owner & DIR_GWRITE) + perm |= S_IWUSR; + /* read if any of list */ + if (owner & DIR_GREAD) + perm |= S_IRUSR; + } else { + /* exec if execute or generic execute */ + if (owner & FILE_GEXEC) + perm |= S_IXUSR; + /* write if any of writedata or generic write */ + if (owner & FILE_GWRITE) + perm |= S_IWUSR; + /* read if any of readdata or generic read */ + if (owner & FILE_GREAD) + perm |= S_IRUSR; + } + } + /* build group permission */ + if (group) { + if (isdir) { + /* exec if any of list, traverse */ + if (group & DIR_GEXEC) + perm |= S_IXGRP; + /* write if any of addfile, adddir, delchild */ + if (group & DIR_GWRITE) + perm |= S_IWGRP; + /* read if any of list */ + if (group & DIR_GREAD) + perm |= S_IRGRP; + } else { + /* exec if execute */ + if (group & FILE_GEXEC) + perm |= S_IXGRP; + /* write if any of writedata, appenddata */ + if (group & FILE_GWRITE) + perm |= S_IWGRP; + /* read if any of readdata */ + if (group & FILE_GREAD) + perm |= S_IRGRP; + } + } + /* build world permission */ + if (world) { + if (isdir) { + /* exec if any of list, traverse */ + if (world & DIR_GEXEC) + perm |= S_IXOTH; + /* write if any of addfile, adddir, delchild */ + if (world & DIR_GWRITE) + perm |= S_IWOTH; + /* read if any of list */ + if (world & DIR_GREAD) + perm |= S_IROTH; + } else { + /* exec if execute */ + if (world & FILE_GEXEC) + perm |= S_IXOTH; + /* write if any of writedata, appenddata */ + if (world & FILE_GWRITE) + perm |= S_IWOTH; + /* read if any of readdata */ + if (world & FILE_GREAD) + perm |= S_IROTH; + } + } + /* build special permission flags */ + if (special) { + if (special & FILE_APPEND_DATA) + perm |= S_ISUID; + if (special & FILE_WRITE_DATA) + perm |= S_ISGID; + if (special & FILE_READ_DATA) + perm |= S_ISVTX; + } + return (perm); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (standard case : different owner, group and administrator) + */ + +static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc, + BOOL groupowns, int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t grantgrps; + mode_t grantwrld; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + tagsset = 0; + /* + * Determine what is granted to some group or world + * Also get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + pxace = posix_desc->acl.ace; + grantgrps = 0; + grantwrld = 0; + denywrld = 0; + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec unless for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } else { + switch (pxace[j].tag) { + case POSIX_ACL_GROUP_OBJ : + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_GROUP : + if (pxace[j].id) + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_OTHER : + grantwrld = pxace[j].perms; + break; + default : + break; + } + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted and what is denied. + * It is important the ACEs have been sorted + */ + j = start; + k = target; + while (j < (start + count)) { + tag = pxace[j].tag; + id = pxace[j].id; + if (pxace[j].perms & POSIX_PERM_DENIAL) { + deny = pxace[j].perms | denywrld; + allow = 0; + } else { + deny = denywrld; + allow = pxace[j].perms; + } + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (pxace[j].perms & POSIX_PERM_DENIAL) + deny |= pxace[j].perms; + else + allow |= pxace[j].perms; + j++; + } + /* + * Build the permissions equivalent to grants and denials + */ + if (groupowns) { + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~deny; + } else + switch (tag) { + case POSIX_ACL_USER_OBJ : + perms = (allow | grantgrps | grantwrld) & ~deny; + break; + case POSIX_ACL_USER : + if (id) + perms = (allow | grantgrps | grantwrld) + & ~deny; + else + perms = allow; + break; + case POSIX_ACL_GROUP_OBJ : + perms = (allow | grantwrld) & ~deny; + break; + case POSIX_ACL_GROUP : + if (id) + perms = (allow | grantwrld) & ~deny; + else + perms = allow; + break; + case POSIX_ACL_MASK : + perms = ~deny; + break; + default : + perms = allow & ~deny; + break; + } + /* + * Store into a Posix ACE + */ + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID)); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (standard case : different owner, group and administrator) + */ + +static int build_std_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL noown; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + noown = TRUE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if (ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) { + noown = FALSE; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && !(pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowgrp |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + /* + * No indication about owner's rights : grant basic rights + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + if (noown) + allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE); + /* + * Add to owner rights granted to group or world + * unless denied personaly, and add to group rights + * granted to world unless denied specifically + */ + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner and group are the same, + * and not administrator) + */ + +static int build_owngrp_permissions(const char *securattr, + const SID *usid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + le32 special; + BOOL grppresent; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + grppresent = FALSE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + } else + if (ntfs_same_sid(usid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + grppresent = TRUE; + } + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + if (!grppresent) + allowgrp = allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (special case : owner or/and group is administrator) + */ + +static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, + int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + int acccnt; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + pxace = posix_desc->acl.ace; + acccnt = posix_desc->acccnt; + tagsset = 0; + denywrld = 0; + /* + * Get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec not for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted (denials are ignored) + * It is important the ACEs have been sorted + */ + j = start; + k = target; + deny = 0; + while (j < (start + count)) { + allow = 0; + tag = pxace[j].tag; + id = pxace[j].id; + if (tag == POSIX_ACL_MASK) { + deny = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == POSIX_ACL_MASK)) + j++; + } else { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow |= pxace[j].perms; + j++; + } + } + + /* + * Store the grants into a Posix ACE + */ + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~denywrld; + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= perms & (S_ISVTX | S_ISUID | S_ISGID); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner or/and group is administrator) + */ + + +static int build_ownadmin_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL firstapply; + int isforeign; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + firstapply = TRUE; + isforeign = 3; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE) + && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (((pace->mask & WRITE_OWNER) && firstapply))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowown |= pace->mask; + isforeign &= ~1; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + isforeign &= ~2; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } + firstapply = FALSE; + } else + if (!(pace->flags & INHERIT_ONLY_ACE)) + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + offace += le16_to_cpu(pace->size); + } + if (isforeign) { + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + } + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if OWNERFROMACL + +/* + * Define the owner of a file as the first user allowed + * to change the owner, instead of the user defined as owner. + * + * This produces better approximations for files written by a + * Windows user in an inheritable directory owned by another user, + * as the access rights are inheritable but the ownership is not. + * + * An important case is the directories "Documents and Settings/user" + * which the users must have access to, though Windows considers them + * as owned by administrator. + */ + +const SID *ntfs_acl_owner(const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL found; + + found = FALSE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + nace = 0; + do { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->mask & WRITE_OWNER) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_is_user_sid(&pace->sid)) + found = TRUE; + offace += le16_to_cpu(pace->size); + } while (!found && (++nace < acecnt)); + } + if (found) + usid = &pace->sid; + else + usid = (const SID*)&securattr[le32_to_cpu(phead->owner)]; + return (usid); +} + +#else + +/* + * Special case for files owned by administrator with full + * access granted to a mapped user : consider this user as the tenant + * of the file. + * + * This situation cannot be represented with Linux concepts and can + * only be found for files or directories created by Windows. + * Typical situation : directory "Documents and Settings/user" which + * is on the path to user's files and must be given access to user + * only. + * + * Check file is owned by administrator and no user has rights before + * calling. + * Returns the uid of tenant or zero if none + */ + + +static uid_t find_tenant(struct MAPPING *const mapping[], + const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + uid_t tid; + uid_t xid; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + tid = 0; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->type == ACCESS_ALLOWED_ACE_TYPE) + && (pace->mask & DIR_WRITE)) { + xid = NTFS_FIND_USER(mapping[MAPUSERS], &pace->sid); + if (xid) tid = xid; + } + offace += le16_to_cpu(pace->size); + } + return (tid); +} + +#endif /* OWNERFROMACL */ + +#if POSIXACLS + +/* + * Build Posix permissions from an ACL + * returns a pointer to the requested permissions + * or a null pointer (with errno set) if there is a problem + * + * If the NTFS ACL was created according to our rules, the retrieved + * Posix ACL should be the exact ACL which was set. However if + * the NTFS ACL was built by a different tool, the result could + * be a a poor approximation of what was expected + */ + +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING *const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + struct POSIX_ACE *pxace; + struct { + uid_t prevuid; + gid_t prevgid; + int groupmasks; + s16 tagsset; + BOOL gotowner; + BOOL gotownermask; + BOOL gotgroup; + mode_t permswrld; + } ctx[2], *pctx; + int offdacl; + int offace; + int alloccnt; + int acecnt; + uid_t uid; + gid_t gid; + int i,j; + int k,l; + BOOL ignore; + BOOL adminowns; + BOOL groupowns; + BOOL firstinh; + BOOL genericinh; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + adminowns = FALSE; + groupowns = ntfs_same_sid(gsid,usid); + firstinh = FALSE; + genericinh = FALSE; + /* + * Build a raw posix security descriptor + * by just translating permissions and ids + * Add 2 to the count of ACE to be able to insert + * a group ACE later in access and default ACLs + * and add 2 more to be able to insert ACEs for owner + * and 2 more for other + */ + alloccnt = acecnt + 6; + pxdesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + + alloccnt*sizeof(struct POSIX_ACE)); + k = 0; + l = alloccnt; + for (i=0; i<2; i++) { + pctx = &ctx[i]; + pctx->permswrld = 0; + pctx->prevuid = -1; + pctx->prevgid = -1; + pctx->groupmasks = 0; + pctx->tagsset = 0; + pctx->gotowner = FALSE; + pctx->gotgroup = FALSE; + pctx->gotownermask = FALSE; + } + for (j=0; jflags & INHERIT_ONLY_ACE) { + pxace = &pxdesc->acl.ace[l - 1]; + pctx = &ctx[1]; + } else { + pxace = &pxdesc->acl.ace[k]; + pctx = &ctx[0]; + } + ignore = FALSE; + /* + * grants for root as a designated user or group + */ + if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&pace->sid, adminsid)) { + pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER); + pxace->id = 0; + if ((pace->mask & (GENERIC_ALL | WRITE_OWNER)) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = genericinh = TRUE; + } else + if (ntfs_same_sid(usid, &pace->sid)) { + pxace->id = -1; + /* + * Owner has no write-owner right : + * a group was defined same as owner + * or admin was owner or group : + * denials are meant to owner + * and grants are meant to group + */ + if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) { + if (ntfs_same_sid(gsid,usid)) { + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + } else { + if (ntfs_same_sid(&pace->sid,usid)) + groupowns = TRUE; + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->tag = POSIX_ACL_GROUP; + pxace->id = gid; + pctx->prevgid = gid; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + pxace->tag = POSIX_ACL_USER; + pxace->id = uid; + } else + ignore = TRUE; + } + } + } else { + /* + * when group owns, late denials for owner + * mean group mask + */ + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pctx->gotownermask = TRUE; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotowner = TRUE; + if (pctx->gotownermask && !pctx->gotowner) { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } else + pxace->tag = POSIX_ACL_USER_OBJ; + /* system ignored, and admin */ + /* ignored at first position */ + if (pace->flags & INHERIT_ONLY_ACE) { + if ((firstinh && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (!firstinh) { + firstinh = TRUE; + } + } else { + if ((adminowns && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (ntfs_same_sid(usid,adminsid)) + adminowns = TRUE; + } + } + } + } else if (ntfs_same_sid(gsid, &pace->sid)) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pctx->gotgroup || (pctx->groupmasks > 1)) { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->id = gid; + pxace->tag = POSIX_ACL_GROUP; + pctx->prevgid = gid; + } else + ignore = TRUE; + } else { + pxace->id = -1; + pxace->tag = POSIX_ACL_GROUP_OBJ; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotgroup = TRUE; + } + + if (ntfs_same_sid(gsid,adminsid) + || ntfs_same_sid(gsid,systemsid)) { + if (pace->mask & (WRITE_OWNER | GENERIC_ALL)) + ignore = TRUE; + if (ntfs_same_sid(gsid,adminsid)) + adminowns = TRUE; + else + genericinh = ignore; + } + } + } else if (is_world_sid((const SID*)&pace->sid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_OTHER; + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = TRUE; + } else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_SPECIAL; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevuid != uid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_MASK; + } else { + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } + pctx->prevuid = uid; + } else { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevgid != gid)) { + pxace->tag = POSIX_ACL_MASK; + pctx->groupmasks++; + } else { + pxace->tag = POSIX_ACL_GROUP; + } + pxace->id = gid; + pctx->prevgid = gid; + } else { + /* + * do not grant rights to unknown + * people and do not define root as a + * designated user or group + */ + ignore = TRUE; + } + } + } + if (!ignore) { + pxace->perms = 0; + /* specific decoding for vtx/uid/gid */ + if (pxace->tag == POSIX_ACL_SPECIAL) { + if (pace->mask & FILE_APPEND_DATA) + pxace->perms |= S_ISUID; + if (pace->mask & FILE_WRITE_DATA) + pxace->perms |= S_ISGID; + if (pace->mask & FILE_READ_DATA) + pxace->perms |= S_ISVTX; + } else + if (isdir) { + if (pace->mask & DIR_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & DIR_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & DIR_GREAD) + pxace->perms |= POSIX_PERM_R; + if ((pace->mask & GENERIC_ALL) + && (pace->flags & INHERIT_ONLY_ACE)) + pxace->perms |= POSIX_PERM_X + | POSIX_PERM_W + | POSIX_PERM_R; + } else { + if (pace->mask & FILE_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & FILE_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & FILE_GREAD) + pxace->perms |= POSIX_PERM_R; + } + + if (pace->type != ACCESS_ALLOWED_ACE_TYPE) + pxace->perms |= POSIX_PERM_DENIAL; + else + if (pxace->tag == POSIX_ACL_OTHER) + pctx->permswrld = pxace->perms; + pctx->tagsset |= pxace->tag; + if (pace->flags & INHERIT_ONLY_ACE) { + l--; + } else { + k++; + } + } + offace += le16_to_cpu(pace->size); + } + /* + * Create world perms if none (both lists) + */ + for (i=0; i<2; i++) + if ((genericinh || !i) + && !(ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_OTHER; + pxace->id = -1; + pxace->perms = 0; + ctx[i].tagsset |= POSIX_ACL_OTHER; + ctx[i].permswrld = 0; + } + /* + * Set basic owner perms if none (both lists) + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + for (i=0; i<2; i++) + if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ) + && (ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_USER_OBJ; + pxace->id = -1; + pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X; + ctx[i].tagsset |= POSIX_ACL_USER_OBJ; + } + /* + * Duplicate world perms as group_obj perms if none + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & POSIX_ACL_OTHER) + && !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + pxace->perms = ctx[i].permswrld; + ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ; + } + /* + * Also duplicate world perms as group perms if they + * were converted to mask and not followed by a group entry + */ + if (ctx[0].groupmasks) { + for (j=k-2; j>=0; j--) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[k]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[0].permswrld; + ctx[0].tagsset |= POSIX_ACL_GROUP; + k++; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + if (ctx[1].groupmasks) { + for (j=l; j<(alloccnt-1); j++) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[l - 1]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[1].permswrld; + ctx[1].tagsset |= POSIX_ACL_GROUP; + l--; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + + /* + * Insert default mask if none present and + * there are designated users or groups + * (the space for it has not beed used) + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP)) + && !(ctx[i].tagsset & POSIX_ACL_MASK)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + pxace->perms = POSIX_PERM_DENIAL; + ctx[i].tagsset |= POSIX_ACL_MASK; + } + + if (k > l) { + ntfs_log_error("Posix descriptor is longer than expected\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } else { + pxdesc->acccnt = k; + pxdesc->defcnt = alloccnt - l; + pxdesc->firstdef = l; + pxdesc->tagsset = ctx[0].tagsset; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + ntfs_sort_posix(pxdesc); + if (adminowns) { + k = norm_ownadmin_permissions_posix(pxdesc, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_ownadmin_permissions_posix(pxdesc, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } else { + k = norm_std_permissions_posix(pxdesc,groupowns, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_std_permissions_posix(pxdesc,groupowns, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } + } + if (pxdesc && !ntfs_valid_posix(pxdesc)) { + ntfs_log_error("Invalid Posix descriptor built\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } + return (pxdesc); +} + +#endif /* POSIXACLS */ + +/* + * Build unix-style (mode_t) permissions from an ACL + * returns the requested permissions + * or a negative result (with errno set) if there is a problem + */ + +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + int perm; + BOOL adminowns; + BOOL groupowns; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + adminowns = ntfs_same_sid(usid,adminsid) + || ntfs_same_sid(gsid,adminsid); + groupowns = !adminowns && ntfs_same_sid(gsid,usid); + if (adminowns) + perm = build_ownadmin_permissions(securattr, usid, gsid, isdir); + else + if (groupowns) + perm = build_owngrp_permissions(securattr, usid, isdir); + else + perm = build_std_permissions(securattr, usid, gsid, isdir); + return (perm); +} + +/* + * The following must be in some library... + */ + +static unsigned long atoul(const char *p) +{ /* must be somewhere ! */ + unsigned long v; + + v = 0; + while ((*p >= '0') && (*p <= '9')) + v = v * 10 + (*p++) - '0'; + return (v); +} + +/* + * Build an internal representation of a SID + * Returns a copy in allocated memory if it succeeds + * The SID is checked to be a valid user one. + */ + +static SID *encodesid(const char *sidstr) +{ + SID *sid; + int cnt; + BIGSID bigsid; + SID *bsid; + u32 auth; + const char *p; + + sid = (SID*) NULL; + if (!strncmp(sidstr, "S-1-", 4)) { + bsid = (SID*)&bigsid; + bsid->revision = SID_REVISION; + p = &sidstr[4]; + auth = atoul(p); + bsid->identifier_authority.high_part = const_cpu_to_be16(0); + bsid->identifier_authority.low_part = cpu_to_be32(auth); + cnt = 0; + p = strchr(p, '-'); + while (p && (cnt < 8)) { + p++; + auth = atoul(p); + bsid->sub_authority[cnt] = cpu_to_le32(auth); + p = strchr(p, '-'); + cnt++; + } + bsid->sub_authority_count = cnt; + if ((cnt > 0) && ntfs_valid_sid(bsid) && ntfs_is_user_sid(bsid)) { + sid = (SID*) ntfs_malloc(4 * cnt + 8); + if (sid) + memcpy(sid, bsid, 4 * cnt + 8); + } + } + return (sid); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + */ + +static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *p; + char *q; + char *pu; + char *pg; + int gotend; + struct MAPLIST *item; + + src = *psrc; + dst = 0; + /* allocate and get a full line */ + item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST)); + if (item) { + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + if (dst < LINESZ) + item->maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + item->maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((item->maptext[0] == '#') || !gotend)); + if (gotend) { + pu = pg = (char*)NULL; + /* decompose into uid, gid and sid */ + p = item->maptext; + item->uidstr = item->maptext; + item->gidstr = strchr(item->uidstr, ':'); + if (item->gidstr) { + pu = item->gidstr++; + item->sidstr = strchr(item->gidstr, ':'); + if (item->sidstr) { + pg = item->sidstr++; + q = strchr(item->sidstr, ':'); + if (q) *q = 0; + } + } + if (pu && pg) + *pu = *pg = '\0'; + else { + ntfs_log_early_error("Bad mapping item \"%s\"\n", + item->maptext); + free(item); + item = (struct MAPLIST*)NULL; + } + } else { + free(item); /* free unused item */ + item = (struct MAPLIST*)NULL; + } + } + *psrc = src; + return (item); +} + +/* + * Read user mapping file and split into their attribute. + * Parameters are kept as text in a chained list until logins + * are converted to uid. + * Returns the head of list, if any + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid) +{ + char buf[BUFSZ]; + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPLIST *lastitem; + int src; + off_t offs; + s64 size; + + firstitem = (struct MAPLIST*)NULL; + lastitem = (struct MAPLIST*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + item->next = (struct MAPLIST*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } while (item); + } + return (firstitem); +} + +/* + * Free memory used to store the user mapping + * The only purpose is to facilitate the detection of memory leaks + */ + +void ntfs_free_mapping(struct MAPPING *mapping[]) +{ + struct MAPPING *user; + struct MAPPING *group; + + /* free user mappings */ + while (mapping[MAPUSERS]) { + user = mapping[MAPUSERS]; + /* do not free SIDs used for group mappings */ + group = mapping[MAPGROUPS]; + while (group && (group->sid != user->sid)) + group = group->next; + if (!group) + free(user->sid); + /* free group list if any */ + if (user->grcnt) + free(user->groups); + /* unchain item and free */ + mapping[MAPUSERS] = user->next; + free(user); + } + /* free group mappings */ + while (mapping[MAPGROUPS]) { + group = mapping[MAPGROUPS]; + free(group->sid); + /* unchain item and free */ + mapping[MAPGROUPS] = group->next; + free(group); + } +} + + +/* + * Build the user mapping list + * user identification may be given in symbolic or numeric format + * + * ! Note ! : does getpwnam() read /etc/passwd or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct passwd *pwd; + SID *sid; + int uid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (item = firstitem; item; item = item->next) { + if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9')) + uid = atoi(item->uidstr); + else { + uid = 0; + if (item->uidstr[0]) { + pwd = getpwnam(item->uidstr); + if (pwd) + uid = pwd->pw_uid; + else + ntfs_log_early_error("Invalid user \"%s\"\n", + item->uidstr); + } + } + /* + * Records with no uid and no gid are inserted + * to define the implicit mapping pattern + */ + if (uid + || (!item->uidstr[0] && !item->gidstr[0])) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + ntfs_log_error("Bad implicit SID pattern %s\n", + item->sidstr); + sid = (SID*)NULL; + } + if (sid) { + mapping = + (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = uid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + return (firstmapping); +} + +/* + * Build the group mapping list + * group identification may be given in symbolic or numeric format + * + * gid not associated to a uid are processed first in order + * to favour real groups + * + * ! Note ! : does getgrnam() read /etc/group or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct group *grp; + BOOL secondstep; + BOOL ok; + int step; + SID *sid; + int gid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (step=1; step<=2; step++) { + for (item = firstitem; item; item = item->next) { + secondstep = (item->uidstr[0] != '\0') + || !item->gidstr[0]; + ok = (step == 1 ? !secondstep : secondstep); + if ((item->gidstr[0] >= '0') + && (item->gidstr[0] <= '9')) + gid = atoi(item->gidstr); + else { + gid = 0; + if (item->gidstr[0]) { + grp = getgrnam(item->gidstr); + if (grp) + gid = grp->gr_gid; + else + ntfs_log_early_error("Invalid group \"%s\"\n", + item->gidstr); + } + } + /* + * Records with no uid and no gid are inserted in the + * second step to define the implicit mapping pattern + */ + if (ok + && (gid + || (!item->uidstr[0] && !item->gidstr[0]))) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + /* error already logged */ + sid = (SID*)NULL; + } + if (sid) { + mapping = (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = gid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + } + return (firstmapping); +} diff --git a/lib/libntfs/orig/source/acls.h b/lib/libntfs/orig/source/acls.h new file mode 100644 index 0000000..8a83d32 --- /dev/null +++ b/lib/libntfs/orig/source/acls.h @@ -0,0 +1,199 @@ +/* + * + * Copyright (c) 2007-2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ACLS_H +#define ACLS_H + +/* + * JPA configuration modes for security.c / acls.c + * should be moved to some config file + */ + +#define BUFSZ 1024 /* buffer size to read mapping file */ +#define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ +#define LINESZ 120 /* maximum useful size of a mapping line */ +#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */ +#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */ + +/* + * JPA The following must be in some library... + * but did not found out where + */ + +#define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8)) +#define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \ + | ((x & 0xff00) << 8) | ((x & 255) << 24)) + +#define cpu_to_be16(x) endian_rev16(cpu_to_le16(x)) +#define cpu_to_be32(x) endian_rev32(cpu_to_le32(x)) + +/* + * Macro definitions needed to share code with secaudit + */ + +#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) +#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) +#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) +#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) + + +/* + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present + */ + + /* flags which are set to mean exec, write or read */ + +#define FILE_READ (FILE_READ_DATA) +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_EXEC (FILE_EXECUTE) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define DIR_EXEC (FILE_TRAVERSE) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ + +#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ) +#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE) +#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE) +#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ) +#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE) +#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE) + + /* standard owner (and administrator) rights */ + +#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) + + /* standard world rights */ + +#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \ + | SYNCHRONIZE) + + /* inheritance flags for files and directories */ + +#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE +#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) + +/* + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. + */ + +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ + +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + +/* + * Struct to hold the input mapping file + * (private to this module) + */ + +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; +}; + +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); + +/* + * Constants defined in acls.c + */ + +extern const SID *adminsid; +extern const SID *worldsid; + +/* + * Functions defined in acls.c + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_valid_sid(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); + +BOOL ntfs_is_user_sid(const SID *usid); + + +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); + +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); + +#if POSIXACLS + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t umask, BOOL isdir); +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt); +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); + +#endif /* POSIXACLS */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); + +#endif /* ACLS_H */ + diff --git a/lib/libntfs/orig/source/attrib.c b/lib/libntfs/orig/source/attrib.c new file mode 100644 index 0000000..e6f614f --- /dev/null +++ b/lib/libntfs/orig/source/attrib.c @@ -0,0 +1,6773 @@ +/** + * attrib.c - Attribute handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2010 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2011 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#include "param.h" +#include "compat.h" +#include "attrib.h" +#include "attrlist.h" +#include "device.h" +#include "mft.h" +#include "debug.h" +#include "mst.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "dir.h" +#include "compress.h" +#include "bitmap.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; +ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('S'), + const_cpu_to_le16('\0') }; + +ntfschar TXF_DATA[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('T'), + const_cpu_to_le16('X'), + const_cpu_to_le16('F'), + const_cpu_to_le16('_'), + const_cpu_to_le16('D'), + const_cpu_to_le16('A'), + const_cpu_to_le16('T'), + const_cpu_to_le16('A'), + const_cpu_to_le16('\0') }; + +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; +} + +static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags |= flag; + else + ntfs_log_trace("Denied setting flag %d for not unnamed data " + "attribute\n", flag); +} + +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags &= ~flag; +} + +#define GenNAttrIno(func_name, flag) \ +int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ +void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ +void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) + +/** + * ntfs_get_attribute_value_length - Find the length of an attribute + * @a: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) +{ + if (!a) { + errno = EINVAL; + return 0; + } + errno = 0; + if (a->non_resident) + return sle64_to_cpu(a->data_size); + + return (s64)le32_to_cpu(a->value_length); +} + +/** + * ntfs_get_attribute_value - Get a copy of an attribute + * @vol: + * @a: + * @b: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b) +{ + runlist *rl; + s64 total, r; + int i; + + /* Sanity checks. */ + if (!vol || !a || !b) { + errno = EINVAL; + return 0; + } + /* Complex attribute? */ + /* + * Ignore the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (a->type != AT_ATTRIBUTE_LIST && a->flags) { + ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " + "this yet.\n", le16_to_cpu(a->flags)); + errno = EOPNOTSUPP; + return 0; + } + if (!a->non_resident) { + /* Attribute is resident. */ + + /* Sanity check. */ + if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) + > le32_to_cpu(a->length)) { + return 0; + } + + memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); + errno = 0; + return (s64)le32_to_cpu(a->value_length); + } + + /* Attribute is not resident. */ + + /* If no data, return 0. */ + if (!(a->data_size)) { + errno = 0; + return 0; + } + /* + * FIXME: What about attribute lists?!? (AIA) + */ + /* Decompress the mapping pairs array into a runlist. */ + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (!rl) { + errno = EINVAL; + return 0; + } + /* + * FIXED: We were overflowing here in a nasty fashion when we + * reach the last cluster in the runlist as the buffer will + * only be big enough to hold data_size bytes while we are + * reading in allocated_size bytes which is usually larger + * than data_size, since the actual data is unlikely to have a + * size equal to a multiple of the cluster size! + * FIXED2: We were also overflowing here in the same fashion + * when the data_size was more than one run smaller than the + * allocated size which happens with Windows XP sometimes. + */ + /* Now load all clusters in the runlist into b. */ + for (i = 0, total = 0; rl[i].length; i++) { + if (total + (rl[i].length << vol->cluster_size_bits) >= + sle64_to_cpu(a->data_size)) { + unsigned char *intbuf = NULL; + /* + * We have reached the last run so we were going to + * overflow when executing the ntfs_pread() which is + * BAAAAAAAD! + * Temporary fix: + * Allocate a new buffer with size: + * rl[i].length << vol->cluster_size_bits, do the + * read into our buffer, then memcpy the correct + * amount of data into the caller supplied buffer, + * free our buffer, and continue. + * We have reached the end of data size so we were + * going to overflow in the same fashion. + * Temporary fix: same as above. + */ + intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits); + if (!intbuf) { + free(rl); + return 0; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we + * just memset the user buffer to 0 for the length of + * the run, which should be 16 (= compression unit + * size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily + * size of 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << + vol->cluster_size_bits, rl[i].length << + vol->cluster_size_bits, intbuf); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << + vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + free(intbuf); + return 0; + } + memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - + total); + free(intbuf); + total = sle64_to_cpu(a->data_size); + break; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we just + * memset the user buffer to 0 for the length of the run, which + * should be 16 (= compression unit size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily size of + * 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, + rl[i].length << vol->cluster_size_bits, + b + total); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + return 0; + } + total += r; + } + free(rl); + return total; +} + +/* Already cleaned up code below, but still look for FIXME:... */ + +/** + * __ntfs_attr_init - primary initialization of an ntfs attribute structure + * @na: ntfs attribute to initialize + * @ni: ntfs inode with which to initialize the ntfs attribute + * @type: attribute type + * @name: attribute name in little endian Unicode or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. + */ +static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, + const ATTR_TYPES type, ntfschar *name, const u32 name_len) +{ + na->rl = NULL; + na->ni = ni; + na->type = type; + na->name = name; + if (name) + na->name_len = name_len; + else + na->name_len = 0; +} + +/** + * ntfs_attr_init - initialize an ntfs_attr with data sizes and status + * @na: + * @non_resident: + * @compressed: + * @encrypted: + * @sparse: + * @allocated_size: + * @data_size: + * @initialized_size: + * @compressed_size: + * @compression_unit: + * + * Final initialization for an ntfs attribute. + */ +void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, + const BOOL encrypted, const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit) +{ + if (!NAttrInitialized(na)) { + na->data_flags = data_flags; + if (non_resident) + NAttrSetNonResident(na); + if (data_flags & ATTR_COMPRESSION_MASK) + NAttrSetCompressed(na); + if (encrypted) + NAttrSetEncrypted(na); + if (sparse) + NAttrSetSparse(na); + na->allocated_size = allocated_size; + na->data_size = data_size; + na->initialized_size = initialized_size; + if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) { + ntfs_volume *vol = na->ni->vol; + + na->compressed_size = compressed_size; + na->compression_block_clusters = 1 << compression_unit; + na->compression_block_size = 1 << (compression_unit + + vol->cluster_size_bits); + na->compression_block_size_bits = ffs( + na->compression_block_size) - 1; + } + NAttrSetInitialized(na); + } +} + +/** + * ntfs_attr_open - open an ntfs attribute for access + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Allocate a new ntfs attribute structure, initialize it with @ni, @type, + * @name, and @name_len, then return it. Return NULL on error with + * errno set to the error code. + * + * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you + * do not care whether the attribute is named or not set @name to NULL. In + * both those cases @name_len is not used at all. + */ +ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na = NULL; + ntfschar *newname = NULL; + ATTR_RECORD *a; + le16 cs; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + + if (!ni || !ni->vol || !ni->mrec) { + errno = EINVAL; + goto out; + } + na = ntfs_calloc(sizeof(ntfs_attr)); + if (!na) + goto out; + if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { + name = ntfs_ucsndup(name, name_len); + if (!name) + goto err_out; + newname = name; + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) + goto put_err_out; + + a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + newname = name; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + + /* + * Wipe the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (type == AT_ATTRIBUTE_LIST) + a->flags = 0; + + if ((type == AT_DATA) && !a->initialized_size) { + /* + * Define/redefine the compression state if stream is + * empty, based on the compression mark on parent + * directory (for unnamed data streams) or on current + * inode (for named data streams). The compression mark + * may change any time, the compression state can only + * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled + */ + a->flags &= ~ATTR_COMPRESSION_MASK; + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) + a->flags |= ATTR_IS_COMPRESSED; + } + + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + + if (na->type == AT_DATA && na->name == AT_UNNAMED && + ((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { + errno = EIO; + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); + goto put_err_out; + } + + if (a->non_resident) { + if ((a->flags & ATTR_COMPRESSION_MASK) + && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } + ntfs_attr_init(na, TRUE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, + sle64_to_cpu(a->allocated_size), + sle64_to_cpu(a->data_size), + sle64_to_cpu(a->initialized_size), + cs ? sle64_to_cpu(a->compressed_size) : 0, + cs ? a->compression_unit : 0); + } else { + s64 l = le32_to_cpu(a->value_length); + ntfs_attr_init(na, FALSE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, + cs ? (l + 7) & ~7 : 0, 0); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return na; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(newname); + free(na); + na = NULL; + goto out; +} + +/** + * ntfs_attr_close - free an ntfs attribute structure + * @na: ntfs attribute structure to free + * + * Release all memory associated with the ntfs attribute @na and then release + * @na itself. + */ +void ntfs_attr_close(ntfs_attr *na) +{ + if (!na) + return; + if (NAttrNonResident(na) && na->rl) + free(na->rl); + /* Don't release if using an internal constant. */ + if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30 + && na->name != STREAM_SDS) + free(na->name); + free(na); +} + +/** + * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute + * @na: ntfs attribute for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * + * Map the part of a runlist containing the @vcn of the ntfs attribute @na. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + ntfs_attr_search_ctx *ctx; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + vcn, NULL, 0, ctx)) { + runlist_element *rl; + + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, + na->rl); + if (rl) { + na->rl = rl; + ntfs_attr_put_search_ctx(ctx); + return 0; + } + } + + ntfs_attr_put_search_ctx(ctx); + return -1; +} + +#if PARTIAL_RUNLIST_UPDATING + +/* + * Map the runlist of an attribute from some point to the end + * + * Returns 0 if success, + * -1 if it failed (errno telling why) + */ + +static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + VCN last_vcn; + VCN highest_vcn; + VCN needed; + VCN existing_vcn; + runlist_element *rl; + ATTR_RECORD *a; + BOOL startseen; + ntfs_attr_search_ctx *ctx; + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + existing_vcn = (na->rl ? na->rl->vcn : -1); + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Get the last vcn in the attribute. */ + last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits; + + needed = vcn; + highest_vcn = 0; + startseen = FALSE; + do { + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + needed, NULL, 0, ctx)) { + + a = ctx->attr; + /* Decode and merge the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, a, + na->rl); + if (rl) { + na->rl = rl; + highest_vcn = le64_to_cpu(a->highest_vcn); + /* corruption detection */ + if (((highest_vcn + 1) < last_vcn) + && ((highest_vcn + 1) <= needed)) { + ntfs_log_error("Corrupt attribute list\n"); + rl = (runlist_element*)NULL; + } + needed = highest_vcn + 1; + if (!a->lowest_vcn) + startseen = TRUE; + /* reaching a previously allocated part ? */ + if ((existing_vcn >= 0) + && (needed >= existing_vcn)) { + needed = last_vcn; + } + } + } else + rl = (runlist_element*)NULL; + } while (rl && (needed < last_vcn)); + ntfs_attr_put_search_ctx(ctx); + /* mark fully mapped if we did so */ + if (rl && startseen) + NAttrSetFullyMapped(na); + return (rl ? 0 : -1); +} + +#endif + +/** + * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute + * @na: ntfs attribute for which to map the runlist + * + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling + * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this + * will map the runlist fragments from each of the extents thus giving access + * to the entirety of the disk allocation of an attribute. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_whole_runlist(ntfs_attr *na) +{ + VCN next_vcn, last_vcn, highest_vcn; + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol = na->ni->vol; + ATTR_RECORD *a; + int ret = -1; + + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); + + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto out; + + /* Map all attribute extents one by one. */ + next_vcn = last_vcn = highest_vcn = 0; + a = NULL; + while (1) { + runlist_element *rl; + + int not_mapped = 0; + if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) + not_mapped = 1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) + break; + + a = ctx->attr; + + if (not_mapped) { + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, + a, na->rl); + if (!rl) + goto err_out; + na->rl = rl; + } + + /* Are we in the first extent? */ + if (!next_vcn) { + if (a->lowest_vcn) { + errno = EIO; + ntfs_log_perror("First extent of inode %llu " + "attribute has non-zero lowest_vcn", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + /* Get the last vcn in the attribute. */ + last_vcn = sle64_to_cpu(a->allocated_size) >> + vol->cluster_size_bits; + } + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) { + errno = ENOENT; + break; + } + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + errno = EIO; + ntfs_log_perror("Inode %llu has corrupt attribute list", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + } + if (!a) { + ntfs_log_perror("Couldn't find attribute for runlist mapping"); + goto err_out; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + errno = EIO; + ntfs_log_perror("Failed to load full runlist: inode: %llu " + "highest_vcn: 0x%llx last_vcn: 0x%llx", + (unsigned long long)na->ni->mft_no, + (long long)highest_vcn, (long long)last_vcn); + goto err_out; + } + if (errno == ENOENT) { + NAttrSetFullyMapped(na); + ret = 0; + } +err_out: + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute + * @na: ntfs attribute whose runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @na->rl to map vcns to + * their corresponding lcns. + * + * If the @vcn is not mapped yet, attempt to map the attribute extent + * containing the @vcn and retry the vcn to lcn conversion. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ========================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. + */ +LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) +{ + LCN lcn; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) + return (LCN)LCN_EINVAL; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); +retry: + /* Convert vcn to lcn. If that fails map the runlist and retry once. */ + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0) + return lcn; + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If the attempt to map the runlist failed, or we are getting + * LCN_RL_NOT_MAPPED despite having mapped the attribute extent + * successfully, something is really badly wrong... + */ + if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) + return (LCN)LCN_EIO; + /* lcn contains the appropriate error code. */ + return lcn; +} + +/** + * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute + * @na: ntfs attribute whose runlist to search + * @vcn: vcn to find + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * @na and return the the address of the runlist element containing the @vcn. + * + * Note you need to distinguish between the lcn of the returned runlist + * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes + * on read and allocate clusters on write. You need to update the runlist, the + * attribute itself as well as write the modified mft record to disk. + * + * If there is an error return NULL with errno set to the error code. The + * following error codes are defined: + * EINVAL Input parameter error. + * ENOENT There is no such vcn in the runlist. + * ENOMEM Not enough memory. + * EIO I/O error or corrupt metadata. + */ +runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) +{ + runlist_element *rl; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)vcn); +retry: + rl = na->rl; + if (!rl) + goto map_rl; + if (vcn < rl[0].vcn) + goto map_rl; + while (rl->length) { + if (vcn < rl[1].vcn) { + if (rl->lcn >= (LCN)LCN_HOLE) + return rl; + break; + } + rl++; + } + switch (rl->lcn) { + case (LCN)LCN_RL_NOT_MAPPED: + goto map_rl; + case (LCN)LCN_ENOENT: + errno = ENOENT; + break; + case (LCN)LCN_EINVAL: + errno = EINVAL; + break; + default: + errno = EIO; + break; + } + return NULL; +map_rl: + /* The @vcn is in an unmapped region, map the runlist and retry. */ + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If we already retried or the mapping attempt failed something has + * gone badly wrong. EINVAL and ENOENT coming from a failed mapping + * attempt are equivalent to errors for us as they should not happen + * in our code paths. + */ + if (is_retry || errno == EINVAL || errno == ENOENT) + errno = EIO; + return NULL; +} + +/** + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2, max_read, max_init; + ntfs_volume *vol; + runlist_element *rl; + u16 efs_padding_length; + + /* Sanity checking arguments is done in ntfs_attr_pread(). */ + + if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) { + if ((na->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + return ntfs_compressed_attr_pread(na, pos, count, b); + else { + /* compression mode not supported */ + errno = EOPNOTSUPP; + return -1; + } + } + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option + */ + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + return -1; + } + + if (!count) + return 0; + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + return -1; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) + return 0; + count = max_read - pos; + } + /* If it is a resident attribute, get the value from the mft record. */ + if (!NAttrNonResident(na)) { + ntfs_attr_search_ctx *ctx; + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { +res_err_out: + ntfs_attr_put_search_ctx(ctx); + return -1; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto res_err_out; + } + memcpy(b, val + pos, count); + ntfs_attr_put_search_ctx(ctx); + return count; + } + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > max_init) { + if (pos >= max_init) { + memset(b, 0, count); + return count; + } + total2 = pos + count - max_init; + count -= total2; + memset((u8*)b + count, 0, total2); + } + /* + * for encrypted non-resident attributes with efs_raw set + * the last two bytes aren't read from disk but contain + * the number of padding bytes so original size can be + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + } + + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds read. + * However, we already truncated the read to the data_size, + * so getting this here is an error. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } + return -1; + } + /* + * Gather the requested data into the linear destination buffer. Note, + * a partial final vcn is taken care of by the @count capping of read + * length. + */ + ofs = pos - (rl->vcn << vol->cluster_size_bits); + for (; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #2", + __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + /* It is a hole, just zero the matching @b range. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update progress counters. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it into @dst. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); + br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (br > 0) { + total += br; + count -= br; + b = (u8*)b + br; + } + if (br == to_read) + continue; + /* If the syscall was interrupted, try again. */ + if (br == (s64)-1 && errno == EINTR) + goto retry; + if (total) + return total; + if (!br) + errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); + return -1; + } + /* Finally, return the number of bytes read. */ + return total + total2; +rl_err_out: + if (total) + return total; + errno = EIO; + return -1; +} + +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) +{ + char *buf; + s64 written, size, end = pos + count; + s64 ofsi; + const runlist_element *rli; + ntfs_volume *vol; + int ret = -1; + + ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, + (long long)count); + + if (!na || pos < 0 || count < 0) { + errno = EINVAL; + goto err_out; + } + + buf = ntfs_calloc(NTFS_BUF_SIZE); + if (!buf) + goto err_out; + + rli = na->rl; + ofsi = 0; + vol = na->ni->vol; + while (pos < end) { + while (rli->length && (ofsi + (rli->length << + vol->cluster_size_bits) <= pos)) { + ofsi += (rli->length << vol->cluster_size_bits); + rli++; + } + size = min(end - pos, NTFS_BUF_SIZE); + /* + * If the zeroed block is fully within a hole, + * we need not write anything, so advance as far + * as possible within the hole. + */ + if ((rli->lcn == (LCN)LCN_HOLE) + && (ofsi <= pos) + && (ofsi + (rli->length << vol->cluster_size_bits) + >= (pos + size))) { + size = min(end - pos, ofsi - pos + + (rli->length << vol->cluster_size_bits)); + pos += size; + } else { + written = ntfs_rl_pwrite(vol, rli, ofsi, pos, + size, buf); + if (written <= 0) { + ntfs_log_perror("Failed to zero space"); + goto err_free; + } + pos += written; + } + } + + ret = 0; +err_free: + free(buf); +err_out: + return ret; +} + +static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, + runlist_element **rl, VCN *update_from) +{ + s64 to_write; + s64 need; + ntfs_volume *vol = na->ni->vol; + int eo, ret = -1; + runlist *rlc; + LCN lcn_seek_from = -1; + VCN cur_vcn, from_vcn; + + to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); + + cur_vcn = (*rl)->vcn; + from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); + + ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " + "%lld\n", (long long)count, (long long)cur_vcn, + (long long)from_vcn, (long long)to_write, (long long)*ofs); + + /* Map the runlist to be able to update mapping pairs later. */ +#if PARTIAL_RUNLIST_UPDATING + if ((!na->rl + || !NAttrDataAppending(na))) { + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + } else { + /* make sure the previous non-hole is mapped */ + rlc = *rl; + rlc--; + if (((*rl)->lcn == LCN_HOLE) + && cur_vcn + && (rlc->vcn < 0)) { + if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1)) + goto err_out; + } + } +#else + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; +#endif + + /* Restore @*rl, it probably get lost during runlist mapping. */ + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + ntfs_log_error("Failed to find run after mapping runlist. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + + /* Search backwards to find the best lcn to start seek from. */ + rlc = *rl; + while (rlc->vcn) { + rlc--; + if (rlc->lcn >= 0) { + /* + * avoid fragmenting a compressed file + * Windows does not do that, and that may + * not be desirable for files which can + * be updated + */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + lcn_seek_from = rlc->lcn + rlc->length; + else + lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); + break; + } + } + if (lcn_seek_from == -1) { + /* Backwards search failed, search forwards. */ + rlc = *rl; + while (rlc->length) { + rlc++; + if (rlc->lcn >= 0) { + lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); + if (lcn_seek_from < -1) + lcn_seek_from = -1; + break; + } + } + } + + need = ((*ofs + to_write - 1) >> vol->cluster_size_bits) + + 1 + (*rl)->vcn - from_vcn; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && (need < na->compression_block_clusters)) { + /* + * for a compressed file, be sure to allocate the full + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. + */ + VCN alloc_vcn; + + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, + lcn_seek_from, DATA_ZONE); + } else + rlc = ntfs_cluster_alloc(vol, from_vcn, need, + lcn_seek_from, DATA_ZONE); + if (!rlc) + goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; + + *rl = ntfs_runlists_merge(na->rl, rlc); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } + if (!*rl) { + eo = errno; + ntfs_log_perror("Failed to merge runlists"); + if (ntfs_cluster_free_from_rl(vol, rlc)) { + ntfs_log_perror("Failed to free hot clusters. " + "Please run chkdsk /f"); + } + errno = eo; + goto err_out; + } + na->unused_runs = 2; + na->rl = *rl; + if ((*update_from == -1) || (from_vcn < *update_from)) + *update_from = from_vcn; + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + /* + * It's definitely a BUG, if we failed to find @cur_vcn, because + * we missed it during instantiating of the hole. + */ + ntfs_log_error("Failed to find run after hole instantiation. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + /* If leaved part of the hole go to the next run. */ + if ((*rl)->lcn < 0) + (*rl)++; + /* Now LCN shoudn't be less than 0. */ + if ((*rl)->lcn < 0) { + ntfs_log_error("BUG! LCN is lesser than 0. " + "Please report to the %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + if (*ofs) { + /* Clear non-sparse region from @cur_vcn to @*ofs. */ + if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits, + *ofs)) + goto err_out; + } + if ((*rl)->vcn < cur_vcn) { + /* + * Clusters that replaced hole are merged with + * previous run, so we need to update offset. + */ + *ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits; + } + if ((*rl)->vcn > cur_vcn) { + /* + * We left part of the hole, so we need to update offset + */ + *ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits; + } + + ret = 0; +err_out: + return ret; +} + +static int stuff_hole(ntfs_attr *na, const s64 pos); + +/* + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block + * + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error + */ + +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) +{ + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; + + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ +// impossible ? + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; + + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); +} + +/* + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block + * + * Must always leave two unused entries in the runlist + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error + */ + +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) +{ + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; + + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); + + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; +#if PARTIAL_RUNLIST_UPDATING + VCN prevblock; +#endif + /* + * Map the runlist, unless it has not been created. + * If appending data, a partial mapping from the + * end of previous block will do. + */ + irl = *prl - na->rl; +#if PARTIAL_RUNLIST_UPDATING + prevblock = pos >> cluster_size_bits; + if (prevblock) + prevblock--; + if (!NAttrBeingNonResident(na) + && (NAttrDataAppending(na) + ? ntfs_attr_map_partial_runlist(na,prevblock) + : ntfs_attr_map_whole_runlist(na))) { +#else + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { +#endif + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; + + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; + + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); +} + +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes); + +/** + * ntfs_attr_pwrite - positioned write to an ntfs attribute + * @na: ntfs attribute to write to + * @pos: position in the attribute to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to ntfs attribute + * @na at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also return + * 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) +{ + s64 written, to_write, ofs, old_initialized_size, old_data_size; + s64 total = 0; + VCN update_from = -1; + ntfs_volume *vol; + s64 fullcount; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + s64 hole_end; + int eo; + int compressed_part; + struct { + unsigned int undo_initialized_size : 1; + unsigned int undo_data_size : 1; + } need_to = { 0, 0 }; + BOOL wasnonresident = FALSE; + BOOL compressed; + BOOL sparse; + BOOL updatemap; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + na->unused_runs = 0; /* prepare overflow checks */ + /* + * Encrypted attributes are only supported in raw mode. We return + * access denied, which is what Windows NT4 does, too. + * Moreover a file cannot be both encrypted and compressed. + */ + if ((na->data_flags & ATTR_IS_ENCRYPTED) + && (compressed || !vol->efs_raw)) { + errno = EACCES; + goto errno_set; + } + /* + * Fill the gap, when writing beyond the end of a compressed + * file. This will make recursive calls + */ + if (compressed + && (na->type == AT_DATA) + && (pos > na->initialized_size) + && stuff_hole(na,pos)) + goto errno_set; + /* If this is a compressed attribute it needs special treatment. */ + wasnonresident = NAttrNonResident(na) != 0; + /* + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. + */ + if (compressed + && ((na->type != AT_DATA) + || ((na->data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED))) { + errno = EOPNOTSUPP; + goto errno_set; + } + + if (!count) + goto out; + /* for a compressed file, get prepared to reserve a full block */ + fullcount = count; + /* If the write reaches beyond the end, extend the attribute. */ + old_data_size = na->data_size; + /* identify whether this is appending to a non resident data attribute */ + if ((na->type == AT_DATA) && (pos >= old_data_size) + && NAttrNonResident(na)) + NAttrSetDataAppending(na); + if (pos + count > na->data_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending data, the attribute is first extended + * before being filled with data. This may cause the + * attribute to be made temporarily sparse, which + * implies reformating the inode and reorganizing the + * full runlist. To avoid unnecessary reorganization, + * we avoid sparse testing until the data is filled in. + */ + if (ntfs_attr_truncate_i(na, pos + count, + (NAttrDataAppending(na) ? + HOLES_DELAY : HOLES_OK))) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#else + if (ntfs_attr_truncate(na, pos + count)) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#endif + /* resizing may change the compression mode */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + need_to.undo_data_size = 1; + } + sparse = (na->data_flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); + /* + * For compressed data, a single full block was allocated + * to deal with compression, possibly in a previous call. + * We are not able to process several blocks because + * some clusters are freed after compression and + * new allocations have to be done before proceeding, + * so truncate the requested count if needed (big buffers). + */ + if (compressed) { + fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; + if (count > fullcount) + count = fullcount; + } + old_initialized_size = na->initialized_size; + /* If it is a resident attribute, write the data to the mft record. */ + if (!NAttrNonResident(na)) { + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: lookup failed", __FUNCTION__); + goto err_out; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto err_out; + } + memcpy(val + pos, b, count); + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * NOTE: We are in a bad state at this moment. We have + * dirtied the mft record but we failed to commit it to + * disk. Since we have read the mft record ok before, + * it is unlikely to fail writing it, so is ok to just + * return error here... (AIA) + */ + ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + total = count; + goto out; + } + + /* Handle writes beyond initialized_size. */ + + if (pos + count > na->initialized_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending, we only need to map the end of the runlist, + * starting at the last previously allocated run, so that + * we are able a new one to it. + * However, for compressed file, we need the full compression + * block, which may be split in several extents. + */ + if (NAttrDataAppending(na)) { + VCN block_begin = pos >> vol->cluster_size_bits; + + if (compressed) + block_begin &= -na->compression_block_clusters; + if (block_begin) + block_begin--; + if (ntfs_attr_map_partial_runlist(na, block_begin)) + goto err_out; + if ((update_from == -1) || (block_begin < update_from)) + update_from = block_begin; + } else +#endif + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + /* + * For a compressed attribute, we must be sure there is an + * available entry, and, when reopening a compressed file, + * we may need to split a hole. So reserve the entries + * before it gets too late. + */ + if (compressed) { + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + } + /* Set initialized_size to @pos + @count. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) + goto err_out; + + /* If write starts beyond initialized_size, zero the gap. */ + if (pos > na->initialized_size) + if (ntfs_attr_fill_zero(na, na->initialized_size, + pos - na->initialized_size)) + goto err_out; + + ctx->attr->initialized_size = cpu_to_sle64(pos + count); + /* fix data_size for compressed files */ + if (compressed) { + na->data_size = pos + count; + ctx->attr->data_size = ctx->attr->initialized_size; + } + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * Undo the change in the in-memory copy and send it + * back for writing. + */ + ctx->attr->initialized_size = + cpu_to_sle64(old_initialized_size); + ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec); + goto err_out; + } + na->initialized_size = pos + count; +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* + * NOTE: At this point the initialized_size in the mft record + * has been updated BUT there is random data on disk thus if + * we decide to abort, we MUST change the initialized_size + * again. + */ + need_to.undo_initialized_size = 1; + } + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we already extended the size of the attribute, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__); + } + goto err_out; + } + /* + * Determine if there is compressed data in the current + * compression block (when appending to an existing file). + * If so, decompression will be needed, and the full block + * must be allocated to be identified as uncompressed. + * This comes in two variants, depending on whether + * compression has saved at least one cluster. + * The compressed size can never be over full size by + * more than 485 (maximum for 15 compression blocks + * compressed to 4098 and the last 3640 bytes compressed + * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485) + * This is less than the smallest cluster, so the hole is + * is never beyond the cluster next to the position of + * the first uncompressed byte to write. + */ + compressed_part = 0; + if (compressed) { + if ((rl->lcn == (LCN)LCN_HOLE) + && wasnonresident) { + if (rl->length < na->compression_block_clusters) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ + compressed_part + = na->compression_block_clusters + - rl->length; + else { + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); + } + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } + + if (compressed_part < 0) + goto err_out; + + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; + } + ofs = pos - (rl->vcn << vol->cluster_size_bits); + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + for (hole_end = 0; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #4", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + hole_end = rl->vcn + rl->length; + + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl, + &update_from)) + goto err_out; + } + if (compressed) { + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + } + + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); +retry: + ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " + "%lld.\n", (long long)to_write, (long long)rl->vcn, + (long long)rl->lcn, (long long)ofs); + if (!NVolReadOnly(vol)) { + + s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; + s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; + u32 bsize = vol->cluster_size; + /* Byte size needed to zero fill a cluster */ + s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + /** + * Zero fill to cluster boundary if we're writing at the + * end of the attribute or into an ex-sparse cluster. + * This will cause the kernel not to seek and read disk + * blocks during write(2) to fill the end of the buffer + * which increases write speed by 2-10 fold typically. + * + * This is done even for compressed files, because + * data is generally first written uncompressed. + */ + if (rounding && ((wend == na->initialized_size) || + (wend < (hole_end << vol->cluster_size_bits)))){ + + char *cb; + + rounding += to_write; + + cb = ntfs_malloc(rounding); + if (!cb) + goto err_out; + + memcpy(cb, b, to_write); + memset(cb + to_write, 0, rounding - to_write); + + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + rounding, cb, compressed_part, + &update_from); + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounding, cb); + if (written == rounding) + written = to_write; + } + + free(cb); + } else { + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + to_write, b, compressed_part, + &update_from); + } else + written = ntfs_pwrite(vol->dev, wpos, + to_write, b); + } + } else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + fullcount -= written; + b = (const u8*)b + written; + } + if (written != to_write) { + /* Partial write cannot be dealt with, stop there */ + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (!written) + errno = EIO; + goto rl_err_out; + } + compressed_part = 0; + } +done: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ +#if PARTIAL_RUNLIST_UPDATING + updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na); +#else + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); +#endif + if (updatemap) + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + total = -1; + goto out; + } +out: + ntfs_log_leave("\n"); + return total; +rl_err_out: + eo = errno; + if (total) { + if (need_to.undo_initialized_size) { + if (pos + total > na->initialized_size) + goto done; + /* + * TODO: Need to try to change initialized_size. If it + * succeeds goto done, otherwise goto err_out. (AIA) + */ + goto err_out; + } + goto done; + } + errno = eo; +err_out: + eo = errno; + if (need_to.undo_initialized_size) { + int err; + + err = 0; + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + err = 1; + } else + ntfs_attr_reinit_search_ctx(ctx); + if (!err) { + err = ntfs_attr_lookup(na->type, na->name, + na->name_len, 0, 0, NULL, 0, ctx); + if (!err) { + na->initialized_size = old_initialized_size; + ctx->attr->initialized_size = cpu_to_sle64( + old_initialized_size); + err = ntfs_mft_record_write(vol, + ctx->ntfs_ino->mft_no, + ctx->mrec); + } + } + if (err) { + /* + * FIXME: At this stage could try to recover by filling + * old_initialized_size -> new_initialized_size with + * data or at least zeroes. (AIA) + */ + ntfs_log_error("Eeek! Failed to recover from error. " + "Leaving metadata in inconsistent " + "state! Run chkdsk!\n"); + } + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + ntfs_attr_update_mapping_pairs(na, 0); + /* Restore original data_size if needed. */ + if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) + ntfs_log_perror("Failed to restore data_size"); + errno = eo; +errno_set: + total = -1; + goto out; +} + +int ntfs_attr_pclose(ntfs_attr *na) +{ + s64 ofs; + int failed; + BOOL ok = TRUE; + VCN update_from = -1; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + int eo; + s64 hole; + int compressed_part; + BOOL compressed; + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", + na->ni->mft_no, na->type); + + if (!na || !na->ni || !na->ni->vol) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + na->unused_runs = 0; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + goto errno_set; + } + /* If this is not a compressed attribute get out */ + /* same if it is resident */ + if (!compressed || !NAttrNonResident(na)) + goto out; + + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + /* Find the runlist element containing the terminal vcn. */ + rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we have already written the last byte uncompressed, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__); + } + goto err_out; + } + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + compressed_part = 0; + if (rl->lcn >= 0) { + runlist_element *xrl; + + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else + if (rl->lcn == (LCN)LCN_HOLE) { + if (rl->length < na->compression_block_clusters) + compressed_part + = na->compression_block_clusters + - rl->length; + else + compressed_part + = na->compression_block_clusters; + } + /* done, if the last block set was compressed */ + if (compressed_part) + goto out; + + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #6", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + hole = rl->vcn + rl->length; + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + + if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from)) + goto err_out; + } + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + +retry: + failed = 0; + if (update_from < 0) update_from = 0; + if (!NVolReadOnly(vol)) { + failed = ntfs_compressed_close(na, rl, ofs, &update_from); +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + } + if (failed) { + /* If the syscall was interrupted, try again. */ + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + ok = FALSE; + goto out; + } +out: + ntfs_log_leave("\n"); + return (!ok); +rl_err_out: + /* + * need not restore old sizes, only compressed_size + * can have changed. It has been set according to + * the current runlist while updating the mapping pairs, + * and must be kept consistent with the runlists. + */ +err_out: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); + errno = eo; +errno_set: + ok = FALSE; + goto out; +} + +/** + * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read + * @na: multi sector transfer protected ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @bk_cnt: number of mst protected blocks to read + * @bk_size: size of each mst protected block in bytes + * @dst: output data buffer + * + * This function will read @bk_cnt blocks of size @bk_size bytes each starting + * at offset @pos from the ntfs attribute @na into the data buffer @b. + * + * On success, the multi sector transfer fixups are applied and the number of + * read blocks is returned. If this number is lower than @bk_cnt this means + * that the read has either reached end of attribute or that an error was + * encountered during the read so that the read is partial. 0 means end of + * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer is detected the magic is + * changed to BAAD but no error is returned, i.e. it is possible that any of + * the returned blocks have multi sector transfer errors. This should be + * detected by the caller by checking each block with is_baad_recordp(&block). + * The reasoning is that we want to fixup as many blocks as possible and we + * want to return even bad ones to the caller so, e.g. in case of ntfsck, the + * errors can be repaired. + */ +s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + const u32 bk_size, void *dst) +{ + s64 br; + u8 *end; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); + if (br <= 0) + return br; + br /= bk_size; + for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + + bk_size) + ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); + /* Finally, return the number of blocks read. */ + return br; +} + +/** + * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write + * @na: multi sector transfer protected ntfs attribute to write to + * @pos: position in the attribute to write to + * @bk_cnt: number of mst protected blocks to write + * @bk_size: size of each mst protected block in bytes + * @src: data buffer to write to disk + * + * This function will write @bk_cnt blocks of size @bk_size bytes each from + * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na + * at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @bk_cnt this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also + * return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case + * of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, + const u32 bk_size, void *src) +{ + s64 written, i; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!bk_cnt) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < bk_cnt; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)src + i * bk_size), bk_size); + if (err < 0) { + /* Abort write at this position. */ + ntfs_log_perror("%s #1", __FUNCTION__); + if (!i) + return err; + bk_cnt = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); + if (written <= 0) { + ntfs_log_perror("%s: written=%lld", __FUNCTION__, + (long long)written); + } + /* Quickly deprotect the data again. */ + for (i = 0; i < bk_cnt; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * + bk_size)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bk_size; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfs_volume *vol; + ntfschar *upcase; + u32 upcase_len; + + ntfs_log_trace("attribute type 0x%x.\n", type); + + if (ctx->ntfs_ino) { + vol = ctx->ntfs_ino->vol; + upcase = vol->upcase; + upcase_len = vol->upcase_len; + } else { + if (name && name != AT_UNNAMED) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + vol = NULL; + upcase = NULL; + upcase_len = 0; + } + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else { + register int rc; + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) + return 0; + /* @val is present; compare values. */ + else { + register int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + register u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + errno = EIO; + ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, + ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); + return -1; +} + +void ntfs_attr_name_free(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) +{ + char *name = NULL; + int name_len; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + ntfs_log_perror("ntfs_ucstombs"); + return NULL; + + } else if (name_len > 0) + return name; + + ntfs_attr_name_free(&name); + return NULL; +} + +/** + * ntfs_external_attr_find - find an attribute in the attribute list of an inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * Find an attribute by searching the attribute list for the corresponding + * attribute list entry. Having found the entry, map the mft record for read + * if the attribute is in a different mft record/inode, find the attribute in + * there and return it. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to + * ENOENT to indicate that there are no more entries. During the enumeration, + * each successful call of ntfs_external_attr_find() will return the next + * attribute described by the attribute list of the base mft record described + * by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * On first search @ctx->ntfs_ino must be the inode of the base mft record and + * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). + * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too + * (@ctx->base_ntfs_ino is then the base inode). + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ntfs_volume *vol; + ATTR_LIST_ENTRY *al_entry, *next_al_entry; + u8 *al_start, *al_end; + ATTR_RECORD *a; + ntfschar *al_name; + u32 al_name_len; + BOOL is_first_search = FALSE; + + ni = ctx->ntfs_ino; + base_ni = ctx->base_ntfs_ino; + ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", + (unsigned long long)ni->mft_no, type); + if (!base_ni) { + /* First call happens with the base mft record. */ + base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; + ctx->base_mrec = ctx->mrec; + } + if (ni == base_ni) + ctx->base_attr = ctx->attr; + if (type == AT_END) + goto not_found; + vol = base_ni->vol; + al_start = base_ni->attr_list; + al_end = al_start + base_ni->attr_list_size; + if (!ctx->al_entry) { + ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; + is_first_search = TRUE; + } + /* + * Iterate over entries in attribute list starting at @ctx->al_entry, + * or the entry following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + al_entry = ctx->al_entry; + ctx->is_first = FALSE; + /* + * If an enumeration and the first attribute is higher than + * the attribute list itself, need to return the attribute list + * attribute. + */ + if ((type == AT_UNUSED) && is_first_search && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) + goto find_attr_list_attr; + } else { + al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + /* + * If this is an enumeration and the attribute list attribute + * is the next one in the enumeration sequence, just return the + * attribute list attribute from the base mft record as it is + * not listed in the attribute list itself. + */ + if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < + le32_to_cpu(AT_ATTRIBUTE_LIST) && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) { + int rc; +find_attr_list_attr: + + /* Check for bogus calls. */ + if (name || name_len || val || val_len || lowest_vcn) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + /* We want the base record. */ + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + + /* Find the attribute list attribute. */ + rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, + IGNORE_CASE, NULL, 0, ctx); + + /* + * Setup the search context so the correct + * attribute is returned next time round. + */ + ctx->al_entry = al_entry; + ctx->is_first = TRUE; + + /* Got it. Done. */ + if (!rc) + return 0; + + /* Error! If other than not found return it. */ + if (errno != ENOENT) + return rc; + + /* Not found?!? Absurd! */ + errno = EIO; + ntfs_log_error("Attribute list wasn't found"); + return -1; + } + } + for (;; al_entry = next_al_entry) { + /* Out of bounds check. */ + if ((u8*)al_entry < base_ni->attr_list || + (u8*)al_entry > al_end) + break; /* Inode is corrupt. */ + ctx->al_entry = al_entry; + /* Catch the end of the attribute list. */ + if ((u8*)al_entry == al_end) + goto not_found; + if (!al_entry->length) + break; + if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + + le16_to_cpu(al_entry->length) > al_end) + break; + next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + + le16_to_cpu(al_entry->length)); + if (type != AT_UNUSED) { + if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) + goto not_found; + if (type != al_entry->type) + continue; + } + al_name_len = al_entry->name_length; + al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); + /* + * If !@type we want the attribute represented by this + * attribute list entry. + */ + if (type == AT_UNUSED) + goto is_enumeration; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + if (al_name_len) + goto not_found; + } else { + int rc; + + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { + + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. Now check @lowest_vcn. Continue search if the + * next attribute list entry still fits @lowest_vcn. Otherwise + * we have reached the right one or the search has failed. + */ + if (lowest_vcn && (u8*)next_al_entry >= al_start && + (u8*)next_al_entry + 6 < al_end && + (u8*)next_al_entry + le16_to_cpu( + next_al_entry->length) <= al_end && + sle64_to_cpu(next_al_entry->lowest_vcn) <= + lowest_vcn && + next_al_entry->type == al_entry->type && + next_al_entry->name_length == al_name_len && + ntfs_names_are_equal((ntfschar*)((char*) + next_al_entry + + next_al_entry->name_offset), + next_al_entry->name_length, + al_name, al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + continue; +is_enumeration: + if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { + if (MSEQNO_LE(al_entry->mft_reference) != + le16_to_cpu( + ni->mrec->sequence_number)) { + ntfs_log_error("Found stale mft reference in " + "attribute list!\n"); + break; + } + } else { /* Mft references do not match. */ + /* Do we want the base record back? */ + if (MREF_LE(al_entry->mft_reference) == + base_ni->mft_no) { + ni = ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + } else { + /* We want an extent record. */ + ni = ntfs_extent_inode_open(base_ni, + al_entry->mft_reference); + if (!ni) + break; + ctx->ntfs_ino = ni; + ctx->mrec = ni->mrec; + } + } + a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the + * mft record containing the attribute represented by the + * current al_entry. + * + * We could call into ntfs_attr_find() to find the right + * attribute in this mft record but this would be less + * efficient and not quite accurate as ntfs_attr_find() ignores + * the attribute instance numbers for example which become + * important when one plays with attribute lists. Also, because + * a proper match has been found in the attribute list entry + * above, the comparison can now be optimized. So it is worth + * re-implementing a simplified ntfs_attr_find() here. + * + * Use a manual loop so we can still use break and continue + * with the same meanings as above. + */ +do_next_attr_loop: + if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + if (a->type == AT_END) + continue; + if (!a->length) + break; + if (al_entry->instance != a->instance) + goto do_next_attr; + /* + * If the type and/or the name are/is mismatched between the + * attribute list entry and the attribute record, there is + * corruption so we break and return error EIO. + */ + if (al_entry->type != a->type) + break; + if (!ntfs_names_are_equal((ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, al_name, + al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + break; + ctx->attr = a; + /* + * If no @val specified or @val specified and it matches, we + * have found it! Also, if !@type, it is an enumeration, so we + * want the current attribute. + */ + if ((type == AT_UNUSED) || !val || (!a->non_resident && + le32_to_cpu(a->value_length) == val_len && + !memcmp((char*)a + le16_to_cpu(a->value_offset), + val, val_len))) { + return 0; + } +do_next_attr: + /* Proceed to the next attribute in the current mft record. */ + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + goto do_next_attr_loop; + } + if (ni != base_ni) { + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->attr = ctx->base_attr; + } + errno = EIO; + ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); + return -1; +not_found: + /* + * If we were looking for AT_END or we were enumerating and reached the + * end, we reset the search context @ctx and use ntfs_attr_find() to + * seek to the end of the base mft record. + */ + if (type == AT_UNUSED || type == AT_END) { + ntfs_attr_reinit_search_ctx(ctx); + return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, + ctx); + } + /* + * The attribute wasn't found. Before we return, we want to ensure + * @ctx->mrec and @ctx->attr indicate the position at which the + * attribute should be inserted in the base mft record. Since we also + * want to preserve @ctx->al_entry we cannot reinitialize the search + * context using ntfs_attr_reinit_search_ctx() as this would set + * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see + * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve + * @ctx->al_entry as the remaining fields (base_*) are identical to + * their non base_ counterparts and we cannot set @ctx->base_attr + * correctly yet as we do not know what @ctx->attr will be set to by + * the call to ntfs_attr_find() below. + */ + ctx->mrec = ctx->base_mrec; + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ctx->base_ntfs_ino; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; + /* + * In case there are multiple matches in the base mft record, need to + * keep enumerating until we get an attribute not found response (or + * another error), otherwise we would keep returning the same attribute + * over and over again and all programs using us for enumeration would + * lock up in a tight loop. + */ + { + int ret; + + do { + ret = ntfs_attr_find(type, name, name_len, ic, val, + val_len, ctx); + } while (!ret); + return ret; + } +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol; + ntfs_inode *base_ni; + int ret = -1; + + ntfs_log_enter("Entering for attribute type 0x%x\n", type); + + if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && + (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || + !vol->upcase || !vol->upcase_len))) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto out; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); + else + ret = ntfs_external_attr_find(type, name, name_len, ic, + lowest_vcn, val, val_len, ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_position - find given or next attribute type in an ntfs inode + * @type: attribute type to start lookup + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute type in an ntfs inode or the next attribute which is not + * the AT_END attribute. Please see more details at ntfs_attr_lookup. + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * The following error codes are defined: + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + * ENOSPC No attribute was found after 'type', only AT_END. + */ +int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + return -1; + if (ctx->attr->type == AT_END) { + errno = ENOSPC; + return -1; + } + } + return 0; +} + +/** + * ntfs_attr_init_search_ctx - initialize an attribute search context + * @ctx: attribute search context to initialize + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Initialize the attribute search context @ctx with @ni and @mrec. + */ +static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, + ntfs_inode *ni, MFT_RECORD *mrec) +{ + if (!mrec) + mrec = ni->mrec; + ctx->mrec = mrec; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ni; + ctx->al_entry = NULL; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; +} + +/** + * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context + * @ctx: attribute search context to reinitialize + * + * Reinitialize the attribute search context @ctx. + * + * This is used when a search for a new attribute is being started to reset + * the search context to the beginning. + */ +void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) +{ + if (!ctx->base_ntfs_ino) { + /* No attribute list. */ + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * This needs resetting due to ntfs_external_attr_find() which + * can leave it set despite having zeroed ctx->base_ntfs_ino. + */ + ctx->al_entry = NULL; + return; + } /* Attribute list. */ + ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); + return; +} + +/** + * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Allocate a new attribute search context, initialize it with @ni and @mrec, + * and return it. Return NULL on error with errno set. + * + * @mrec can be NULL, in which case the mft record is taken from @ni. + * + * Note: For low level utilities which know what they are doing we allow @ni to + * be NULL and @mrec to be set. Do NOT do this unless you understand the + * implications!!! For example it is no longer safe to call ntfs_attr_lookup(). + */ +ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) +{ + ntfs_attr_search_ctx *ctx; + + if (!ni && !mrec) { + errno = EINVAL; + ntfs_log_perror("NULL arguments"); + return NULL; + } + ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx)); + if (ctx) + ntfs_attr_init_search_ctx(ctx, ni, mrec); + return ctx; +} + +/** + * ntfs_attr_put_search_ctx - release an attribute search context + * @ctx: attribute search context to free + * + * Release the attribute search context @ctx. + */ +void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) +{ + // NOTE: save errno if it could change and function stays void! + free(ctx); +} + +/** + * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to find + * + * Search for the attribute definition record corresponding to the attribute + * @type in the $AttrDef system file. + * + * Return the attribute type definition record if found and NULL if not found + * or an error occurred. On error the error code is stored in errno. The + * following error codes are defined: + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type) +{ + ATTR_DEF *ad; + + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; + } + for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < + vol->attrdef_len && ad->type; ++ad) { + /* We haven't found it yet, carry on searching. */ + if (le32_to_cpu(ad->type) < le32_to_cpu(type)) + continue; + /* We found the attribute; return it. */ + if (ad->type == type) + return ad; + /* We have gone too far already. No point in continuing. */ + break; + } + errno = ENOENT; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; +} + +/** + * ntfs_attr_size_bounds_check - check a size of an attribute type for validity + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * @size: size which to check + * + * Check whether the @size in bytes is valid for an attribute of @type on the + * ntfs volume @vol. This information is obtained from $AttrDef system file. + * + * Return 0 if valid and -1 if not valid or an error occurred. On error the + * error code is stored in errno. The following error codes are defined: + * ERANGE - @size is not valid for the attribute @type. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). + */ +int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, + const s64 size) +{ + ATTR_DEF *ad; + s64 min_size, max_size; + + if (size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: size=%lld", __FUNCTION__, + (long long)size); + return -1; + } + + /* + * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise + * Windows would crash. This is not listed in the AttrDef. + */ + if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { + errno = ERANGE; + ntfs_log_perror("Too large attrlist (%lld)", (long long)size); + return -1; + } + + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + + min_size = sle64_to_cpu(ad->min_size); + max_size = sle64_to_cpu(ad->max_size); + + if ((min_size && (size < min_size)) || + ((max_size > 0) && (size > max_size))) { + errno = ERANGE; + ntfs_log_perror("Attr type %d size check failed (min,size,max=" + "%lld,%lld,%lld)", type, (long long)min_size, + (long long)size, (long long)max_size); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type to check + * @name: attribute name to check + * @name_len: attribute name length + * + * Check whether the attribute of @type and @name with name length @name_len on + * the ntfs volume @vol is allowed to be non-resident. This information is + * obtained from $AttrDef system file and is augmented by rules imposed by + * Microsoft (e.g. see http://support.microsoft.com/kb/974729/). + * + * Return 0 if the attribute is allowed to be non-resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +static int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type, + const ntfschar *name, int name_len) +{ + ATTR_DEF *ad; + BOOL allowed; + + /* + * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a + * name of $TXF_DATA must be resident despite the entry for + * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. + * Failure to obey this on the root directory mft record of a volume + * causes Windows Vista and later to see the volume as a RAW volume and + * thus cannot mount it at all. + */ + if ((type == AT_LOGGED_UTILITY_STREAM) + && name + && ntfs_names_are_equal(TXF_DATA, 9, name, name_len, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) + allowed = FALSE; + else { + /* Find the attribute definition record in $AttrDef. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + /* Check the flags and return the result. */ + allowed = !(ad->flags & ATTR_DEF_RESIDENT); + } + if (!allowed) { + errno = EPERM; + ntfs_log_trace("Attribute can't be non-resident\n"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_resident - check if an attribute can be resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be resident. This information is derived from our ntfs knowledge and may + * not be completely accurate, especially when user defined attributes are + * present. Basically we allow everything to be resident except for index + * allocation and extended attribute attributes. + * + * Return 0 if the attribute is allowed to be resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be resident. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + * + * Warning: In the system file $MFT the attribute $Bitmap must be non-resident + * otherwise windows will not boot (blue screen of death)! We cannot + * check for this here as we don't know which inode's $Bitmap is being + * asked about so the caller needs to special case this. + */ +int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) +{ + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + return -1; + } + if (type != AT_INDEX_ALLOCATION) + return 0; + + ntfs_log_trace("Attribute can't be resident\n"); + errno = EPERM; + return -1; +} + +/** + * ntfs_make_room_for_attr - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * ENOSPC - There is not enough space available to complete operation. The + * caller has to make space before calling this. + * EINVAL - Input parameters were faulty. + */ +int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) +{ + u32 biu; + + ntfs_log_trace("Entering for pos 0x%d, size %u.\n", + (int)(pos - (u8*)m), (unsigned) size); + + /* Make size 8-byte alignment. */ + size = (size + 7) & ~7; + + /* Rigorous consistency checks. */ + if (!m || !pos || pos < (u8*)m) { + errno = EINVAL; + ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); + return -1; + } + /* The -8 is for the attribute terminator. */ + if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { + errno = EINVAL; + return -1; + } + /* Nothing to do. */ + if (!size) + return 0; + + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("No enough space in the MFT record\n"); + return -1; + } + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (u8*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_resident_attr_record_add - add resident attribute to inode + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute + * @name: name of the new attribute + * @name_len: name length of the new attribute + * @val: value of the new attribute + * @size: size of new attribute (length of @val, if @val != NULL) + * @flags: flags of the new attribute + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type and with same name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, u32 size, + ATTR_FLAGS data_flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + int err, offset; + ntfs_inode *base_ni; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, (unsigned) data_flags); + + if (!ni || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_resident(ni->vol, type)) { + if (errno == EPERM) + ntfs_log_trace("Attribute can't be resident.\n"); + else + ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, + ctx)) { + err = EEXIST; + ntfs_log_trace("Attribute already present.\n"); + goto put_err_out; + } + if (errno != ENOENT) { + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + length = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_trace("Failed to make room for attribute.\n"); + goto put_err_out; + } + + /* Setup record fields. */ + offset = ((u8*)a - (u8*)m); + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 0; + a->name_length = name_len; + a->name_offset = (name_len + ? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) + : const_cpu_to_le16(0)); + a->flags = data_flags; + a->instance = m->next_attr_instance; + a->value_length = cpu_to_le32(size); + a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); + if (val) + memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); + else + memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); + if (type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_attr_record_resize(m, a, 0); + ntfs_log_trace("Failed add attribute entry to " + "ATTRIBUTE_LIST.\n"); + goto put_err_out; + } + } + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); + } + ntfs_inode_mark_dirty(ni); + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_record_add - add extent of non-resident attribute + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute extent + * @name: name of the new attribute extent + * @name_len: name length of the new attribute extent + * @lowest_vcn: lowest vcn of the new attribute extent + * @dataruns_size: dataruns size of the new attribute extent + * @flags: flags of the new attribute extent + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type, with same lowest vcn and with same + * name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + ATTR_FLAGS flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + ntfs_inode *base_ni; + int err, offset; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " + "dataruns_size %d, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, + (long long) lowest_vcn, dataruns_size, (unsigned) flags); + + if (!ni || dataruns_size <= 0 || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno == EPERM) + ntfs_log_perror("Attribute can't be non resident"); + else + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, + ctx)) { + err = EEXIST; + ntfs_log_perror("Attribute 0x%x already present", type); + goto put_err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("ntfs_attr_find failed"); + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + dataruns_size = (dataruns_size + 7) & ~7; + length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * + name_len + 7) & ~7) + dataruns_size + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_perror("Failed to make room for attribute"); + goto put_err_out; + } + + /* Setup record fields. */ + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0)); + a->flags = flags; + a->instance = m->next_attr_instance; + a->lowest_vcn = cpu_to_sle64(lowest_vcn); + a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); + a->compression_unit = (flags & ATTR_IS_COMPRESSED) + ? STANDARD_COMPRESSION_UNIT : 0; + /* If @lowest_vcn == 0, than setup empty attribute. */ + if (!lowest_vcn) { + a->highest_vcn = cpu_to_sle64(-1); + a->allocated_size = 0; + a->data_size = 0; + a->initialized_size = 0; + /* Set empty mapping pairs. */ + *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; + } + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_log_perror("Failed add attr entry to attrlist"); + ntfs_attr_record_resize(m, a, 0); + goto put_err_out; + } + } + ntfs_inode_mark_dirty(ni); + /* + * Locate offset from start of the MFT record where new attribute is + * placed. We need relookup it, because record maybe moved during + * update of attribute list. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, + lowest_vcn, NULL, 0, ctx)) { + ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); + ntfs_attr_put_search_ctx(ctx); + return -1; + + } + offset = (u8*)ctx->attr - (u8*)ctx->mrec; + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_rm - remove attribute extent + * @ctx: search context describing the attribute which should be removed + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ATTR_TYPES type; + + if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->attr->type)); + type = ctx->attr->type; + ni = ctx->ntfs_ino; + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + /* Remove attribute itself. */ + if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { + ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " + "record.\n"); + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) + if (ntfs_attrlist_entry_add(ni, ctx->attr)) + ntfs_log_trace("Rollback failed. Leaving inconstant " + "metadata.\n"); + errno = EIO; + return -1; + } + ntfs_inode_mark_dirty(ni); + + /* + * Remove record from $ATTRIBUTE_LIST if present and we don't want + * delete $ATTRIBUTE_LIST itself. + */ + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { + if (ntfs_attrlist_entry_rm(ctx)) { + ntfs_log_trace("Couldn't delete record from " + "$ATTRIBUTE_LIST.\n"); + return -1; + } + } + + /* Post $ATTRIBUTE_LIST delete setup. */ + if (type == AT_ATTRIBUTE_LIST) { + if (NInoAttrList(base_ni) && base_ni->attr_list) + free(base_ni->attr_list); + base_ni->attr_list = NULL; + NInoClearAttrList(base_ni); + NInoAttrListClearDirty(base_ni); + } + + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - + le16_to_cpu(ctx->mrec->attrs_offset) == 8) { + if (ntfs_mft_record_free(ni->vol, ni)) { + // FIXME: We need rollback here. + ntfs_log_trace("Couldn't free MFT record.\n"); + errno = EIO; + return -1; + } + /* Remove done if we freed base inode. */ + if (ni == base_ni) + return 0; + } + + if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) + return 0; + + /* Remove attribute list if we don't need it any more. */ + if (!ntfs_attrlist_need(base_ni)) { + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* + * FIXME: Should we succeed here? Definitely something + * goes wrong because NInoAttrList(base_ni) returned + * that we have got attribute list. + */ + ntfs_log_trace("Couldn't find attribute list. Succeed " + "anyway.\n"); + return 0; + } + /* Deallocate clusters. */ + if (ctx->attr->non_resident) { + runlist *al_rl; + + al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, + ctx->attr, NULL); + if (!al_rl) { + ntfs_log_trace("Couldn't decompress attribute list " + "runlist. Succeed anyway.\n"); + return 0; + } + if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { + ntfs_log_trace("Leaking clusters! Run chkdsk. " + "Couldn't free clusters from " + "attribute list runlist.\n"); + } + free(al_rl); + } + /* Remove attribute record itself. */ + if (ntfs_attr_record_rm(ctx)) { + /* + * FIXME: Should we succeed here? BTW, chkdsk doesn't + * complain if it find MFT record with attribute list, + * but without extents. + */ + ntfs_log_trace("Couldn't remove attribute list. Succeed " + "anyway.\n"); + return 0; + } + } + return 0; +} + +/** + * ntfs_attr_add - add attribute to inode + * @ni: opened ntfs inode to which add attribute + * @type: type of the new attribute + * @name: name in unicode of the new attribute + * @name_len: name length in unicode characters of the new attribute + * @val: value of new attribute + * @size: size of the new attribute / length of @val (if specified) + * + * @val should always be specified for always resident attributes (eg. FILE_NAME + * attribute), for attributes that can become non-resident @val can be NULL + * (eg. DATA attribute). @size can be specified even if @val is NULL, in this + * case data size will be equal to @size and initialized size will be equal + * to 0. + * + * If inode haven't got enough space to add attribute, add attribute to one of + * it extents, if no extents present or no one of them have enough space, than + * allocate new extent and add attribute to it. + * + * If on one of this steps attribute list is needed but not present, than it is + * added transparently to caller. So, this function should not be called with + * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call + * ntfs_inode_add_attrlist instead. + * + * On success return 0. On error return -1 with errno set to the error code. + */ +int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, s64 size) +{ + u32 attr_rec_size; + int err, i, offset; + BOOL is_resident; + BOOL can_be_non_resident = FALSE; + ntfs_inode *attr_ni; + ntfs_attr *na; + ATTR_FLAGS data_flags; + + if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, + (long long)size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", + (long long)ni->mft_no, type, (long long)size); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + /* Check the attribute type and the size. */ + if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* Sanity checks for always resident attributes. */ + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + goto err_out; + } + /* @val is mandatory. */ + if (!val) { + errno = EINVAL; + ntfs_log_perror("val is mandatory for always resident " + "attributes"); + return -1; + } + if (size > ni->vol->mft_record_size) { + errno = ERANGE; + ntfs_log_perror("Attribute is too big"); + return -1; + } + } else + can_be_non_resident = TRUE; + + /* + * Determine resident or not will be new attribute. We add 8 to size in + * non resident case for mapping pairs. + */ + if (!ntfs_attr_can_be_resident(ni->vol, type)) { + is_resident = TRUE; + } else { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_resident failed"); + goto err_out; + } + is_resident = FALSE; + } + /* Calculate attribute record size. */ + if (is_resident) + attr_rec_size = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + else + attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; + + /* + * If we have enough free space for the new attribute in the base MFT + * record, then add attribute to it. + */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { + attr_ni = ni; + goto add_attr_record; + } + + /* Try to add to extent inodes. */ + if (ntfs_inode_attach_all_extents(ni)) { + err = errno; + ntfs_log_perror("Failed to attach all extents to inode"); + goto err_out; + } + for (i = 0; i < ni->nr_extents; i++) { + attr_ni = ni->extent_nis[i]; + if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - + le32_to_cpu(attr_ni->mrec->bytes_in_use) >= + attr_rec_size) + goto add_attr_record; + } + + /* There is no extent that contain enough space for new attribute. */ + if (!NInoAttrList(ni)) { + /* Add attribute list not present, add it and retry. */ + if (ntfs_inode_add_attrlist(ni)) { + err = errno; + ntfs_log_perror("Failed to add attribute list"); + goto err_out; + } + return ntfs_attr_add(ni, type, name, name_len, val, size); + } + /* Allocate new extent. */ + attr_ni = ntfs_mft_record_alloc(ni->vol, ni); + if (!attr_ni) { + err = errno; + ntfs_log_perror("Failed to allocate extent record"); + goto err_out; + } + +add_attr_record: + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && ((type == AT_DATA) + || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) + data_flags = ATTR_IS_COMPRESSED; + else + data_flags = const_cpu_to_le16(0); + if (is_resident) { + /* Add resident attribute. */ + offset = ntfs_resident_attr_record_add(attr_ni, type, name, + name_len, val, size, data_flags); + if (offset < 0) { + if (errno == ENOSPC && can_be_non_resident) + goto add_non_resident; + err = errno; + ntfs_log_perror("Failed to add resident attribute"); + goto free_err_out; + } + return 0; + } + +add_non_resident: + /* Add non resident attribute. */ + offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, + name_len, 0, 8, data_flags); + if (offset < 0) { + err = errno; + ntfs_log_perror("Failed to add non resident attribute"); + goto free_err_out; + } + + /* If @size == 0, we are done. */ + if (!size) + return 0; + + /* Open new attribute and resize it. */ + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added attribute"); + goto rm_attr_err_out; + } + /* Resize and set attribute value. */ + if (ntfs_attr_truncate(na, size) || + (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { + err = errno; + ntfs_log_perror("Failed to initialize just added attribute"); + if (ntfs_attr_rm(na)) + ntfs_log_perror("Failed to remove just added attribute"); + ntfs_attr_close(na); + goto err_out; + } + ntfs_attr_close(na); + return 0; + +rm_attr_err_out: + /* Remove just added attribute. */ + if (ntfs_attr_record_resize(attr_ni->mrec, + (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) + ntfs_log_perror("Failed to remove just added attribute #2"); +free_err_out: + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - + le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) + ntfs_log_perror("Failed to free MFT record"); +err_out: + errno = err; + return -1; +} + +/* + * Change an attribute flag + */ + +int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) +{ + ntfs_attr_search_ctx *ctx; + int res; + + res = -1; + /* Search for designated attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(type, name, name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* do the requested change (all small endian le16) */ + ctx->attr->flags = (ctx->attr->flags & ~mask) + | (flags & mask); + NInoSetDirty(ni); + res = 0; + } + ntfs_attr_put_search_ctx(ctx); + } + return (res); +} + + +/** + * ntfs_attr_rm - remove attribute from ntfs inode + * @na: opened ntfs attribute to delete + * + * Remove attribute and all it's extents from ntfs inode. If attribute was non + * resident also free all clusters allocated by attribute. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_attr_rm(ntfs_attr *na) +{ + ntfs_attr_search_ctx *ctx; + int ret = 0; + + if (!na) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) na->ni->mft_no, na->type); + + /* Free cluster allocation. */ + if (NAttrNonResident(na)) { + if (ntfs_attr_map_whole_runlist(na)) + return -1; + if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { + ntfs_log_trace("Failed to free cluster allocation. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + } + + /* Search for attribute extents and remove them all. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_trace("Failed to remove attribute extent. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + ntfs_attr_reinit_search_ctx(ctx); + } + ntfs_attr_put_search_ctx(ctx); + if (errno != ENOENT) { + ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " + "metadata.\n"); + ret = -1; + } + + return ret; +} + +/** + * ntfs_attr_record_resize - resize an attribute record + * @m: mft record containing attribute record + * @a: attribute record to resize + * @new_size: new size in bytes to which to resize the attribute record @a + * + * Resize the attribute record @a, i.e. the resident part of the attribute, in + * the mft record @m to @new_size bytes. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + * + * Warning: If you make a record smaller without having copied all the data you + * are interested in the data may be overwritten! + */ +int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) +{ + u32 old_size, alloc_size, attr_size; + + old_size = le32_to_cpu(m->bytes_in_use); + alloc_size = le32_to_cpu(m->bytes_allocated); + attr_size = le32_to_cpu(a->length); + + ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", + (unsigned)old_size, (unsigned)alloc_size, + (unsigned)attr_size, (unsigned)new_size); + + /* Align to 8 bytes, just in case the caller hasn't. */ + new_size = (new_size + 7) & ~7; + + /* If the actual attribute length has changed, move things around. */ + if (new_size != attr_size) { + + u32 new_muse = old_size - attr_size + new_size; + + /* Not enough space in this mft record. */ + if (new_muse > alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Not enough space in the MFT record " + "(%u > %u)\n", new_muse, alloc_size); + return -1; + } + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", + new_muse, alloc_size); + return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + } + + /* Move attributes following @a to their new location. */ + memmove((u8 *)a + new_size, (u8 *)a + attr_size, + old_size - ((u8 *)a - (u8 *)m) - attr_size); + + /* Adjust @m to reflect the change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + + /* Adjust @a to reflect the new size. */ + if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) + a->length = cpu_to_le32(new_size); + } + return 0; +} + +/** + * ntfs_resident_attr_value_resize - resize the value of a resident attribute + * @m: mft record containing attribute record + * @a: attribute record whose value to resize + * @new_size: new size in bytes to which to resize the attribute value of @a + * + * Resize the value of the attribute @a in the mft record @m to @new_size bytes. + * If the value is made bigger, the newly "allocated" space is cleared. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + */ +int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size) +{ + int ret; + + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); + + /* Resize the resident part of the attribute record. */ + if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + + new_size + 7) & ~7)) < 0) + return ret; + /* + * If we made the attribute value bigger, clear the area between the + * old size and @new_size. + */ + if (new_size > le32_to_cpu(a->value_length)) + memset((u8*)a + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length), 0, new_size - + le32_to_cpu(a->value_length)); + /* Finally update the length of the attribute value. */ + a->value_length = cpu_to_le32(new_size); + return 0; +} + +/** + * ntfs_attr_record_move_to - move attribute record to target inode + * @ctx: attribute search context describing the attribute record + * @ni: opened ntfs inode to which move attribute record + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) +{ + ntfs_attr_search_ctx *nctx; + ATTR_RECORD *a; + int err; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " + "0x%llx, ni->mft_no 0x%llx.\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (long long) ctx->ntfs_ino->mft_no, + (long long) ni->mft_no); + + if (ctx->ntfs_ino == ni) + return 0; + + if (!ctx->al_entry) { + ntfs_log_trace("Inode should contain attribute list to use this " + "function.\n"); + errno = EINVAL; + return -1; + } + + /* Find place in MFT record where attribute will be moved. */ + a = ctx->attr; + nctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!nctx) + return -1; + + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, + 0, nctx)) { + ntfs_log_trace("Attribute of such type, with same name already " + "present in this MFT record.\n"); + err = EEXIST; + goto put_err_out; + } + if (errno != ENOENT) { + err = errno; + ntfs_log_debug("Attribute lookup failed.\n"); + goto put_err_out; + } + + /* Make space and move attribute. */ + if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, + le32_to_cpu(a->length))) { + err = errno; + ntfs_log_trace("Couldn't make space for attribute.\n"); + goto put_err_out; + } + memcpy(nctx->attr, a, le32_to_cpu(a->length)); + nctx->attr->instance = nctx->mrec->next_attr_instance; + nctx->mrec->next_attr_instance = cpu_to_le16( + (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); + ntfs_attr_record_resize(ctx->mrec, a, 0); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_inode_mark_dirty(ni); + + /* Update attribute list. */ + ctx->al_entry->mft_reference = + MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + ctx->al_entry->instance = nctx->attr->instance; + ntfs_attrlist_mark_dirty(ni); + + ntfs_attr_put_search_ctx(nctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(nctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_move_away - move away attribute record from it's mft record + * @ctx: attribute search context describing the attribute record + * @extra: minimum amount of free space in the new holder of record + * + * New attribute record holder must have free @extra bytes after moving + * attribute record to it. + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) +{ + ntfs_inode *base_ni, *ni; + MFT_RECORD *m; + int i; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, + ctx, ctx ? ctx->attr : NULL, extra); + return -1; + } + + ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (unsigned long long)ctx->ntfs_ino->mft_no); + + if (ctx->ntfs_ino->nr_extents == -1) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + if (!NInoAttrList(base_ni)) { + errno = EINVAL; + ntfs_log_perror("Inode %llu has no attrlist", + (unsigned long long)base_ni->mft_no); + return -1; + } + + if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { + ntfs_log_perror("Couldn't attach extents, inode=%llu", + (unsigned long long)base_ni->mft_no); + return -1; + } + + /* Walk through all extents and try to move attribute to them. */ + for (i = 0; i < base_ni->nr_extents; i++) { + ni = base_ni->extent_nis[i]; + m = ni->mrec; + + if (ctx->ntfs_ino->mft_no == ni->mft_no) + continue; + + if (le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) < + le32_to_cpu(ctx->attr->length) + extra) + continue; + + /* + * ntfs_attr_record_move_to can fail if extent with other lowest + * VCN already present in inode we trying move record to. So, + * do not return error. + */ + if (!ntfs_attr_record_move_to(ctx, ni)) + return 0; + } + + /* + * Failed to move attribute to one of the current extents, so allocate + * new extent and move attribute to it. + */ + ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Couldn't allocate MFT record"); + return -1; + } + if (ntfs_attr_record_move_to(ctx, ni)) { + ntfs_log_perror("Couldn't move attribute to MFT record"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute + * @na: open ntfs attribute to make non-resident + * @ctx: ntfs search context describing the attribute + * + * Convert a resident ntfs attribute to a non-resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * TODO: others... + * + * NOTE to self: No changes in the attribute list are required to move from + * a resident to a non-resident attribute. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx) +{ + s64 new_allocated_size, bw; + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + runlist *rl; + int mp_size, mp_ofs, name_ofs, arec_size, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Some preliminary sanity checking. */ + if (NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make non-resident attribute " + "non-resident. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Check that the attribute is allowed to be non-resident. */ + if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len)) + return -1; + + new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size + - 1) & ~(vol->cluster_size - 1); + + if (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } + /* Start by allocating clusters to hold the attribute value. */ + rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> + vol->cluster_size_bits, -1, DATA_ZONE); + if (!rl) + return -1; + } else + rl = NULL; + /* + * Setup the in-memory attribute structure to be non-resident so that + * we can use ntfs_attr_pwrite(). + */ + NAttrSetNonResident(na); + NAttrSetBeingNonResident(na); + na->rl = rl; + na->allocated_size = new_allocated_size; + na->data_size = na->initialized_size = le32_to_cpu(a->value_length); + /* + * FIXME: For now just clear all of these as we don't support them when + * writing. + */ + NAttrClearSparse(na); + NAttrClearEncrypted(na); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* set compression writing parameters */ + na->compression_block_size + = 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits); + na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT; + } + + if (rl) { + /* Now copy the attribute value to the allocated cluster(s). */ + bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), + (u8*)a + le16_to_cpu(a->value_offset)); + if (bw != le32_to_cpu(a->value_length)) { + err = errno; + ntfs_log_debug("Eeek! Failed to write out attribute value " + "(bw = %lli, errno = %i). " + "Aborting...\n", (long long)bw, err); + if (bw >= 0) + err = EIO; + goto cluster_free_err_out; + } + } + /* Determine the size of the mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX); + if (mp_size < 0) { + err = errno; + ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " + "Aborting...\n"); + goto cluster_free_err_out; + } + /* Calculate new offsets for the name and the mapping pairs array. */ + if (na->ni->flags & FILE_ATTR_COMPRESSED) + name_ofs = (sizeof(ATTR_REC) + 7) & ~7; + else + name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; + mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + /* + * Determine the size of the resident part of the non-resident + * attribute record. (Not compressed thus no compressed_size element + * present.) + */ + arec_size = (mp_ofs + mp_size + 7) & ~7; + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + err = errno; + goto cluster_free_err_out; + } + + /* + * Convert the resident part of the attribute record to describe a + * non-resident attribute. + */ + a->non_resident = 1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + a->name_offset = cpu_to_le16(name_ofs); + + /* Setup the fields specific to non-resident attributes. */ + a->lowest_vcn = cpu_to_sle64(0); + a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> + vol->cluster_size_bits); + + a->mapping_pairs_offset = cpu_to_le16(mp_ofs); + + /* + * Update the flags to match the in-memory ones. + * However cannot change the compression state if we had + * a fuse_file_info open with a mark for release. + * The decisions about compression can only be made when + * creating/recreating the stream, not when making non resident. + */ + a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* support only ATTR_IS_COMPRESSED compression mode */ + a->compression_unit = STANDARD_COMPRESSION_UNIT; + a->compressed_size = const_cpu_to_le64(0); + } else { + a->compression_unit = 0; + a->flags &= ~ATTR_COMPRESSION_MASK; + na->data_flags = a->flags; + } + + memset(&a->reserved1, 0, sizeof(a->reserved1)); + + a->allocated_size = cpu_to_sle64(new_allocated_size); + a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); + + /* Generate the mapping pairs array in the attribute record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, + rl, 0, NULL) < 0) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " + "corrupt attribute record on disk. In memory " + "runlist is still intact! Error code is %i. " + "FIXME: Need to rollback instead!\n", errno); + return -1; + } + + /* Done! */ + return 0; + +cluster_free_err_out: + if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) + ntfs_log_trace("Eeek! Failed to release allocated clusters in error " + "code path. Leaving inconsistent metadata...\n"); + NAttrClearNonResident(na); + na->allocated_size = na->data_size; + na->rl = NULL; + free(rl); + errno = err; + return -1; +} + + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); + +/** + * ntfs_resident_attr_resize - resize a resident, open ntfs attribute + * @na: resident ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * + * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + * Can also be used to force an attribute non-resident. In this case, the + * size cannot be changed. + * + * On success return 0 + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + BOOL force_non_resident) +{ + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol; + ntfs_inode *ni; + int err, ret = STATUS_ERROR; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + /* Get the attribute record that needs modification. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, + ctx)) { + err = errno; + ntfs_log_perror("ntfs_attr_lookup failed"); + goto put_err_out; + } + vol = na->ni->vol; + /* + * Check the attribute type and the corresponding minimum and maximum + * sizes against @newsize and fail if @newsize is out of bounds. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + goto put_err_out; + } + /* + * If @newsize is bigger than the mft record we need to make the + * attribute non-resident if the attribute type supports it. If it is + * smaller we can go ahead and attempt the resize. + */ + if ((newsize < vol->mft_record_size) && !force_non_resident) { + /* Perform the resize of the attribute record. */ + if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize))) { + /* Update attribute size everywhere. */ + na->data_size = na->initialized_size = newsize; + na->allocated_size = (newsize + 7) & ~7; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + na->compressed_size = na->allocated_size; + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); + } + goto resize_done; + } + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + err = errno; + goto put_err_out; + } + } + /* There is not enough space in the mft record to perform the resize. */ + + /* Make the attribute non-resident if possible. */ + if (!ntfs_attr_make_non_resident(na, ctx)) { + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (force_non_resident) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } + /* Resize non-resident attribute */ + return ntfs_attr_truncate(na, newsize); + } else if (errno != ENOSPC && errno != EPERM) { + err = errno; + ntfs_log_perror("Failed to make attribute non-resident"); + goto put_err_out; + } + + /* Try to make other attributes non-resident and retry each time. */ + ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + ntfs_attr *tna; + ATTR_RECORD *a; + + a = ctx->attr; + if (a->non_resident) + continue; + + /* + * Check out whether convert is reasonable. Assume that mapping + * pairs will take 8 bytes. + */ + if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, + compressed_size) + ((a->name_length * + sizeof(ntfschar) + 7) & ~7) + 8) + continue; + + tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + + le16_to_cpu(a->name_offset)), a->name_length); + if (!tna) { + err = errno; + ntfs_log_perror("Couldn't open attribute"); + goto put_err_out; + } + if (ntfs_attr_make_non_resident(tna, ctx)) { + ntfs_attr_close(tna); + continue; + } + if ((tna->type == AT_DATA) && !tna->name_len) { + /* + * If we had to make the unnamed data attribute + * non-resident, propagate its new allocated size + * to all name attributes and directory indexes + */ + tna->ni->allocated_size = tna->allocated_size; + NInoFileNameSetDirty(tna->ni); + } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } + ntfs_inode_mark_dirty(tna->ni); + ntfs_attr_close(tna); + ntfs_attr_put_search_ctx(ctx); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); + goto put_err_out; + } + + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, + non_resident_end) + 8)) { + ntfs_log_perror("Could not free space in MFT record"); + return -1; + } + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + + /* + * Move the attribute to a new mft record, creating an attribute list + * attribute or modifying it if it is already present. + */ + + /* Point search context back to attribute which we need resize. */ + ntfs_attr_init_search_ctx(ctx, na->ni, NULL); + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); + err = errno; + goto put_err_out; + } + + /* + * Check whether attribute is already single in this MFT record. + * 8 added for the attribute terminator. + */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) == + le16_to_cpu(ctx->mrec->attrs_offset) + + le32_to_cpu(ctx->attr->length) + 8) { + err = ENOSPC; + ntfs_log_trace("MFT record is filled with one attribute\n"); + ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + goto put_err_out; + } + + /* Add attribute list if not present. */ + if (na->ni->nr_extents == -1) + ni = na->ni->base_ni; + else + ni = na->ni; + if (!NInoAttrList(ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(ni)) + return -1; + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(vol, ni); + if (!ni) { + err = errno; + ntfs_log_perror("Couldn't allocate new MFT record"); + goto put_err_out; + } + /* Move attribute to it. */ + if (ntfs_attr_record_move_to(ctx, ni)) { + err = errno; + ntfs_log_perror("Couldn't move attribute to new MFT record"); + goto put_err_out; + } + /* Update ntfs attribute. */ + if (na->ni->nr_extents == -1) + na->ni = ni; + + ntfs_attr_put_search_ctx(ctx); + /* Try to perform resize once again. */ + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + +resize_done: + /* + * Set the inode (and its base inode if it exists) dirty so it is + * written out later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); + ntfs_log_leave("\n"); + return ret; +} + +/* + * Force an attribute to be made non-resident without + * changing its size. + * + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. + * + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. + * + * returns 0 if successful, + * < 0 if failed, with errno telling why + */ + +int ntfs_attr_force_non_resident(ntfs_attr *na) +{ + int res; + + res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); +} + +/** + * ntfs_attr_make_resident - convert a non-resident to a resident attribute + * @na: open ntfs attribute to make resident + * @ctx: ntfs search context describing the attribute + * + * Convert a non-resident ntfs attribute to a resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed. + * EPERM - The attribute is not allowed to be resident. + * EIO - I/O error, damaged inode or bug. + * ENOSPC - There is no enough space to perform conversion. + * EOPNOTSUPP - Requested conversion is not supported yet. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + int name_ofs, val_ofs, err = EIO; + s64 arec_size, bytes_read; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Should be called for the first extent of the attribute. */ + if (sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_trace("Eeek! Should be called for the first extent of the " + "attribute. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Some preliminary sanity checking. */ + if (!NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make resident attribute resident. " + "Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ + if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { + errno = EPERM; + return -1; + } + + /* Check that the attribute is allowed to be resident. */ + if (ntfs_attr_can_be_resident(vol, na->type)) + return -1; + + if (na->data_flags & ATTR_IS_ENCRYPTED) { + ntfs_log_trace("Making encrypted streams resident is not " + "implemented yet.\n"); + errno = EOPNOTSUPP; + return -1; + } + + /* Work out offsets into and size of the resident attribute. */ + name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ + val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + arec_size = (val_ofs + na->data_size + 7) & ~7; + + /* Sanity check the size before we start modifying the attribute. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + + arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("Not enough space to make attribute resident\n"); + return -1; + } + + /* Read and cache the whole runlist if not already done. */ + if (ntfs_attr_map_whole_runlist(na)) + return -1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) { + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + } + a->name_offset = cpu_to_le16(name_ofs); + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + /* + * Bug, because ntfs_attr_record_resize should not fail (we + * already checked that attribute fits MFT record). + */ + ntfs_log_error("BUG! Failed to resize attribute record. " + "Please report to the %s. Aborting...\n", + NTFS_DEV_LIST); + errno = EIO; + return -1; + } + + /* Convert the attribute record to describe a resident attribute. */ + a->non_resident = 0; + a->flags = 0; + a->value_length = cpu_to_le32(na->data_size); + a->value_offset = cpu_to_le16(val_ofs); + /* + * If a data stream was wiped out, adjust the compression mode + * to current state of compression flag + */ + if (!na->data_size + && (na->type == AT_DATA) + && (na->ni->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (na->ni->flags & FILE_ATTR_COMPRESSED)) { + a->flags |= ATTR_IS_COMPRESSED; + na->data_flags = a->flags; + } + /* + * File names cannot be non-resident so we would never see this here + * but at least it serves as a reminder that there may be attributes + * for which we do need to set this flag. (AIA) + */ + if (a->type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + a->reservedR = 0; + + /* Sanity fixup... Shouldn't really happen. (AIA) */ + if (na->initialized_size > na->data_size) + na->initialized_size = na->data_size; + + /* Copy data from run list to resident attribute value. */ + bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, + (u8*)a + val_ofs); + if (bytes_read != na->initialized_size) { + if (bytes_read < 0) + err = errno; + ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " + "inconstant metadata. Run chkdsk. " + "Aborting...\n"); + errno = err; + return -1; + } + + /* Clear memory in gap between initialized_size and data_size. */ + if (na->initialized_size < na->data_size) + memset((u8*)a + val_ofs + na->initialized_size, 0, + na->data_size - na->initialized_size); + + /* + * Deallocate clusters from the runlist. + * + * NOTE: We can use ntfs_cluster_free() because we have already mapped + * the whole run list and thus it doesn't matter that the attribute + * record is in a transiently corrupted state at this moment in time. + */ + if (ntfs_cluster_free(vol, na, 0, -1) < 0) { + err = errno; + ntfs_log_perror("Eeek! Failed to release allocated clusters"); + ntfs_log_trace("Ignoring error and leaving behind wasted " + "clusters.\n"); + } + + /* Throw away the now unused runlist. */ + free(na->rl); + na->rl = NULL; + + /* Update in-memory struct ntfs_attr. */ + NAttrClearNonResident(na); + NAttrClearSparse(na); + NAttrClearEncrypted(na); + na->initialized_size = na->data_size; + na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; + na->compression_block_size = 0; + na->compression_block_size_bits = na->compression_block_clusters = 0; + return 0; +} + +/* + * If we are in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ +static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, + hole_type holes, ntfs_attr_search_ctx *ctx) +{ + int sparse, ret = 0; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (a->lowest_vcn) + goto out; + + a->allocated_size = cpu_to_sle64(na->allocated_size); + + /* Update sparse bit, unless this is an intermediate state */ + if (holes == HOLES_DELAY) + sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); + else { + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } + } + + /* Check whether attribute becomes sparse, unless check is delayed. */ + if ((holes != HOLES_DELAY) + && sparse + && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* + * Move attribute to another mft record, if attribute is too + * small to add compressed_size field to it and we have no + * free space in the current mft record. + */ + if ((le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset) == 8) + && !(le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use))) { + + if (!NInoAttrList(na->ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(na->ni)) + goto leave; + goto retry; + } + if (ntfs_attr_record_move_away(ctx, 8)) { + ntfs_log_perror("Failed to move attribute"); + goto error; + } + ntfs_attr_put_search_ctx(ctx); + goto retry; + } + if (!(le32_to_cpu(a->length) - le16_to_cpu( + a->mapping_pairs_offset))) { + errno = EIO; + ntfs_log_perror("Mapping pairs space is 0"); + goto error; + } + + NAttrSetSparse(na); + a->flags |= ATTR_IS_SPARSE; + na->data_flags = a->flags; + a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows + set it so, even if attribute is not actually compressed. */ + + memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); + } + + /* Attribute no longer sparse. */ + if (!sparse && (a->flags & ATTR_IS_SPARSE) && + !(a->flags & ATTR_IS_COMPRESSED)) { + + NAttrClearSparse(na); + a->flags &= ~ATTR_IS_SPARSE; + a->compression_unit = 0; + + memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + if (le16_to_cpu(a->name_offset) >= 8) + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); + } + + /* Update compressed size if required. */ + if (NAttrFullyMapped(na) + && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); + if (new_compr_size == -1) + goto error; + + na->compressed_size = new_compr_size; + a->compressed_size = cpu_to_sle64(new_compr_size); + } + /* + * Set FILE_NAME dirty flag, to update sparse bit and + * allocated size in the index. + */ + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + NInoFileNameSetDirty(na->ni); + } +out: + return ret; +leave: ret = -1; goto out; /* return -1 */ +retry: ret = -2; goto out; +error: ret = -3; goto out; +} + +#define NTFS_VCN_DELETE_MARK -2 +/** + * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs + */ +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn, + hole_type holes) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *ni, *base_ni; + MFT_RECORD *m; + ATTR_RECORD *a; + VCN stop_vcn; + const runlist_element *stop_rl; + int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; + BOOL finished_build; + BOOL first_updated = FALSE; + +retry: + if (!na || !na->rl) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p", __FUNCTION__, na); + return -1; + } + + ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (!NAttrNonResident(na)) { + errno = EINVAL; + ntfs_log_perror("%s: resident attribute", __FUNCTION__); + return -1; + } + +#if PARTIAL_RUNLIST_UPDATING + /* + * For a file just been made sparse, we will have + * to reformat the first extent, so be sure the + * runlist is fully mapped and fully processed. + * Same if the file was sparse and is not any more. + * Note : not needed if the full runlist is to be processed + */ + if ((holes != HOLES_DELAY) + && (!NAttrFullyMapped(na) || from_vcn) + && !(na->data_flags & ATTR_IS_COMPRESSED)) { + BOOL changed; + + if (!(na->data_flags & ATTR_IS_SPARSE)) { + int sparse; + runlist_element *xrl; + + /* + * If attribute was not sparse, we only + * have to check whether there is a hole + * in the updated region. + */ + xrl = na->rl; + if (xrl->lcn == LCN_RL_NOT_MAPPED) + xrl++; + sparse = ntfs_rl_sparse(xrl); + if (sparse < 0) { + ntfs_log_error("Could not check whether sparse\n"); + errno = EIO; + return (-1); + } + changed = sparse > 0; + } else { + /* + * If attribute was sparse, the compressed + * size has been maintained, and it gives + * and easy way to check whether the + * attribute is still sparse. + */ + changed = (((na->data_size - 1) + | (na->ni->vol->cluster_size - 1)) + 1) + == na->compressed_size; + } + if (changed) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_error("Could not map whole for sparse change\n"); + errno = EIO; + return (-1); + } + from_vcn = 0; + } + } +#endif + if (na->ni->nr_extents == -1) + base_ni = na->ni->base_ni; + else + base_ni = na->ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) + return -1; + + /* Fill attribute records with new mapping pairs. */ + stop_vcn = 0; + stop_rl = na->rl; + finished_build = FALSE; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { + a = ctx->attr; + m = ctx->mrec; + if (!a->lowest_vcn) + first_updated = TRUE; + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * contain @from_vcn. Also we do not need @from_vcn anymore, + * set it to 0 to make ntfs_attr_lookup enumerate attributes. + */ + if (from_vcn) { + LCN first_lcn; + + stop_vcn = sle64_to_cpu(a->lowest_vcn); + from_vcn = 0; + /* + * Check whether the first run we need to update is + * the last run in runlist, if so, then deallocate + * all attrubute extents starting this one. + */ + first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); + if (first_lcn == LCN_EINVAL) { + errno = EIO; + ntfs_log_perror("Bad runlist"); + goto put_err_out; + } + if (first_lcn == LCN_ENOENT || + first_lcn == LCN_RL_NOT_MAPPED) + finished_build = TRUE; + } + + /* + * Check whether we finished mapping pairs build, if so mark + * extent as need to delete (by setting highest vcn to + * NTFS_VCN_DELETE_MARK (-2), we shall check it later and + * delete extent) and continue search. + */ + if (finished_build) { + ntfs_log_trace("Mark attr 0x%x for delete in inode " + "%lld.\n", (unsigned)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + continue; + } + + switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) { + case -1: return -1; + case -2: goto retry; + case -3: goto put_err_out; + } + + /* + * Determine maximum possible length of mapping pairs, + * if we shall *not* expand space for mapping pairs. + */ + cur_max_mp_size = le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset); + /* + * Determine maximum possible length of mapping pairs in the + * current mft record, if we shall expand space for mapping + * pairs. + */ + exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; + /* Get the size for the rest of mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl, + stop_vcn, exp_max_mp_size); + if (mp_size <= 0) { + ntfs_log_perror("%s: get MP size failed", __FUNCTION__); + goto put_err_out; + } + /* Test mapping pairs for fitting in the current mft record. */ + if (mp_size > exp_max_mp_size) { + /* + * Mapping pairs of $ATTRIBUTE_LIST attribute must fit + * in the base mft record. Try to move out other + * attributes and try again. + */ + if (na->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, mp_size - + cur_max_mp_size)) { + ntfs_log_perror("Attribute list is too " + "big. Defragment the " + "volume\n"); + return -1; + } + goto retry; + } + + /* Add attribute list if it isn't present, and retry. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(base_ni)) { + ntfs_log_perror("Can not add attrlist"); + return -1; + } + goto retry; + } + + /* + * Set mapping pairs size to maximum possible for this + * mft record. We shall write the rest of mapping pairs + * to another MFT records. + */ + mp_size = exp_max_mp_size; + } + + /* Change space for mapping pairs if we need it. */ + if (((mp_size + 7) & ~7) != cur_max_mp_size) { + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->mapping_pairs_offset) + + mp_size)) { + errno = EIO; + ntfs_log_perror("Failed to resize attribute"); + goto put_err_out; + } + } + + /* Update lowest vcn. */ + a->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + if ((ctx->ntfs_ino->nr_extents == -1 || + NInoAttrList(ctx->ntfs_ino)) && + ctx->attr->type != AT_ATTRIBUTE_LIST) { + ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_attrlist_mark_dirty(ctx->ntfs_ino); + } + + /* + * Generate the new mapping pairs array directly into the + * correct destination, i.e. the attribute record itself. + */ + if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( + a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl)) + finished_build = TRUE; + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (!finished_build && errno != ENOSPC) { + ntfs_log_perror("Failed to build mapping pairs"); + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); + goto put_err_out; + } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } + + /* Deallocate not used attribute extents and return with success. */ + if (finished_build) { + ntfs_attr_reinit_search_ctx(ctx); + ntfs_log_trace("Deallocate marked extents.\n"); + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (sle64_to_cpu(ctx->attr->highest_vcn) != + NTFS_VCN_DELETE_MARK) + continue; + /* Remove unused attribute record. */ + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_perror("Could not remove unused attr"); + goto put_err_out; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (errno != ENOENT) { + ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); + goto put_err_out; + } + ntfs_log_trace("Deallocate done.\n"); + ntfs_attr_put_search_ctx(ctx); + goto ok; + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Allocate new MFT records for the rest of mapping pairs. */ + while (1) { + /* Calculate size of rest mapping pairs. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, + na->rl, stop_vcn, INT_MAX); + if (mp_size <= 0) { + ntfs_log_perror("%s: get mp size failed", __FUNCTION__); + goto put_err_out; + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Could not allocate new MFT record"); + goto put_err_out; + } + m = ni->mrec; + /* + * If mapping size exceed available space, set them to + * possible maximum. + */ + cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) - + (offsetof(ATTR_RECORD, compressed_size) + + (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) ? + sizeof(a->compressed_size) : 0)) - + ((sizeof(ntfschar) * na->name_len + 7) & ~7); + if (mp_size > cur_max_mp_size) + mp_size = cur_max_mp_size; + /* Add attribute extent to new record. */ + err = ntfs_non_resident_attr_record_add(ni, na->type, + na->name, na->name_len, stop_vcn, mp_size, + na->data_flags); + if (err == -1) { + err = errno; + ntfs_log_perror("Could not add attribute extent"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Could not free MFT record"); + errno = err; + goto put_err_out; + } + a = (ATTR_RECORD*)((u8*)m + err); + + err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl); + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (err < 0 && errno != ENOSPC) { + err = errno; + ntfs_log_perror("Failed to build MP"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Couldn't free MFT record"); + errno = err; + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + ntfs_inode_mark_dirty(ni); + /* All mapping pairs has been written. */ + if (!err) + break; + } +ok: + ret = 0; +out: + return ret; +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + goto out; +} +#undef NTFS_VCN_DELETE_MARK + +/** + * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute + * @na: non-resident ntfs open attribute for which we need update + * @from_vcn: update runlist starting this VCN + * + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + * + * FIXME: This function does not update sparse bit and compressed size correctly + * if called with @from_vcn != 0. + * + * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments passed. + * ENOMEM - Not enough memory to complete operation. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST + * or there is no free MFT records left to allocate. + */ +int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + */ +static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) +{ + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + VCN first_free_vcn; + s64 nr_freed_clusters; + int err; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long) + na->ni->mft_no, na->type, (long long)newsize); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding minimum size + * against @newsize and fail if @newsize is too small. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ERANGE) { + ntfs_log_trace("Eeek! Size bounds check failed. " + "Aborting...\n"); + } else if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* The first cluster outside the new allocation. */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only deallocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " + "failed.\n"); + return -1; + } + /* Deallocate all clusters starting with the first free one. */ + nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, + -1); + if (nr_freed_clusters < 0) { + ntfs_log_trace("Eeek! Freeing of clusters failed. " + "Aborting...\n"); + return -1; + } + + /* Truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { + /* + * Failed to truncate the runlist, so just throw it + * away, it will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_trace("Eeek! Run list truncation failed.\n"); + return -1; + } + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; + /* Write mapping pairs for new runlist. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { + ntfs_log_trace("Eeek! Mapping pairs update failed. " + "Leaving inconstant metadata. " + "Run chkdsk.\n"); + return -1; + } + } + + /* Get the first attribute record. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " + "Leaving inconstant metadata.\n"); + goto put_err_out; + } + + /* Update data and initialized size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + if (newsize < na->initialized_size) { + na->initialized_size = newsize; + ctx->attr->initialized_size = cpu_to_sle64(newsize); + } + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + + /* If the attribute now has zero size, make it resident. */ + if (!newsize) { + if (ntfs_attr_make_resident(na, ctx)) { + /* If couldn't make resident, just continue. */ + if (errno != EPERM) + ntfs_log_error("Failed to make attribute " + "resident. Leaving as is...\n"); + } + } + + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to expand + * @newsize: new size (in bytes) to which to expand the attribute + * + * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, + * by allocating new clusters. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + LCN lcn_seek_from; + VCN first_free_vcn; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + runlist *rl, *rln; + s64 org_alloc_size; + int err; + + ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize, (long long)na->data_size); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding maximum size + * against @newsize and fail if @newsize is too big. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ENOENT) + errno = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + return -1; + } + + if (na->type == AT_DATA) + NAttrSetDataAppending(na); + /* Save for future use. */ + org_alloc_size = na->allocated_size; + /* The first cluster outside the new allocation. */ + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only allocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { +#if PARTIAL_RUNLIST_UPDATING + s64 start_update; + + /* + * Update from the last previously allocated run, + * as we may have to expand an existing hole. + */ + start_update = na->allocated_size >> vol->cluster_size_bits; + if (start_update) + start_update--; + if (ntfs_attr_map_partial_runlist(na, start_update)) { + ntfs_log_perror("failed to map partial runlist"); + return -1; + } +#else + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); + return -1; + } +#endif + + /* + * If we extend $DATA attribute on NTFS 3+ volume, we can add + * sparse runs instead of real allocation of clusters. + */ + if ((na->type == AT_DATA) && (vol->major_ver >= 3) + && (holes != HOLES_NO)) { + rl = ntfs_malloc(0x1000); + if (!rl) + return -1; + + rl[0].vcn = (na->allocated_size >> + vol->cluster_size_bits); + rl[0].lcn = LCN_HOLE; + rl[0].length = first_free_vcn - + (na->allocated_size >> vol->cluster_size_bits); + rl[1].vcn = first_free_vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + } else { + /* + * Determine first after last LCN of attribute. + * We will start seek clusters from this LCN to avoid + * fragmentation. If there are no valid LCNs in the + * attribute let the cluster allocator choose the + * starting LCN. + */ + lcn_seek_from = -1; + if (na->rl->length) { + /* Seek to the last run list element. */ + for (rl = na->rl; (rl + 1)->length; rl++) + ; + /* + * If the last LCN is a hole or similar seek + * back to last valid LCN. + */ + while (rl->lcn < 0 && rl != na->rl) + rl--; + /* + * Only set lcn_seek_from it the LCN is valid. + */ + if (rl->lcn >= 0) + lcn_seek_from = rl->lcn + rl->length; + } + + rl = ntfs_cluster_alloc(vol, na->allocated_size >> + vol->cluster_size_bits, first_free_vcn - + (na->allocated_size >> + vol->cluster_size_bits), lcn_seek_from, + DATA_ZONE); + if (!rl) { + ntfs_log_perror("Cluster allocation failed " + "(%lld)", + (long long)first_free_vcn - + ((long long)na->allocated_size >> + vol->cluster_size_bits)); + return -1; + } + } + + /* Append new clusters to attribute runlist. */ + rln = ntfs_runlists_merge(na->rl, rl); + if (!rln) { + /* Failed, free just allocated clusters. */ + err = errno; + ntfs_log_perror("Run list merge failed"); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + errno = err; + return -1; + } + na->rl = rln; + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; + /* Write mapping pairs for new runlist. */ +#if PARTIAL_RUNLIST_UPDATING + if (ntfs_attr_update_mapping_pairs_i(na, start_update, holes)) { +#else + if (ntfs_attr_update_mapping_pairs(na, 0)) { +#endif + err = errno; + ntfs_log_perror("Mapping pairs update failed"); + goto rollback; + } + } + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + err = errno; + if (na->allocated_size == org_alloc_size) { + errno = err; + return -1; + } else + goto rollback; + } + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + ntfs_log_perror("Lookup of first attribute extent failed"); + if (err == ENOENT) + err = EIO; + if (na->allocated_size != org_alloc_size) { + ntfs_attr_put_search_ctx(ctx); + goto rollback; + } else + goto put_err_out; + } + + /* Update data size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +rollback: + /* Free allocated clusters. */ + if (ntfs_cluster_free(vol, na, org_alloc_size >> + vol->cluster_size_bits, -1) < 0) { + err = EIO; + ntfs_log_perror("Leaking clusters"); + } + /* Now, truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, org_alloc_size >> + vol->cluster_size_bits)) { + /* + * Failed to truncate the runlist, so just throw it away, it + * will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); + } else { + /* Prepare to mapping pairs update. */ + na->allocated_size = org_alloc_size; + /* Restore mapping pairs. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> + vol->cluster_size_bits*/)) { + ntfs_log_perror("Failed to restore old mapping pairs"); + } + } + errno = err; + return -1; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + + +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_non_resident_attr_expand_i(na, newsize, holes); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_truncate - resize an ntfs attribute + * @na: open ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * @holes: how to create a hole if expanding + * + * Change the size of an open ntfs attribute @na to @newsize bytes. If the + * attribute is made bigger and the attribute is resident the newly + * "allocated" space is cleared and if the attribute is non-resident the + * newly allocated space is marked as not initialised and no real allocation + * on disk is performed. + * + * On success return 0. + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EOPNOTSUPP - The desired resize is not implemented yet. + * EACCES - Encrypted attribute. + */ +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret = STATUS_ERROR; + s64 fullsize; + BOOL compressed; + + if (!na || newsize < 0 || + (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return STATUS_ERROR; + } + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + if (na->data_size == newsize) { + ntfs_log_trace("Size is already ok\n"); + ret = STATUS_OK; + goto out; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (na->data_flags & ATTR_IS_ENCRYPTED) { + errno = EACCES; + ntfs_log_trace("Cannot truncate encrypted attribute\n"); + goto out; + } + /* + * TODO: Implement making handling of compressed attributes. + * Currently we can only expand the attribute or delete it, + * and only for ATTR_IS_COMPRESSED. This is however possible + * for resident attributes when there is no open fuse context + * (important case : $INDEX_ROOT:$I30) + */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + if (compressed + && NAttrNonResident(na) + && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { + errno = EOPNOTSUPP; + ntfs_log_perror("Failed to truncate compressed attribute"); + goto out; + } + if (NAttrNonResident(na)) { + /* + * For compressed data, the last block must be fully + * allocated, and we do not know the size of compression + * block until the attribute has been made non-resident. + * Moreover we can only process a single compression + * block at a time (from where we are about to write), + * so we silently do not allocate more. + * + * Note : do not request upsizing of compressed files + * unless being able to face the consequences ! + */ + if (compressed && newsize && (newsize > na->data_size)) + fullsize = (na->initialized_size + | (na->compression_block_size - 1)) + 1; + else + fullsize = newsize; + if (fullsize > na->data_size) + ret = ntfs_non_resident_attr_expand(na, fullsize, + holes); + else + ret = ntfs_non_resident_attr_shrink(na, fullsize); + } else + ret = ntfs_resident_attr_resize(na, newsize); +out: + ntfs_log_leave("Return status %d\n", ret); + return ret; +} + +/* + * Resize an attribute, creating a hole if relevant + */ + +int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_OK)); +} + +/* + * Resize an attribute, avoiding hole creation + */ + +int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_NO)); +} + +/* + * Stuff a hole in a compressed file + * + * An unallocated hole must be aligned on compression block size. + * If needed current block and target block are stuffed with zeroes. + * + * Returns 0 if succeeded, + * -1 if it failed (as explained in errno) + */ + +static int stuff_hole(ntfs_attr *na, const s64 pos) +{ + s64 size; + s64 begin_size; + s64 end_size; + char *buf; + int ret; + + ret = 0; + /* + * If the attribute is resident, the compression block size + * is not defined yet and we can make no decision. + * So we first try resizing to the target and if the + * attribute is still resident, we're done + */ + if (!NAttrNonResident(na)) { + ret = ntfs_resident_attr_resize(na, pos); + if (!ret && !NAttrNonResident(na)) + na->initialized_size = na->data_size = pos; + } + if (!ret && NAttrNonResident(na)) { + /* does the hole span over several compression block ? */ + if ((pos ^ na->initialized_size) + & ~(na->compression_block_size - 1)) { + begin_size = ((na->initialized_size - 1) + | (na->compression_block_size - 1)) + + 1 - na->initialized_size; + end_size = pos & (na->compression_block_size - 1); + size = (begin_size > end_size ? begin_size : end_size); + } else { + /* short stuffing in a single compression block */ + begin_size = size = pos - na->initialized_size; + end_size = 0; + } + if (size) + buf = (char*)ntfs_malloc(size); + else + buf = (char*)NULL; + if (buf || !size) { + memset(buf,0,size); + /* stuff into current block */ + if (begin_size + && (ntfs_attr_pwrite(na, + na->initialized_size, begin_size, buf) + != begin_size)) + ret = -1; + /* create an unstuffed hole */ + if (!ret + && ((na->initialized_size + end_size) < pos) + && ntfs_non_resident_attr_expand(na, + pos - end_size, HOLES_OK)) + ret = -1; + else + na->initialized_size + = na->data_size = pos - end_size; + /* stuff into the target block */ + if (!ret && end_size + && (ntfs_attr_pwrite(na, + na->initialized_size, end_size, buf) + != end_size)) + ret = -1; + if (buf) + free(buf); + } else + ret = -1; + } + /* make absolutely sure we have reached the target */ + if (!ret && (na->initialized_size != pos)) { + ntfs_log_error("Failed to stuff a compressed file" + "target %lld reached %lld\n", + (long long)pos, (long long)na->initialized_size); + errno = EIO; + ret = -1; + } + return (ret); +} + +/** + * ntfs_attr_readall - read the entire data from an ntfs attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * @data_size: if non-NULL then store here the data size + * + * This function will read the entire content of an ntfs attribute. + * If @name is AT_UNNAMED then look specifically for an unnamed attribute. + * If @name is NULL then the attribute could be either named or not. + * In both those cases @name_len is not used at all. + * + * On success a buffer is allocated with the content of the attribute + * and which needs to be freed when it's not needed anymore. If the + * @data_size parameter is non-NULL then the data size is set there. + * + * On error NULL is returned with errno set to the error code. + */ +void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size) +{ + ntfs_attr *na; + void *data, *ret = NULL; + s64 size; + + ntfs_log_enter("Entering\n"); + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + ntfs_log_perror("ntfs_attr_open failed"); + goto err_exit; + } + data = ntfs_malloc(na->data_size); + if (!data) + goto out; + + size = ntfs_attr_pread(na, 0, na->data_size, data); + if (size != na->data_size) { + ntfs_log_perror("ntfs_attr_pread failed"); + free(data); + goto out; + } + ret = data; + if (data_size) + *data_size = size; +out: + ntfs_attr_close(na); +err_exit: + ntfs_log_leave("\n"); + return ret; +} + +/* + * Read some data from a data attribute + * + * Returns the amount of data read, negative if there was an error + */ + +int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)offset < (size_t)na->data_size) { + if (offset + size > (size_t)na->data_size) + size = na->data_size - offset; + while (size) { + res = ntfs_attr_pread(na, offset, size, buf + total); + if ((off_t)res < (off_t)size) + ntfs_log_perror("ntfs_attr_pread partial read " + "(%lld : %lld <> %d)", + (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +/* + * Write some data into a data attribute + * + * Returns the amount of data written, negative if there was an error + */ + +int ntfs_attr_data_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + res = ntfs_attr_pwrite(na, offset, size, buf + total); + if (res < (s64)size) + ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " + "%lld <> %d)", (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + int ret; + + ntfs_log_trace("Entering\n"); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return 0; + + ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, + ctx); + + ntfs_attr_put_search_ctx(ctx); + + return !ret; +} + +int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + ntfs_log_error("%s: NULL inode pointer", __FUNCTION__); + errno = EINVAL; + return -1; + } + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + /* do not log removal of non-existent stream */ + if (type != AT_DATA) { + ntfs_log_perror("Failed to open attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + } + return -1; + } + + ret = ntfs_attr_rm(na); + if (ret) + ntfs_log_perror("Failed to remove attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + ntfs_attr_close(na); + + return ret; +} + +/* Below macros are 32-bit ready. */ +#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) +#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) + +static u8 *ntfs_init_lut256(void) +{ + int i; + u8 *lut; + + lut = ntfs_malloc(256); + if (lut) + for(i = 0; i < 256; i++) + *(lut + i) = 8 - BITCOUNT(i); + return lut; +} + +s64 ntfs_attr_get_free_bits(ntfs_attr *na) +{ + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; + s64 nr_free = 0; + + lut = ntfs_init_lut256(); + if (!lut) + return -1; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; + + while (1) { + u32 *p; + br = ntfs_attr_pread(na, total, 65536, buf); + if (br <= 0) + break; + total += br; + p = (u32 *)buf + br / 4 - 1; + for (; (u8 *)p >= buf; p--) { + nr_free += lut[ *p & 255] + + lut[(*p >> 8) & 255] + + lut[(*p >> 16) & 255] + + lut[(*p >> 24) ]; + } + switch (br % 4) { + case 3: nr_free += lut[*(buf + br - 3)]; + case 2: nr_free += lut[*(buf + br - 2)]; + case 1: nr_free += lut[*(buf + br - 1)]; + } + } + free(buf); +out: + free(lut); + if (!total || br < 0) + return -1; + return nr_free; +} diff --git a/lib/libntfs/orig/source/attrib.h b/lib/libntfs/orig/source/attrib.h new file mode 100644 index 0000000..67fb957 --- /dev/null +++ b/lib/libntfs/orig/source/attrib.h @@ -0,0 +1,394 @@ +/* + * attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006-2007 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRIB_H +#define _NTFS_ATTRIB_H + +/* Forward declarations */ +typedef struct _ntfs_attr ntfs_attr; +typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; + +#include "types.h" +#include "inode.h" +#include "unistr.h" +#include "runlist.h" +#include "volume.h" +#include "debug.h" +#include "logging.h" + +extern ntfschar AT_UNNAMED[]; +extern ntfschar STREAM_SDS[]; + +/* The little endian Unicode string $TXF_DATA as a global constant. */ +extern ntfschar TXF_DATA[10]; + +/** + * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn() + * + * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn(). + * + * TODO: Describe them. + */ +typedef enum { + LCN_HOLE = -1, /* Keep this as highest value or die! */ + LCN_RL_NOT_MAPPED = -2, + LCN_ENOENT = -3, + LCN_EINVAL = -4, + LCN_EIO = -5, +} ntfs_lcn_special_values; + +typedef enum { /* ways of processing holes when expanding */ + HOLES_NO, + HOLES_OK, + HOLES_DELAY +} hole_type; + +/** + * struct ntfs_attr_search_ctx - search context used in attribute search functions + * @mrec: buffer containing mft record to search + * @attr: attribute record in @mrec where to begin/continue search + * @is_first: if true lookup_attr() begins search with @attr, else after @attr + * + * Structure must be initialized to zero before the first call to one of the + * attribute search functions. Initialize @mrec to point to the mft record to + * search, and @attr to point to the first attribute within @mrec (not necessary + * if calling the _first() functions), and set @is_first to TRUE (not necessary + * if calling the _first() functions). + * + * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE, + * the search begins after @attr. This is so that, after the first call to one + * of the search attribute functions, we can call the function again, without + * any modification of the search context, to automagically get the next + * matching attribute. + */ +struct _ntfs_attr_search_ctx { + MFT_RECORD *mrec; + ATTR_RECORD *attr; + BOOL is_first; + ntfs_inode *ntfs_ino; + ATTR_LIST_ENTRY *al_entry; + ntfs_inode *base_ntfs_ino; + MFT_RECORD *base_mrec; + ATTR_RECORD *base_attr; +}; + +extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); +extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, + MFT_RECORD *mrec); +extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); + +extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type); + +/** + * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode + * @ctx: initialised attribute search context + * + * Syntactic sugar for walking attributes in an inode. + * + * Return 0 on success and -1 on error with errno set to the error code from + * ntfs_attr_lookup(). + * + * Example: When you want to enumerate all attributes in an open ntfs inode + * @ni, you can simply do: + * + * int err; + * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); + * if (!ctx) + * // Error code is in errno. Handle this case. + * while (!(err = ntfs_attrs_walk(ctx))) { + * ATTR_RECORD *attr = ctx->attr; + * // attr now contains the next attribute. Do whatever you want + * // with it and then just continue with the while loop. + * } + * if (err && errno != ENOENT) + * // Ooops. An error occurred! You should handle this case. + * // Now finished with all attributes in the inode. + */ +static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) +{ + return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx); +} + +/** + * struct ntfs_attr - ntfs in memory non-resident attribute structure + * @rl: if not NULL, the decompressed runlist + * @ni: base ntfs inode to which this attribute belongs + * @type: attribute type + * @name: Unicode name of the attribute + * @name_len: length of @name in Unicode characters + * @state: NTFS attribute specific flags describing this attribute + * @allocated_size: copy from the attribute record + * @data_size: copy from the attribute record + * @initialized_size: copy from the attribute record + * @compressed_size: copy from the attribute record + * @compression_block_size: size of a compression block (cb) + * @compression_block_size_bits: log2 of the size of a cb + * @compression_block_clusters: number of clusters per cb + * + * This structure exists purely to provide a mechanism of caching the runlist + * of an attribute. If you want to operate on a particular attribute extent, + * you should not be using this structure at all. If you want to work with a + * resident attribute, you should not be using this structure at all. As a + * fail-safe check make sure to test NAttrNonResident() and if it is false, you + * know you shouldn't be using this structure. + * + * If you want to work on a resident attribute or on a specific attribute + * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent) + * record, edit that, and then write back the mft record (or set the + * corresponding ntfs inode dirty for delayed write back). + * + * @rl is the decompressed runlist of the attribute described by this + * structure. Obviously this only makes sense if the attribute is not resident, + * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet + * @rl is NULL, so be prepared to cope with @rl == NULL. + * + * @ni is the base ntfs inode of the attribute described by this structure. + * + * @type is the attribute type (see layout.h for the definition of ATTR_TYPES), + * @name and @name_len are the little endian Unicode name and the name length + * in Unicode characters of the attribute, respectively. + * + * @state contains NTFS attribute specific flags describing this attribute + * structure. See ntfs_attr_state_bits above. + */ +struct _ntfs_attr { + runlist_element *rl; + ntfs_inode *ni; + ATTR_TYPES type; + ATTR_FLAGS data_flags; + ntfschar *name; + u32 name_len; + unsigned long state; + s64 allocated_size; + s64 data_size; + s64 initialized_size; + s64 compressed_size; + u32 compression_block_size; + u8 compression_block_size_bits; + u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ +}; + +/** + * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr + * structure + */ +typedef enum { + NA_Initialized, /* 1: structure is initialized. */ + NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_DataAppending, /* 1: Attribute is being appended to */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ +} ntfs_attr_state_bits; + +#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) +#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) +#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) + +#define NAttrInitialized(na) test_nattr_flag(na, Initialized) +#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) +#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) + +#define NAttrNonResident(na) test_nattr_flag(na, NonResident) +#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) +#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) + +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) + +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) + +#define NAttrDataAppending(na) test_nattr_flag(na, DataAppending) +#define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending) +#define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending) + +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) + +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ +extern void NAttrClear##func_name(ntfs_attr *na); + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) +#undef GenNAttrIno + +/** + * union attr_val - Union of all known attribute values + * + * For convenience. Used in the attr structure. + */ +typedef union { + u8 _default; /* Unnamed u8 to serve as default when just using + a_val without specifying any of the below. */ + STANDARD_INFORMATION std_inf; + ATTR_LIST_ENTRY al_entry; + FILE_NAME_ATTR filename; + OBJECT_ID_ATTR obj_id; + SECURITY_DESCRIPTOR_ATTR sec_desc; + VOLUME_NAME vol_name; + VOLUME_INFORMATION vol_inf; + DATA_ATTR data; + INDEX_ROOT index_root; + INDEX_BLOCK index_blk; + BITMAP_ATTR bmp; + REPARSE_POINT reparse; + EA_INFORMATION ea_inf; + EA_ATTR ea; + PROPERTY_SET property_set; + LOGGED_UTILITY_STREAM logged_util_stream; + EFS_ATTR_HEADER efs; +} attr_val; + +extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, const BOOL encrypted, + const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit); + + /* warning : in the following "name" has to be freeable */ + /* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */ +extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern void ntfs_attr_close(ntfs_attr *na); + +extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b); +extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, + const void *b); +extern int ntfs_attr_pclose(ntfs_attr *na); + +extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size); + +extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, + const s64 bk_cnt, const u32 bk_size, void *dst); +extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, + s64 bk_cnt, const u32 bk_size, void *src); + +extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); +extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); + +extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); +extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); + +extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, + const ATTR_TYPES type, const s64 size); +extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, + const ATTR_TYPES type); +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); +extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); + +extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, u32 size, + ATTR_FLAGS flags); +extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + ATTR_FLAGS flags); +extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, s64 size); +extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask); +extern int ntfs_attr_rm(ntfs_attr *na); + +extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); + +extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size); + +extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); +extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); + +extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); + +extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); +extern int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize); + +/** + * get_attribute_value_length - return the length of the value of an attribute + * @a: pointer to a buffer containing the attribute record + * + * Return the byte size of the attribute value of the attribute @a (as it + * would be after eventual decompression and filling in of holes if sparse). + * If we return 0, check errno. If errno is 0 the actual length was 0, + * otherwise errno describes the error. + * + * FIXME: Describe possible errnos. + */ +extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); + +/** + * get_attribute_value - return the attribute value of an attribute + * @vol: volume on which the attribute is present + * @a: attribute to get the value of + * @b: destination buffer for the attribute value + * + * Make a copy of the attribute value of the attribute @a into the destination + * buffer @b. Note, that the size of @b has to be at least equal to the value + * returned by get_attribute_value_length(@a). + * + * Return number of bytes copied. If this is zero check errno. If errno is 0 + * then nothing was read due to a zero-length attribute value, otherwise + * errno describes the error. + */ +extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b); + +extern void ntfs_attr_name_free(char **name); +extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); +extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); +extern int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset); +extern int ntfs_attr_data_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset); + +#endif /* defined _NTFS_ATTRIB_H */ + diff --git a/lib/libntfs/orig/source/attrlist.c b/lib/libntfs/orig/source/attrlist.c new file mode 100644 index 0000000..9c62f31 --- /dev/null +++ b/lib/libntfs/orig/source/attrlist.c @@ -0,0 +1,314 @@ +/** + * attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "attrlist.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_attrlist_need - check whether inode need attribute list + * @ni: opened ntfs inode for which perform check + * + * Check whether all are attributes belong to one MFT record, in that case + * attribute list is not needed. + * + * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set + * to the error code. If function succeed errno set to 0. The following error + * codes are defined: + * EINVAL - Invalid arguments passed to function or attribute haven't got + * attribute list. + */ +int ntfs_attrlist_need(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Inode haven't got attribute list.\n"); + errno = EINVAL; + return -1; + } + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_attrlist_entry_add - add an attribute list attribute entry + * @ni: opened ntfs inode, which contains that attribute + * @attr: attribute record to add to attribute list + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed to function. + * ENOMEM - Not enough memory to allocate necessary buffers. + * EIO - I/O error occurred or damaged filesystem. + * EEXIST - Such attribute already present in attribute list. + */ +int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ATTR_LIST_ENTRY *ale; + MFT_REF mref; + ntfs_attr *na = NULL; + ntfs_attr_search_ctx *ctx; + u8 *new_al; + int entry_len, entry_offset, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, + (unsigned) le32_to_cpu(attr->type)); + + if (!ni || !attr) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Determine size and allocate memory for new attribute list. */ + entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + attr->name_length + 7) & ~7; + new_al = ntfs_calloc(ni->attr_list_size + entry_len); + if (!new_al) + return -1; + + /* Find place for the new entry. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) + ((u8*)attr + le16_to_cpu(attr->name_offset)) : + AT_UNNAMED, attr->name_length, CASE_SENSITIVE, + (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : + 0, (attr->non_resident) ? NULL : ((u8*)attr + + le16_to_cpu(attr->value_offset)), (attr->non_resident) ? + 0 : le32_to_cpu(attr->value_length), ctx)) { + /* Found some extent, check it to be before new extent. */ + if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { + err = EEXIST; + ntfs_log_trace("Such attribute already present in the " + "attribute list.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* Add new entry after this extent. */ + ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + } else { + /* Check for real errors. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* No previous extents found. */ + ale = ctx->al_entry; + } + /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ + ntfs_attr_put_search_ctx(ctx); + + /* Determine new entry offset. */ + entry_offset = ((u8 *)ale - ni->attr_list); + /* Set pointer to new entry. */ + ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); + /* Zero it to fix valgrind warning. */ + memset(ale, 0, entry_len); + /* Form new entry. */ + ale->type = attr->type; + ale->length = cpu_to_le16(entry_len); + ale->name_length = attr->name_length; + ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); + if (attr->non_resident) + ale->lowest_vcn = attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = mref; + ale->instance = attr->instance; + memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), + attr->name_length * sizeof(ntfschar)); + + /* Resize $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, ni->attr_list, entry_offset); + memcpy(new_al + entry_offset + entry_len, ni->attr_list + + entry_offset, ni->attr_list_size - entry_offset); + + /* Set new runlist. */ + free(ni->attr_list); + ni->attr_list = new_al; + ni->attr_list_size = ni->attr_list_size + entry_len; + NInoAttrListSetDirty(ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} + +/** + * ntfs_attrlist_entry_rm - remove an attribute list attribute entry + * @ctx: attribute search context describing the attribute list entry + * + * Remove the attribute list entry @ctx->al_entry from the attribute list. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) +{ + u8 *new_al; + int new_al_len; + ntfs_inode *base_ni; + ntfs_attr *na; + ATTR_LIST_ENTRY *ale; + int err; + + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + ale = ctx->al_entry; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->al_entry->type), + (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); + + if (!NInoAttrList(base_ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Allocate memory for new attribute list. */ + new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); + new_al = ntfs_calloc(new_al_len); + if (!new_al) + return -1; + + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); + memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( + ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); + + /* Set new runlist. */ + free(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(base_ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} diff --git a/lib/libntfs/orig/source/attrlist.h b/lib/libntfs/orig/source/attrlist.h new file mode 100644 index 0000000..2952e48 --- /dev/null +++ b/lib/libntfs/orig/source/attrlist.h @@ -0,0 +1,51 @@ +/* + * attrlist.h - Exports for attribute list attribute handling. + * Originated from Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRLIST_H +#define _NTFS_ATTRLIST_H + +#include "attrib.h" + +extern int ntfs_attrlist_need(ntfs_inode *ni); + +extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); +extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); + +/** + * ntfs_attrlist_mark_dirty - set the attribute list dirty + * @ni: ntfs inode which base inode contain dirty attribute list + * + * Set the attribute list dirty so it is written out later (at the latest at + * ntfs_inode_close() time). + * + * This function cannot fail. + */ +static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + NInoAttrListSetDirty(ni->base_ni); + else + NInoAttrListSetDirty(ni); +} + +#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/lib/libntfs/orig/source/bit_ops.h b/lib/libntfs/orig/source/bit_ops.h new file mode 100644 index 0000000..762be0b --- /dev/null +++ b/lib/libntfs/orig/source/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/lib/libntfs/orig/source/bitmap.c b/lib/libntfs/orig/source/bitmap.c new file mode 100644 index 0000000..65162a2 --- /dev/null +++ b/lib/libntfs/orig/source/bitmap.c @@ -0,0 +1,300 @@ +/** + * bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2006 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_bit_set - set a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to set + * @new_value: value to set bit to (0 or 1) + * + * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. + */ +void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + if (!bitmap || new_value > 1) + return; + if (!new_value) + bitmap[bit >> 3] &= ~(1 << (bit & 7)); + else + bitmap[bit >> 3] |= (1 << (bit & 7)); +} + +/** + * ntfs_bit_get - get value of a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to get + * + * Get and return the value of the bit @bit in @bitmap (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get(const u8 *bitmap, const u64 bit) +{ + if (!bitmap) + return -1; + return (bitmap[bit >> 3] >> (bit & 7)) & 1; +} + +/** + * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it + * @bitmap: field of bits + * @bit: bit to get/set + * @new_value: value to set bit to (0 or 1) + * + * Return the value of the bit @bit and set it to @new_value (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + register u8 old_bit, shift; + + if (!bitmap || new_value > 1) + return -1; + shift = bit & 7; + old_bit = (bitmap[bit >> 3] >> shift) & 1; + if (new_value != old_bit) + bitmap[bit >> 3] ^= 1 << shift; + return old_bit; +} + +/** + * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na to @value, where @value is either 0 or 1. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, + s64 count, int value) +{ + s64 bufsize, br; + u8 *buf, *lastbyte_buf; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; + + if (!na || start_bit < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); + return -1; + } + + bit = start_bit & 7; + if (bit) + firstbyte = 1; + else + firstbyte = 0; + + /* Calculate the required buffer size in bytes, capping it at 8kiB. */ + bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; + if (bufsize > 8192) + bufsize = 8192; + + buf = ntfs_malloc(bufsize); + if (!buf) + return -1; + + /* Depending on @value, zero or set all bits in the allocated buffer. */ + memset(buf, value ? 0xff : 0, bufsize); + + /* If there is a first partial byte... */ + if (bit) { + /* read it in... */ + br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); + if (br != 1) { + if (br >= 0) + errno = EIO; + goto free_err_out; + } + /* and set or clear the appropriate bits in it. */ + while ((bit & 7) && count--) { + if (value) + *buf |= 1 << bit++; + else + *buf &= ~(1 << bit++); + } + /* Update @start_bit to the new position. */ + start_bit = (start_bit + 7) & ~7; + } + + /* Loop until @count reaches zero. */ + lastbyte = 0; + lastbyte_buf = NULL; + bit = count & 7; + do { + /* If there is a last partial byte... */ + if (count > 0 && bit) { + lastbyte_pos = ((count + 7) >> 3) + firstbyte; + if (!lastbyte_pos) { + // FIXME: Eeek! BUG! + ntfs_log_error("Lastbyte is zero. Leaving " + "inconsistent metadata.\n"); + errno = EIO; + goto free_err_out; + } + /* and it is in the currently loaded bitmap window... */ + if (lastbyte_pos <= bufsize) { + lastbyte_buf = buf + lastbyte_pos - 1; + + /* read the byte in... */ + br = ntfs_attr_pread(na, (start_bit + count) >> + 3, 1, lastbyte_buf); + if (br != 1) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); + goto free_err_out; + } + /* and set/clear the appropriate bits in it. */ + while (bit && count--) { + if (value) + *lastbyte_buf |= 1 << --bit; + else + *lastbyte_buf &= ~(1 << --bit); + } + /* We don't want to come back here... */ + bit = 0; + /* We have a last byte that we have handled. */ + lastbyte = 1; + } + } + + /* Write the prepared buffer to disk. */ + tmp = (start_bit >> 3) - firstbyte; + br = ntfs_attr_pwrite(na, tmp, bufsize, buf); + if (br != bufsize) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); + goto free_err_out; + } + + /* Update counters. */ + tmp = (bufsize - firstbyte - lastbyte) << 3; + if (firstbyte) { + firstbyte = 0; + /* + * Re-set the partial first byte so a subsequent write + * of the buffer does not have stale, incorrect bits. + */ + *buf = value ? 0xff : 0; + } + start_bit += tmp; + count -= tmp; + if (bufsize > (tmp = (count + 7) >> 3)) + bufsize = tmp; + + if (lastbyte && count != 0) { + // FIXME: Eeek! BUG! + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); + errno = EIO; + goto free_err_out; + } + } while (count > 0); + + ret = 0; + +free_err_out: + free(buf); + return ret; +} + +/** + * ntfs_bitmap_set_run - set a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_bitmap_clear_run - clear a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to clear + * @count: number of bits to clear + * + * Clear @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; +} + diff --git a/lib/libntfs/orig/source/bitmap.h b/lib/libntfs/orig/source/bitmap.h new file mode 100644 index 0000000..10b5f6c --- /dev/null +++ b/lib/libntfs/orig/source/bitmap.h @@ -0,0 +1,96 @@ +/* + * bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BITMAP_H +#define _NTFS_BITMAP_H + +#include "types.h" +#include "attrib.h" + +/* + * NOTES: + * + * - Operations are 8-bit only to ensure the functions work both on little + * and big endian machines! So don't make them 32-bit ops! + * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1. + * - _Caller_ has to make sure that the bit to operate on is less than the + * size of the bitmap. + */ + +extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern char ntfs_bit_get(const u8 *bitmap, const u64 bit); +extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); +extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); + +/** + * ntfs_bitmap_set_bit - set a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to set + * + * Set the @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_set_run(na, bit, 1); +} + +/** + * ntfs_bitmap_clear_bit - clear a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to clear + * + * Clear @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_clear_run(na, bit, 1); +} + +/* + * rol32 - rotate a 32-bit value left + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/* + * ror32 - rotate a 32-bit value right + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +#endif /* defined _NTFS_BITMAP_H */ + diff --git a/lib/libntfs/orig/source/bootsect.c b/lib/libntfs/orig/source/bootsect.c new file mode 100644 index 0000000..e9bea37 --- /dev/null +++ b/lib/libntfs/orig/source/bootsect.c @@ -0,0 +1,285 @@ +/** + * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2003-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "compat.h" +#include "bootsect.h" +#include "debug.h" +#include "logging.h" + +/** + * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector + * @b: buffer containing putative boot sector to analyze + * @silent: if zero, output progress messages to stderr + * + * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b + * must be at least 512 bytes in size. + * + * If @silent is zero, output progress messages to stderr. Otherwise, do not + * output any messages (except when configured with --enable-debug in which + * case warning/debug messages may be displayed). + * + * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. + */ +BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) +{ + u32 i; + BOOL ret = FALSE; + + ntfs_log_debug("Beginning bootsector check.\n"); + + ntfs_log_debug("Checking OEMid, NTFS signature.\n"); + if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ + ntfs_log_error("NTFS signature is missing.\n"); + goto not_ntfs; + } + + ntfs_log_debug("Checking bytes per sector.\n"); + if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || + le16_to_cpu(b->bpb.bytes_per_sector) > 4096) { + ntfs_log_error("Unexpected bytes per sector value (%d).\n", + le16_to_cpu(b->bpb.bytes_per_sector)); + goto not_ntfs; + } + + ntfs_log_debug("Checking sectors per cluster.\n"); + switch (b->bpb.sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + break; + default: + ntfs_log_error("Unexpected sectors per cluster value (%d).\n", + b->bpb.sectors_per_cluster); + goto not_ntfs; + } + + ntfs_log_debug("Checking cluster size.\n"); + i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * + b->bpb.sectors_per_cluster; + if (i > 65536) { + ntfs_log_error("Unexpected cluster size (%d).\n", i); + goto not_ntfs; + } + + ntfs_log_debug("Checking reserved fields are zero.\n"); + if (le16_to_cpu(b->bpb.reserved_sectors) || + le16_to_cpu(b->bpb.root_entries) || + le16_to_cpu(b->bpb.sectors) || + le16_to_cpu(b->bpb.sectors_per_fat) || + le32_to_cpu(b->bpb.large_sectors) || + b->bpb.fats) { + ntfs_log_error("Reserved fields aren't zero " + "(%d, %d, %d, %d, %d, %d).\n", + le16_to_cpu(b->bpb.reserved_sectors), + le16_to_cpu(b->bpb.root_entries), + le16_to_cpu(b->bpb.sectors), + le16_to_cpu(b->bpb.sectors_per_fat), + le32_to_cpu(b->bpb.large_sectors), + b->bpb.fats); + goto not_ntfs; + } + + ntfs_log_debug("Checking clusters per mft record.\n"); + if ((u8)b->clusters_per_mft_record < 0xe1 || + (u8)b->clusters_per_mft_record > 0xf7) { + switch (b->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per mft record " + "(%d).\n", b->clusters_per_mft_record); + goto not_ntfs; + } + } + + ntfs_log_debug("Checking clusters per index block.\n"); + if ((u8)b->clusters_per_index_record < 0xe1 || + (u8)b->clusters_per_index_record > 0xf7) { + switch (b->clusters_per_index_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per index record " + "(%d).\n", b->clusters_per_index_record); + goto not_ntfs; + } + } + + if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) + ntfs_log_debug("Warning: Bootsector has invalid end of sector " + "marker.\n"); + + ntfs_log_debug("Bootsector check completed successfully.\n"); + + ret = TRUE; +not_ntfs: + return ret; +} + +static const char *last_sector_error = +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; + +/** + * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector + * @vol: ntfs_volume to setup + * @bs: buffer containing ntfs boot sector to parse + * + * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the + * obtained values. + * + * Return 0 on success or -1 on error with errno set to the error code EINVAL. + */ +int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) +{ + s64 sectors; + u8 sectors_per_cluster; + s8 c; + + /* We return -1 with errno = EINVAL on error. */ + errno = EINVAL; + + vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); + vol->sector_size_bits = ffs(vol->sector_size) - 1; + ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); + ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); + /* + * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being + * below or equal the number_of_clusters) really belong in the + * ntfs_boot_sector_is_ntfs but in this way we can just do this once. + */ + sectors_per_cluster = bs->bpb.sectors_per_cluster; + ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); + if (sectors_per_cluster & (sectors_per_cluster - 1)) { + ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." + "\n", sectors_per_cluster); + return -1; + } + + sectors = sle64_to_cpu(bs->number_of_sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); + if (!sectors) { + ntfs_log_error("Volume size is set to zero.\n"); + return -1; + } + if (vol->dev->d_ops->seek(vol->dev, + (sectors - 1) << vol->sector_size_bits, + SEEK_SET) == -1) { + ntfs_log_perror("Failed to read last sector (%lld)", + (long long)sectors); + ntfs_log_error("%s", last_sector_error); + return -1; + } + + vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1); + + vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); + vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); + if (vol->mft_lcn > vol->nr_clusters || + vol->mftmirr_lcn > vol->nr_clusters) { + ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " + "greater than the number of clusters (%lld).\n", + (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, + (long long)vol->nr_clusters); + return -1; + } + + vol->cluster_size = sectors_per_cluster * vol->sector_size; + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("cluster_size (%d) is not a power of 2.\n", + vol->cluster_size); + return -1; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + /* + * Need to get the clusters per mft record and handle it if it is + * negative. Then calculate the mft_record_size. A value of 0x80 is + * illegal, thus signed char is actually ok! + */ + c = bs->clusters_per_mft_record; + ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); + ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); + ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); + /* + * When clusters_per_mft_record is negative, it means that it is to + * be taken to be the negative base 2 logarithm of the mft_record_size + * min bytes. Then: + * mft_record_size = 2^(-clusters_per_mft_record) bytes. + */ + if (c < 0) + vol->mft_record_size = 1 << -c; + else + vol->mft_record_size = c << vol->cluster_size_bits; + if (vol->mft_record_size & (vol->mft_record_size - 1)) { + ntfs_log_error("mft_record_size (%d) is not a power of 2.\n", + vol->mft_record_size); + return -1; + } + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); + ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); + /* Same as above for INDX record. */ + c = bs->clusters_per_index_record; + ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); + if (c < 0) + vol->indx_record_size = 1 << -c; + else + vol->indx_record_size = c << vol->cluster_size_bits; + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); + ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); + /* + * Work out the size of the MFT mirror in number of mft records. If the + * cluster size is less than or equal to the size taken by four mft + * records, the mft mirror stores the first four mft records. If the + * cluster size is bigger than the size taken by four mft records, the + * mft mirror contains as many mft records as will fit into one + * cluster. + */ + if (vol->cluster_size <= 4 * vol->mft_record_size) + vol->mftmirr_size = 4; + else + vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; + return 0; +} + diff --git a/lib/libntfs/orig/source/bootsect.h b/lib/libntfs/orig/source/bootsect.h new file mode 100644 index 0000000..a299e82 --- /dev/null +++ b/lib/libntfs/orig/source/bootsect.h @@ -0,0 +1,42 @@ +/* + * bootsect.h - Exports for bootsector record handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BOOTSECT_H +#define _NTFS_BOOTSECT_H + +#include "types.h" +#include "volume.h" +#include "layout.h" + +/** + * ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume + * @b: buffer containing the boot sector + * + * This function checks the boot sector in @b for describing a valid ntfs + * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. + */ +extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b); +extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs); + +#endif /* defined _NTFS_BOOTSECT_H */ + diff --git a/lib/libntfs/orig/source/cache.c b/lib/libntfs/orig/source/cache.c new file mode 100644 index 0000000..43ff151 --- /dev/null +++ b/lib/libntfs/orig/source/cache.c @@ -0,0 +1,609 @@ +/** + * cache.c : deal with LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "types.h" +#include "security.h" +#include "cache.h" +#include "misc.h" +#include "logging.h" + +/* + * General functions to deal with LRU caches + * + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. + * + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. + * + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. + */ + +/* + * Enter a new hash index, after a new record has been inserted + * + * Do not call when a record has been modified (with no key change) + */ + +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) +{ + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; + + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; + } else { + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Drop a hash index when a record is about to be deleted + */ + +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) +{ + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; + + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Fetch an entry from cache + * + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed + */ + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + } + } + cache->reads++; + } + return (current); +} + +/* + * Enter an inode number into cache + * returns the cache entry or NULL if not possible + */ + +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } + + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ + + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->payload, item->payload, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); +} + +/* + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion + */ + +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) +{ + struct CACHED_GENERIC *previous; + + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } + + +/* + * Invalidate entries in cache + * + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate + * + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. + */ + +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; + + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + previous = (struct CACHED_GENERIC*)NULL; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + previous = current; + current = current->next; + } + } + } + } + return (count); +} + +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) +{ + int count; + + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); +} + +/* + * Free memory allocated to a cache + */ + +static void ntfs_free_cache(struct CACHE_HEADER *cache) +{ + struct CACHED_GENERIC *entry; + + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); + } +} + +/* + * Create a cache + * + * Returns the cache header, or NULL if the cache could not be created + */ + +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) +{ + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; + + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; + + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; ifree_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); +} + +/* + * Create all LRU caches + * + * No error return, if creation is not possible, cacheing will + * just be not available + */ + +void ntfs_create_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); +#endif +#if CACHE_NIDATA_SIZE + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); +#endif +#if CACHE_LOOKUP_SIZE + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); +#endif + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); +#if CACHE_LEGACY_SIZE + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); +#endif +} + +/* + * Free all LRU caches + */ + +void ntfs_free_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + ntfs_free_cache(vol->xinode_cache); +#endif +#if CACHE_NIDATA_SIZE + ntfs_free_cache(vol->nidata_cache); +#endif +#if CACHE_LOOKUP_SIZE + ntfs_free_cache(vol->lookup_cache); +#endif + ntfs_free_cache(vol->securid_cache); +#if CACHE_LEGACY_SIZE + ntfs_free_cache(vol->legacy_cache); +#endif +} diff --git a/lib/libntfs/orig/source/cache.h b/lib/libntfs/orig/source/cache.h new file mode 100644 index 0000000..be63b1a --- /dev/null +++ b/lib/libntfs/orig/source/cache.h @@ -0,0 +1,118 @@ +/* + * cache.h : deal with indexed LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_CACHE_H_ +#define _NTFS_CACHE_H_ + +#include "volume.h" + +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; +} ; + +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; +} ; + +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; +} ; + +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; +} ; + +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 +} ; + +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); + +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; +} ; + +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; +} ; + + /* cast to generic, avoiding gcc warnings */ +#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); + +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); + +#endif /* _NTFS_CACHE_H_ */ + diff --git a/lib/libntfs/orig/source/cache2.c b/lib/libntfs/orig/source/cache2.c new file mode 100644 index 0000000..872f1a5 --- /dev/null +++ b/lib/libntfs/orig/source/cache2.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "cache2.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + NTFS_CACHE* cache; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + ntfs_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _NTFS_cache_destructor (NTFS_CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + _NTFS_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + ntfs_free (cache->cacheEntries[i].cache); + } + ntfs_free (cache->cacheEntries); + ntfs_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +{ + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + NTFS_CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + NTFS_CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + NTFS_CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = _NTFS_cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _NTFS_cache_flush (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _NTFS_cache_invalidate (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + _NTFS_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/lib/libntfs/orig/source/cache2.h b/lib/libntfs/orig/source/cache2.h new file mode 100644 index 0000000..21daca7 --- /dev/null +++ b/lib/libntfs/orig/source/cache2.h @@ -0,0 +1,135 @@ +/* + NTFS_CACHE.h + The NTFS_CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This NTFS_CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the NTFS_CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _CACHE2_H +#define _CACHE2_H + +//#include "common.h" +//#include "disc.h" + +#include +#include +#include +#include +#include + +typedef struct { + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} NTFS_CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + NTFS_CACHE_ENTRY* cacheEntries; +} NTFS_CACHE; + +/* +Read data from a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE, zeroing the sector first +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the NTFS_CACHE +*/ +bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { +// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +/* +Write a full sector to the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { +// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE +*/ +bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); + +/* +Clear out the contents of the NTFS_CACHE without writing any dirty sectors first +*/ +void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); + +#endif // _CACHE_H + diff --git a/lib/libntfs/orig/source/collate.c b/lib/libntfs/orig/source/collate.c new file mode 100644 index 0000000..5f7a015 --- /dev/null +++ b/lib/libntfs/orig/source/collate.c @@ -0,0 +1,271 @@ +/** + * collate.c - NTFS collation handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "index.h" +#include "collate.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" + +/** + * ntfs_collate_binary - Which of two binary objects should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + + ntfs_log_trace("Entering.\n"); + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 4) { + ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); + return NTFS_COLLATION_ERROR; + } + d1 = le32_to_cpup(data1); + d2 = le32_to_cpup(data2); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ + +static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + int len; + const le32 *p1, *p2; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) { + ntfs_log_error("data1_len or data2_len not valid\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while ((d1 == d2) && ((len -= 4) > 0)); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_security_hash - Which of two security descriptors + * should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * JPA compare two security hash keys made of two unsigned le32 + * + * Returns: -1, 0 or 1 depending of how the keys compare + */ +static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + const le32 *p1, *p2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 8) { + ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else { + p1++; + p2++; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else + rc = 0; + } + } + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_file_name - Which of two filenames should be listed first + * @vol: + * @data1: + * @data1_len: unused + * @data2: + * @data2_len: unused + * + * Description... + * + * Returns: + */ +static int ntfs_collate_file_name(ntfs_volume *vol, + const void *data1, const int data1_len __attribute__((unused)), + const void *data2, const int data2_len __attribute__((unused))) +{ + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; + int rc; + + ntfs_log_trace("Entering.\n"); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/* + * Get a pointer to appropriate collation function. + * + * Returns NULL if the needed function is not implemented + */ + +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) +{ + COLLATE collate; + + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; + } + return (collate); +} diff --git a/lib/libntfs/orig/source/collate.h b/lib/libntfs/orig/source/collate.h new file mode 100644 index 0000000..fe38383 --- /dev/null +++ b/lib/libntfs/orig/source/collate.h @@ -0,0 +1,34 @@ +/* + * collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COLLATE_H +#define _NTFS_COLLATE_H + +#include "types.h" +#include "volume.h" + +#define NTFS_COLLATION_ERROR -2 + +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); + +#endif /* _NTFS_COLLATE_H */ diff --git a/lib/libntfs/orig/source/compat.c b/lib/libntfs/orig/source/compat.c new file mode 100644 index 0000000..63114a4 --- /dev/null +++ b/lib/libntfs/orig/source/compat.c @@ -0,0 +1,250 @@ +/** + * compat.c - Tweaks for Windows compatibility + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compat.h" + +#ifndef HAVE_FFS +/** + * ffs - Find the first set bit in an int + * @x: + * + * Description... + * + * Returns: + */ +int ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; +} +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/daemon.c + */ +/* + * Copyright (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +int daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); +} +/* + * End: src/lib/libresolv2/common/bsd/daemon.c + *************************************************************/ +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/strsep.c + */ +/* + * Copyright (c) 1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +/* + * End: src/lib/libresolv2/common/bsd/strsep.c + *************************************************************/ +#endif /* HAVE_STRSEP */ + diff --git a/lib/libntfs/orig/source/compat.h b/lib/libntfs/orig/source/compat.h new file mode 100644 index 0000000..957752a --- /dev/null +++ b/lib/libntfs/orig/source/compat.h @@ -0,0 +1,86 @@ +/* + * compat.h - Tweaks for Windows compatibility. + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2008-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPAT_H +#define _NTFS_COMPAT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifndef HAVE_FFS +extern int ffs(int i); +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif /* HAVE_STRSEP */ + +#ifdef WINDOWS + +#define HAVE_STDIO_H /* mimic config.h */ +#define HAVE_STDARG_H + +#define atoll _atoi64 +#define fdatasync commit +#define __inline__ inline +#define __attribute__(X) /*nothing*/ + +#else /* !defined WINDOWS */ + +#ifndef O_BINARY +#define O_BINARY 0 /* unix is binary by default */ +#endif + +#ifdef GEKKO + +#include "mem_allocate.h" + +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define major(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define minor(dev) ((unsigned int) ((dev) & MINORMASK)) +#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi)) +#define random rand + +#endif /* defined GEKKO */ + +#endif /* defined WINDOWS */ + +#endif /* defined _NTFS_COMPAT_H */ + diff --git a/lib/libntfs/orig/source/compress.c b/lib/libntfs/orig/source/compress.c new file mode 100644 index 0000000..fe6bb66 --- /dev/null +++ b/lib/libntfs/orig/source/compress.c @@ -0,0 +1,1836 @@ +/** + * compress.c - Compressed attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2009-2011 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * A part of the compression algorithm is based on lzhuf.c whose header + * describes the roles of the original authors (with no apparent copyright + * notice, and according to http://home.earthlink.net/~neilbawd/pall.html + * this was put into public domain in 1988 by Haruhiko OKUMURA). + * + * LZHUF.C English version 1.0 + * Based on Japanese version 29-NOV-1988 + * LZSS coded by Haruhiko OKUMURA + * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI + * Edited and translated to English by Kenji RIKITAKE + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "runlist.h" +#include "compress.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +#undef le16_to_cpup +/* the standard le16_to_cpup() crashes for unaligned data on some processors */ +#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8)) + +/** + * enum ntfs_compression_constants - constants used in the compression code + */ +typedef enum { + /* Token types and access mask. */ + NTFS_SYMBOL_TOKEN = 0, + NTFS_PHRASE_TOKEN = 1, + NTFS_TOKEN_MASK = 1, + + /* Compression sub-block constants. */ + NTFS_SB_SIZE_MASK = 0x0fff, + NTFS_SB_SIZE = 0x1000, + NTFS_SB_IS_COMPRESSED = 0x8000, +} ntfs_compression_constants; + +#define THRESHOLD 3 /* minimal match length for compression */ +#define NIL NTFS_SB_SIZE /* End of tree's node */ + +struct COMPRESS_CONTEXT { + const unsigned char *inbuf; + unsigned int len; + unsigned int nbt; + int match_position; + unsigned int match_length; + u16 lson[NTFS_SB_SIZE + 1]; + u16 rson[NTFS_SB_SIZE + 257]; + u16 dad[NTFS_SB_SIZE + 1]; +} ; + +/* + * Initialize the match tree + */ + +static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx) +{ + int i; + + for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++) + pctx->rson[i] = NIL; /* root */ + for (i = 0; i < NTFS_SB_SIZE; i++) + pctx->dad[i] = NIL; /* node */ +} + +/* + * Insert a new node into match tree for quickly locating + * further similar strings + */ + +static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, + unsigned int r) +{ + unsigned int pp; + BOOL less; + BOOL done; + const unsigned char *key; + int c; + unsigned long mxi; + unsigned int mxl; + + mxl = (1 << (16 - pctx->nbt)) + 2; + less = FALSE; + done = FALSE; + key = &pctx->inbuf[r]; + pp = NTFS_SB_SIZE + 1 + key[0]; + pctx->rson[r] = pctx->lson[r] = NIL; + pctx->match_length = 0; + do { + if (!less) { + if (pctx->rson[pp] != NIL) + pp = pctx->rson[pp]; + else { + pctx->rson[pp] = r; + pctx->dad[r] = pp; + done = TRUE; + } + } else { + if (pctx->lson[pp] != NIL) + pp = pctx->lson[pp]; + else { + pctx->lson[pp] = r; + pctx->dad[r] = pp; + done = TRUE; + } + } + if (!done) { + register unsigned long i; + register const unsigned char *p1,*p2; + + i = 1; + mxi = NTFS_SB_SIZE - r; + if (mxi < 2) + less = FALSE; + else { + p1 = key; + p2 = &pctx->inbuf[pp]; + /* this loop has a significant impact on performances */ + do { + } while ((p1[i] == p2[i]) && (++i < mxi)); + less = (i < mxi) && (p1[i] < p2[i]); + } + if (i >= THRESHOLD) { + if (i > pctx->match_length) { + pctx->match_position = + r - pp + 2*NTFS_SB_SIZE - 1; + if ((pctx->match_length = i) > mxl) { + i = pctx->rson[pp]; + pctx->rson[r] = i; + pctx->dad[i] = r; + i = pctx->lson[pp]; + pctx->lson[r] = i; + pctx->dad[i] = r; + i = pctx->dad[pp]; + pctx->dad[r] = i; + if (pctx->rson[i] == pp) + pctx->rson[i] = r; + else + pctx->lson[i] = r; + /* remove pp */ + pctx->dad[pp] = NIL; + done = TRUE; + pctx->match_length = mxl; + } + } else + if ((i == pctx->match_length) + && ((c = (r - pp + 2*NTFS_SB_SIZE - 1)) + < pctx->match_position)) + pctx->match_position = c; + } + } + } while (!done); +} + +/* + * Search for the longest previous string matching the + * current one + * + * Returns the end of the longest current string which matched + * or zero if there was a bug + */ + +static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, + unsigned int rr, int dd) +{ + unsigned int bestlen = 0; + + do { + rr++; + if (pctx->match_length > 0) + pctx->match_length--; + if (!pctx->len) { + ntfs_log_error("compress bug : void run\n"); + goto bug; + } + if (--pctx->len) { + if (rr >= NTFS_SB_SIZE) { + ntfs_log_error("compress bug : buffer overflow\n"); + goto bug; + } + if (((rr + bestlen) < NTFS_SB_SIZE)) { + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) + pctx->nbt++; + ntfs_new_node(pctx,rr); + if (pctx->match_length > bestlen) + bestlen = pctx->match_length; + } else + if (dd > 0) { + rr += dd; + if ((int)pctx->match_length > dd) + pctx->match_length -= dd; + else + pctx->match_length = 0; + if ((int)pctx->len < dd) { + ntfs_log_error("compress bug : run overflows\n"); + goto bug; + } + pctx->len -= dd; + dd = 0; + } + } + } while (dd-- > 0); + return (rr); +bug : + return (0); +} + +/* + * Compress an input block + * + * Returns the size of the compressed block (including header) + * or zero if there was an error + */ + +static unsigned int ntfs_compress_block(const char *inbuf, + unsigned int size, char *outbuf) +{ + struct COMPRESS_CONTEXT *pctx; + char *ptag; + int dd; + unsigned int rr; + unsigned int last_match_length; + unsigned int q; + unsigned int xout; + unsigned int ntag; + + pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT)); + if (pctx) { + pctx->inbuf = (const unsigned char*)inbuf; + ntfs_init_compress_tree(pctx); + xout = 2; + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + rr = 0; + pctx->nbt = 4; + pctx->len = size; + pctx->match_length = 0; + ntfs_new_node(pctx,0); + do { + if (pctx->match_length > pctx->len) + pctx->match_length = pctx->len; + if (pctx->match_length < THRESHOLD) { + pctx->match_length = 1; + if (ntag >= 8) { + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + } + outbuf[xout++] = inbuf[rr]; + ntag++; + } else { + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) + pctx->nbt++; + q = (pctx->match_position << (16 - pctx->nbt)) + + pctx->match_length - THRESHOLD; + if (ntag >= 8) { + ntag = 0; + ptag = &outbuf[xout++]; + *ptag = 0; + } + *ptag |= 1 << ntag++; + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + } + last_match_length = pctx->match_length; + dd = last_match_length; + if (dd-- > 0) { + rr = ntfs_nextmatch(pctx,rr,dd); + if (!rr) + goto bug; + } + /* + * stop if input is exhausted or output has exceeded + * the maximum size. Two extra bytes have to be + * reserved in output buffer, as 3 bytes may be + * output in a loop. + */ + } while ((pctx->len > 0) + && (rr < size) && (xout < (NTFS_SB_SIZE + 2))); + /* uncompressed must be full size, so accept if better */ + if (xout < (NTFS_SB_SIZE + 2)) { + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + memcpy(&outbuf[2],inbuf,size); + if (size < NTFS_SB_SIZE) + memset(&outbuf[size+2],0,NTFS_SB_SIZE - size); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + free(pctx); + } else { + xout = 0; + errno = ENOMEM; + } + return (xout); /* 0 for an error, > size if cannot compress */ +bug : + return (0); +} + +/** + * ntfs_decompress - decompress a compression block into an array of pages + * @dest: buffer to which to write the decompressed data + * @dest_size: size of buffer @dest in bytes + * @cb_start: compression block to decompress + * @cb_size: size of compression block @cb_start in bytes + * + * This decompresses the compression block @cb_start into the destination + * buffer @dest. + * + * @cb_start is a pointer to the compression block which needs decompressing + * and @cb_size is the size of @cb_start in bytes (8-64kiB). + * + * Return 0 if success or -EOVERFLOW on error in the compressed stream. + */ +static int ntfs_decompress(u8 *dest, const u32 dest_size, + u8 *const cb_start, const u32 cb_size) +{ + /* + * Pointers into the compressed data, i.e. the compression block (cb), + * and the therein contained sub-blocks (sb). + */ + u8 *cb_end = cb_start + cb_size; /* End of cb. */ + u8 *cb = cb_start; /* Current position in cb. */ + u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ + u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ + /* Variables for uncompressed data / destination. */ + u8 *dest_end = dest + dest_size; /* End of dest buffer. */ + u8 *dest_sb_start; /* Start of current sub-block in dest. */ + u8 *dest_sb_end; /* End of current sb in dest. */ + /* Variables for tag and token parsing. */ + u8 tag; /* Current tag. */ + int token; /* Loop counter for the eight tokens in tag. */ + + ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); +do_next_sb: + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); + /* + * Have we reached the end of the compression block or the end of the + * decompressed data? The latter can happen for example if the current + * position in the compression block is one byte before its end so the + * first two checks do not detect it. + */ + if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) { + ntfs_log_debug("Completed. Returning success (0).\n"); + return 0; + } + /* Setup offset for the current sub-block destination. */ + dest_sb_start = dest; + dest_sb_end = dest + NTFS_SB_SIZE; + /* Check that we are still within allowed boundaries. */ + if (dest_sb_end > dest_end) + goto return_overflow; + /* Does the minimum size of a compressed sb overflow valid range? */ + if (cb + 6 > cb_end) + goto return_overflow; + /* Setup the current sub-block source pointers and validate range. */ + cb_sb_start = cb; + cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK) + + 3; + if (cb_sb_end > cb_end) + goto return_overflow; + /* Now, we are ready to process the current sub-block (sb). */ + if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) { + ntfs_log_debug("Found uncompressed sub-block.\n"); + /* This sb is not compressed, just copy it into destination. */ + /* Advance source position to first data byte. */ + cb += 2; + /* An uncompressed sb must be full size. */ + if (cb_sb_end - cb != NTFS_SB_SIZE) + goto return_overflow; + /* Copy the block and advance the source position. */ + memcpy(dest, cb, NTFS_SB_SIZE); + cb += NTFS_SB_SIZE; + /* Advance destination position to next sub-block. */ + dest += NTFS_SB_SIZE; + goto do_next_sb; + } + ntfs_log_debug("Found compressed sub-block.\n"); + /* This sb is compressed, decompress it into destination. */ + /* Forward to the first tag in the sub-block. */ + cb += 2; +do_next_tag: + if (cb == cb_sb_end) { + /* Check if the decompressed sub-block was not full-length. */ + if (dest < dest_sb_end) { + int nr_bytes = dest_sb_end - dest; + + ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); + /* Zero remainder and update destination position. */ + memset(dest, 0, nr_bytes); + dest += nr_bytes; + } + /* We have finished the current sub-block. */ + goto do_next_sb; + } + /* Check we are still in range. */ + if (cb > cb_sb_end || dest > dest_sb_end) + goto return_overflow; + /* Get the next tag and advance to first token. */ + tag = *cb++; + /* Parse the eight tokens described by the tag. */ + for (token = 0; token < 8; token++, tag >>= 1) { + u16 lg, pt, length, max_non_overlap; + register u16 i; + u8 *dest_back_addr; + + /* Check if we are done / still in range. */ + if (cb >= cb_sb_end || dest > dest_sb_end) + break; + /* Determine token type and parse appropriately.*/ + if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { + /* + * We have a symbol token, copy the symbol across, and + * advance the source and destination positions. + */ + *dest++ = *cb++; + /* Continue with the next token. */ + continue; + } + /* + * We have a phrase token. Make sure it is not the first tag in + * the sb as this is illegal and would confuse the code below. + */ + if (dest == dest_sb_start) + goto return_overflow; + /* + * Determine the number of bytes to go back (p) and the number + * of bytes to copy (l). We use an optimized algorithm in which + * we first calculate log2(current destination position in sb), + * which allows determination of l and p in O(1) rather than + * O(n). We just need an arch-optimized log2() function now. + */ + lg = 0; + for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) + lg++; + /* Get the phrase token into i. */ + pt = le16_to_cpup((le16*)cb); + /* + * Calculate starting position of the byte sequence in + * the destination using the fact that p = (pt >> (12 - lg)) + 1 + * and make sure we don't go too far back. + */ + dest_back_addr = dest - (pt >> (12 - lg)) - 1; + if (dest_back_addr < dest_sb_start) + goto return_overflow; + /* Now calculate the length of the byte sequence. */ + length = (pt & (0xfff >> lg)) + 3; + /* Verify destination is in range. */ + if (dest + length > dest_sb_end) + goto return_overflow; + /* The number of non-overlapping bytes. */ + max_non_overlap = dest - dest_back_addr; + if (length <= max_non_overlap) { + /* The byte sequence doesn't overlap, just copy it. */ + memcpy(dest, dest_back_addr, length); + /* Advance destination pointer. */ + dest += length; + } else { + /* + * The byte sequence does overlap, copy non-overlapping + * part and then do a slow byte by byte copy for the + * overlapping part. Also, advance the destination + * pointer. + */ + memcpy(dest, dest_back_addr, max_non_overlap); + dest += max_non_overlap; + dest_back_addr += max_non_overlap; + length -= max_non_overlap; + while (length--) + *dest++ = *dest_back_addr++; + } + /* Advance source position and continue with the next token. */ + cb += 2; + } + /* No tokens left in the current tag. Continue with the next tag. */ + goto do_next_tag; +return_overflow: + errno = EOVERFLOW; + ntfs_log_perror("Failed to decompress file"); + return -1; +} + +/** + * ntfs_is_cb_compressed - internal function, do not use + * + * This is a very specialised function determining if a cb is compressed or + * uncompressed. It is assumed that checking for a sparse cb has already been + * performed and that the cb is not sparse. It makes all sorts of other + * assumptions as well and hence it is not useful anywhere other than where it + * is used at the moment. Please, do not make this function available for use + * outside of compress.c as it is bound to confuse people and not do what they + * want. + * + * Return TRUE on errors so that the error will be detected later on in the + * code. Might be a bit confusing to debug but there really should never be + * errors coming from here. + */ +static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl, + VCN cb_start_vcn, int cb_clusters) +{ + /* + * The simplest case: the run starting at @cb_start_vcn contains + * @cb_clusters clusters which are all not sparse, thus the cb is not + * compressed. + */ +restart: + cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); + while (cb_clusters > 0) { + /* Go to the next run. */ + rl++; + /* Map the next runlist fragment if it is not mapped. */ + if (rl->lcn < LCN_HOLE || !rl->length) { + cb_start_vcn = rl->vcn; + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl || rl->lcn < LCN_HOLE || !rl->length) + return TRUE; + /* + * If the runs were merged need to deal with the + * resulting partial run so simply restart. + */ + if (rl->vcn < cb_start_vcn) + goto restart; + } + /* If the current run is sparse, the cb is compressed. */ + if (rl->lcn == LCN_HOLE) + return TRUE; + /* If the whole cb is not sparse, it is not compressed. */ + if (rl->length >= cb_clusters) + return FALSE; + cb_clusters -= rl->length; + }; + /* All cb_clusters were not sparse thus the cb is not compressed. */ + return FALSE; +} + +/** + * ntfs_compressed_attr_pread - read from a compressed attribute + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead. + * + * This function will read @count bytes starting at offset @pos from the + * compressed ntfs attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number + * is lower than @count this means that the read reached end of file or that + * an error was encountered during the read so that the read is partial. + * 0 means end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2; + u64 cb_size_mask; + VCN start_vcn, vcn, end_vcn; + ntfs_volume *vol; + runlist_element *rl; + u8 *dest, *cb, *cb_pos, *cb_end; + u32 cb_size; + int err; + ATTR_FLAGS data_flags; + FILE_ATTR_FLAGS compression; + unsigned int nr_cbs, cb_clusters; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + data_flags = na->data_flags; + compression = na->ni->flags & FILE_ATTR_COMPRESSED; + if (!na || !na->ni || !na->ni->vol || !b + || ((data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED) + || pos < 0 || count < 0) { + errno = EINVAL; + return -1; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na)) { + errno = EACCES; + return -1; + } + if (!count) + return 0; + /* Truncate reads beyond end of attribute. */ + if (pos + count > na->data_size) { + if (pos >= na->data_size) { + return 0; + } + count = na->data_size - pos; + } + /* If it is a resident attribute, simply use ntfs_attr_pread(). */ + if (!NAttrNonResident(na)) + return ntfs_attr_pread(na, pos, count, b); + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > na->initialized_size) { + if (pos >= na->initialized_size) { + memset(b, 0, count); + return count; + } + total2 = pos + count - na->initialized_size; + count -= total2; + memset((u8*)b + count, 0, total2); + } + vol = na->ni->vol; + cb_size = na->compression_block_size; + cb_size_mask = cb_size - 1UL; + cb_clusters = na->compression_block_clusters; + + /* Need a temporary buffer for each loaded compression block. */ + cb = (u8*)ntfs_malloc(cb_size); + if (!cb) + return -1; + + /* Need a temporary buffer for each uncompressed block. */ + dest = (u8*)ntfs_malloc(cb_size); + if (!dest) { + free(cb); + return -1; + } + /* + * The first vcn in the first compression block (cb) which we need to + * decompress. + */ + start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; + /* Offset in the uncompressed cb at which to start reading data. */ + ofs = pos & cb_size_mask; + /* + * The first vcn in the cb after the last cb which we need to + * decompress. + */ + end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> + vol->cluster_size_bits; + /* Number of compression blocks (cbs) in the wanted vcn range. */ + nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> + na->compression_block_size_bits; + cb_end = cb + cb_size; +do_next_cb: + nr_cbs--; + cb_pos = cb; + vcn = start_vcn; + start_vcn += cb_clusters; + + /* Check whether the compression block is sparse. */ + rl = ntfs_attr_find_vcn(na, vcn); + if (!rl || rl->lcn < LCN_HOLE) { + free(cb); + free(dest); + if (total) + return total; + /* FIXME: Do we want EIO or the error code? (AIA) */ + errno = EIO; + return -1; + } + if (rl->lcn == LCN_HOLE) { + /* Sparse cb, zero out destination range overlapping the cb. */ + ntfs_log_debug("Found sparse compression block.\n"); + to_read = min(count, cb_size - ofs); + memset(b, 0, to_read); + ofs = 0; + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { + s64 tdata_size, tinitialized_size; + /* + * Uncompressed cb, read it straight into the destination range + * overlapping the cb. + */ + ntfs_log_debug("Found uncompressed compression block.\n"); + /* + * Read the uncompressed data into the destination buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the data by simply using ntfs_attr_pread(). (-8 + * NOTE: we have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = min(count, cb_size - ofs); + ofs += vcn << vol->cluster_size_bits; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, ofs, to_read, b); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + total += br; + count -= br; + b = (u8*)b + br; + to_read -= br; + ofs += br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + ofs = 0; + } else { + s64 tdata_size, tinitialized_size; + + /* + * Compressed cb, decompress it into the temporary buffer, then + * copy the data to the destination range overlapping the cb. + */ + ntfs_log_debug("Found compressed compression block.\n"); + /* + * Read the compressed data into the temporary buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the raw, compressed data by simply using + * ntfs_attr_pread(). (-8 + * NOTE: We have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = cb_size; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, + (vcn << vol->cluster_size_bits) + + (cb_pos - cb), to_read, cb_pos); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + cb_pos += br; + to_read -= br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + /* Just a precaution. */ + if (cb_pos + 2 <= cb_end) + *(u16*)cb_pos = 0; + ntfs_log_debug("Successfully read the compression block.\n"); + if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { + err = errno; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return -1; + } + to_read = min(count, cb_size - ofs); + memcpy(b, dest + ofs, to_read); + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + ofs = 0; + } + /* Do we have more work to do? */ + if (nr_cbs) + goto do_next_cb; + /* We no longer need the buffers. */ + free(cb); + free(dest); + /* Return number of bytes read. */ + return total + total2; +} + +/* + * Read data from a set of clusters + * + * Returns the amount of data read + */ + +static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, u32 to_read, char *inbuf) +{ + u32 count; + int xgot; + u32 got; + s64 xpos; + BOOL first; + char *xinbuf; + const runlist_element *xrl; + + got = 0; + xrl = rl; + xinbuf = inbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_read - got) < count) + count = to_read - got; + xgot = ntfs_pread(vol->dev, xpos, count, xinbuf); + if (xgot == (int)count) { + got += count; + xpos += count; + xinbuf += count; + xrl++; + } + first = FALSE; + } while ((xgot == (int)count) && (got < to_read)); + return (got); +} + +/* + * Write data to a set of clusters + * + * Returns the amount of data written + */ + +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) +{ + s32 count; + s32 put, xput; + s64 xpos; + BOOL first; + const char *xoutbuf; + const runlist_element *xrl; + + put = 0; + xrl = rl; + xoutbuf = outbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_write - put) < count) + count = to_write - put; + xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf); + if (xput == count) { + put += count; + xpos += count; + xoutbuf += count; + xrl++; + } + first = FALSE; + } while ((xput == count) && (put < to_write)); + return (put); +} + + +/* + * Compress and write a set of blocks + * + * returns the size actually written (rounded to a full cluster) + * or 0 if all zeroes (nothing is written) + * or -1 if could not compress (nothing is written) + * or -2 if there were an irrecoverable error (errno set) + */ + +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) +{ + ntfs_volume *vol; + char *outbuf; + char *pbuf; + u32 compsz; + s32 written; + s32 rounded; + unsigned int clsz; + u32 p; + unsigned int sz; + unsigned int bsz; + BOOL fail; + BOOL allzeroes; + /* a single compressed zero */ + static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ; + /* a couple of compressed zeroes */ + static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ; + /* more compressed zeroes, to be followed by some count */ + static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ; + + vol = na->ni->vol; + written = -1; /* default return */ + clsz = 1 << vol->cluster_size_bits; + /* may need 2 extra bytes per block and 2 more bytes */ + outbuf = (char*)ntfs_malloc(na->compression_block_size + + 2*(na->compression_block_size/NTFS_SB_SIZE) + + 2); + if (outbuf) { + fail = FALSE; + compsz = 0; + allzeroes = TRUE; + for (p=0; (p na->compression_block_size)) + fail = TRUE; + else { + if (allzeroes) { + /* check whether this is all zeroes */ + switch (sz) { + case 4 : + allzeroes = !memcmp( + pbuf,onezero,4); + break; + case 5 : + allzeroes = !memcmp( + pbuf,twozeroes,5); + break; + case 6 : + allzeroes = !memcmp( + pbuf,morezeroes,4); + break; + default : + allzeroes = FALSE; + break; + } + } + compsz += sz; + } + } + if (!fail && !allzeroes) { + /* add a couple of null bytes, space has been checked */ + outbuf[compsz++] = 0; + outbuf[compsz++] = 0; + /* write a full cluster, to avoid partial reading */ + rounded = ((compsz - 1) | (clsz - 1)) + 1; + written = write_clusters(vol, rl, offs, rounded, outbuf); + if (written != rounded) { + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ + ntfs_log_error("error writing compressed data\n"); + errno = EIO; + written = -2; + } + } else + if (!fail) + written = 0; + free(outbuf); + } + return (written); +} + +/* + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid + */ + +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) +{ + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; + + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); +} + +/* + * Free unneeded clusters after overwriting compressed data + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + * + * +======= start of block =====+ + * 0 |A chunk may overflow | <-- rl usedcnt : A + B + * |A on previous block | then B + * |A | + * +-- end of allocated chunk --+ freelength : C + * |B | (incl overflow) + * +== end of compressed data ==+ + * |C | <-- freerl freecnt : C + D + * |C chunk may overflow | + * |C on next block | + * +-- end of allocated chunk --+ + * |D | + * |D chunk may overflow | + * 15 |D on next block | + * +======== end of block ======+ + * + */ + +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) +{ + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; + + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; + + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); + + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; + + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); +} + + +/* + * Free unneeded clusters after compression + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + */ + +static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, + s64 used, s64 reserved, BOOL appending, + VCN *update_from) +{ + s32 freecnt; + s32 usedcnt; + int res; + s64 freelcn; + s64 freevcn; + s32 freelength; + BOOL mergeholes; + BOOL beginhole; + ntfs_volume *vol; + runlist_element *freerl; + + res = -1; /* default return */ + vol = na->ni->vol; + freecnt = (reserved - used) >> vol->cluster_size_bits; + usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; + /* skip entries fully used, if any */ + while (rl->length && (rl->length < usedcnt)) { + usedcnt -= rl->length; /* must be > 0 */ + rl++; + } + if (rl->length) { + /* + * Splitting the current allocation block requires + * an extra runlist element to create the hole. + * The required entry has been prereserved when + * mapping the runlist. + */ + /* get the free part in initial run */ + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + /* new count of allocated clusters */ + if (!((freevcn + freecnt) + & (na->compression_block_clusters - 1))) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; + + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { + /* + * move the unused part to the end. Doing so, + * the vcn will be out of order. This does + * not harm, the vcn are meaningless now, and + * only the lcn are meaningful for freeing. + */ + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; + } else { + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { + freerl--; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; + } + } + } else { + ntfs_log_error("Bad end of a compression block set\n"); + errno = EIO; + } + } else { + ntfs_log_error("No cluster to free after compression\n"); + errno = EIO; + } + return (res); +} + +/* + * Read existing data, decompress and append buffer + * Do nothing if something fails + */ + +static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, + s64 offs, u32 compsz, s32 pos, BOOL appending, + char *outbuf, s64 to_write, const void *b) +{ + int fail = 1; + char *compbuf; + u32 decompsz; + u32 got; + + if (compsz == na->compression_block_size) { + /* if the full block was requested, it was a hole */ + memset(outbuf,0,compsz); + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } else { + compbuf = (char*)ntfs_malloc(compsz); + if (compbuf) { + /* must align to full block for decompression */ + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; + got = read_clusters(na->ni->vol, rl, offs, + compsz, compbuf); + if ((got == compsz) + && !ntfs_decompress((u8*)outbuf,decompsz, + (u8*)compbuf,compsz)) { + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } + free(compbuf); + } + } + return (fail); +} + +/* + * Flush a full compression block + * + * returns the size actually written (rounded to a full cluster) + * or 0 if could not compress (and written uncompressed) + * or -1 if there were an irrecoverable error (errno set) + */ + +static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) +{ + int rounded; + int written; + int clsz; + + if (compress) { + written = ntfs_comp_set(na, rl, offs, count, outbuf); + if (written == -1) + compress = FALSE; + if ((written >= 0) + && ntfs_compress_free(na,rl,offs + written, + offs + na->compression_block_size, appending, + update_from)) + written = -1; + } else + written = 0; + if (!compress) { + clsz = 1 << na->ni->vol->cluster_size_bits; + rounded = ((count - 1) | (clsz - 1)) + 1; + written = write_clusters(na->ni->vol, rl, + offs, rounded, outbuf); + if (written != rounded) + written = -1; + } + return (written); +} + +/* + * Write some data to be compressed. + * Compression only occurs when a few clusters (usually 16) are + * full. When this occurs an extra runlist slot may be needed, so + * it has to be reserved beforehand. + * + * Returns the size of uncompressed data written, + * or negative if an error occurred. + * When the returned size is less than requested, new clusters have + * to be allocated before the function is called again. + */ + +s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 to_flush; + s64 roffs; + s64 got; + s64 start_vcn; + s64 nextblock; + s64 endwrite; + u32 compsz; + char *inbuf; + char *outbuf; + BOOL fail; + BOOL done; + BOOL compress; + BOOL appending; + + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + compress = FALSE; + done = FALSE; + /* + * Cannot accept writing beyond the current compression set + * because when compression occurs, clusters are freed + * and have to be reallocated. + * (cannot happen with standard fuse 4K buffers) + * Caller has to avoid this situation, or face consequences. + */ + nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) + | (na->compression_block_size - 1)) + 1; + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { + /* it is time to compress */ + compress = TRUE; + /* only process what we can */ + to_write = rounded = nextblock + - (offs + (wrl->vcn << vol->cluster_size_bits)); + } + start_vcn = 0; + fail = FALSE; + brl = wrl; + roffs = 0; + /* + * If we are about to compress or we need to decompress + * existing data, we have to process a full set of blocks. + * So relocate the parameters to the beginning of allocation + * containing the first byte of the set of blocks. + */ + if (compress || compressed_part) { + /* find the beginning of block */ + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + while (brl->vcn && (brl->vcn > start_vcn)) { + /* jumping back a hole means big trouble */ + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when appending\n"); + fail = TRUE; + errno = EIO; + } + brl--; + offs += brl->length << vol->cluster_size_bits; + } + roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + } + if (compressed_part && !fail) { + /* + * The set of compression blocks contains compressed data + * (we are reopening an existing file to append to it) + * Decompress the data and append + */ + compsz = compressed_part << vol->cluster_size_bits; + outbuf = (char*)ntfs_malloc(na->compression_block_size); + if (outbuf) { + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } + if (!ntfs_read_append(na, brl, roffs, compsz, + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { + written = ntfs_flush(na, brl, roffs, + outbuf, to_flush, compress, appending, + update_from); + if (written >= 0) { + written = to_write; + done = TRUE; + } + } + free(outbuf); + } + } else { + if (compress && !fail) { + /* + * we are filling up a block, read the full set + * of blocks and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + to_read = offs - roffs; + if (to_read) + got = read_clusters(vol, brl, roffs, + to_read, inbuf); + else + got = 0; + if (got == to_read) { + memcpy(&inbuf[to_read],b,to_write); + written = ntfs_comp_set(na, brl, roffs, + to_read + to_write, inbuf); + /* + * if compression was not successful, + * only write the part which was requested + */ + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + + roffs, + appending, update_from)) { + done = TRUE; + written = to_write; + } + } + free(inbuf); + } + } + if (!done) { + /* + * if the compression block is not full, or + * if compression failed for whatever reason, + * write uncompressed + */ + /* check we are not overflowing current allocation */ + if ((wpos + rounded) + > ((wrl->lcn + wrl->length) + << vol->cluster_size_bits)) { + ntfs_log_error("writing on unallocated clusters\n"); + errno = EIO; + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounded, b); + if (written == rounded) + written = to_write; + } + } + } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; + return (written); +} + +/* + * Close a file written compressed. + * This compresses the last partial compression block of the file. + * Two empty runlist slots have to be reserved beforehand. + * + * Returns zero if closing is successful. + */ + +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 roffs; + s64 got; + s64 start_vcn; + char *inbuf; + BOOL fail; + BOOL done; + + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + done = FALSE; + /* + * There generally is an uncompressed block at end of file, + * read the full block and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); + brl = wrl; + fail = FALSE; + while (brl->vcn && (brl->vcn > start_vcn)) { + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when closing\n"); + fail = TRUE; + errno = EIO; + } + brl--; + } + if (!fail) { + /* roffs can be an offset from another uncomp block */ + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; + if (to_read) { + got = read_clusters(vol, brl, roffs, to_read, + inbuf); + if (got == to_read) { + written = ntfs_comp_set(na, brl, roffs, + to_read, inbuf); + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + roffs, + TRUE, update_from)) { + done = TRUE; + } else + /* if compression failed, leave uncompressed */ + if (written == -1) + done = TRUE; + } + } else + done = TRUE; + free(inbuf); + } + } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; + return (!done); +} diff --git a/lib/libntfs/orig/source/compress.h b/lib/libntfs/orig/source/compress.h new file mode 100644 index 0000000..c256932 --- /dev/null +++ b/lib/libntfs/orig/source/compress.h @@ -0,0 +1,41 @@ +/* + * compress.h - Exports for compressed attribute handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPRESS_H +#define _NTFS_COMPRESS_H + +#include "types.h" +#include "attrib.h" + +extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, + void *b); + +extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from); + +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); + +#endif /* defined _NTFS_COMPRESS_H */ + diff --git a/lib/libntfs/orig/source/config.h b/lib/libntfs/orig/source/config.h new file mode 100644 index 0000000..a0a5da0 --- /dev/null +++ b/lib/libntfs/orig/source/config.h @@ -0,0 +1,388 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +#undef ENABLE_CRYPTO + +/* Define to 1 if debug should be enabled */ +#undef ENABLE_DEBUG + +/* Define to 1 if the nfconv patch should be enabled */ +#undef ENABLE_NFCONV + +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +#undef ENABLE_UUID + +/* Define to 1 if using internal fuse */ +#undef FUSE_INTERNAL + +/* Define to 1 if you have the `atexit' function. */ +#define HAVE_ATEXIT 1 + +/* Define to 1 if you have the `basename' function. */ +#undef HAVE_BASENAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the `daemon' function. */ +#undef HAVE_DAEMON + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the header file. */ +#undef HAVE_FEATURES_H + +/* Define to 1 if you have the `ffs' function. */ +#define HAVE_FFS 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getmntent' function. */ +#undef HAVE_GETMNTENT + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `hasmntopt' function. */ +#undef HAVE_HASMNTOPT + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBGEN_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBINTL_H + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_FD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_HDREG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_MAJOR_H + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MACHINE_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if mbrtowc and mbstate_t are properly declared. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbsinit' function. */ +#define HAVE_MBSINIT 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MNTENT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setxattr' function. */ +#undef HAVE_SETXATTR + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#define HAVE_STAT_EMPTY_STRING_BUG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if `st_atim' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM + +/* Define to 1 if `st_atimensec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC + +/* Define to 1 if `st_atimespec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC + +/* Define to 1 if `st_blocks' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define to 1 if `st_rdev' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_BYTEORDER_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MKDEV_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSMACROS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_VFS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Don't update /etc/mtab */ +#undef IGNORE_MTAB + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Don't use default IO ops */ +#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +/* Name of package */ +#define PACKAGE "ntfs-3g" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "ntfs-3g" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "ntfs-3g 2011.4.12" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "ntfs-3g" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2011.4.12" + +/* POSIX ACL support */ +#undef POSIXACLS + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + +/* Version number of package */ +#define VERSION "2011.4.12" + +/* Define to 1 if this is a Windows OS */ +#undef WINDOWS + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#define WORDS_BIGENDIAN 1 + +/* Define to 1 if your processor stores words with the least significant byte + first (like Intel and VAX, unlike Motorola and SPARC). */ +#undef WORDS_LITTLEENDIAN + +/* system extended attributes mappings */ +#undef XATTR_MAPPINGS + +/* Number of bits in a file offset, on hosts where this is settable. */ +#define _FILE_OFFSET_BITS 64 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +/* Define for large files, on AIX-style hosts. */ +#define _LARGE_FILES 1 + +/* Required define if using POSIX threads */ +#undef _REENTRANT + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline __inline__ +#endif + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/lib/libntfs/orig/source/debug.c b/lib/libntfs/orig/source/debug.c new file mode 100644 index 0000000..f193483 --- /dev/null +++ b/lib/libntfs/orig/source/debug.c @@ -0,0 +1,79 @@ +/** + * debug.c - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "runlist.h" +#include "debug.h" +#include "logging.h" + +#ifdef DEBUG +/** + * ntfs_debug_runlist_dump - Dump a runlist. + * @rl: + * + * Description... + * + * Returns: + */ +void ntfs_debug_runlist_dump(const runlist_element *rl) +{ + int i = 0; + const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", + "LCN_ENOENT ", "LCN_EINVAL ", + "LCN_unknown " }; + + ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); + if (!rl) { + ntfs_log_debug("Run list not present.\n"); + return; + } + ntfs_log_debug("VCN LCN Run length\n"); + do { + LCN lcn = (rl + i)->lcn; + + if (lcn < (LCN)0) { + int idx = -lcn - 1; + + if (idx > -LCN_EINVAL - 1) + idx = 4; + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } else + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } while (rl[i++].length); +} + +#endif + diff --git a/lib/libntfs/orig/source/debug.h b/lib/libntfs/orig/source/debug.h new file mode 100644 index 0000000..cf39b62 --- /dev/null +++ b/lib/libntfs/orig/source/debug.h @@ -0,0 +1,47 @@ +/* + * debug.h - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEBUG_H +#define _NTFS_DEBUG_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "logging.h" + +struct _runlist_element; + +#ifdef DEBUG +extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); +#else +static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} +#endif + +#define NTFS_BUG(msg) \ +{ \ + int ___i; \ + ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ + ntfs_log_debug("Forcing segmentation fault!"); \ + ___i = ((int*)NULL)[1]; \ +} + +#endif /* defined _NTFS_DEBUG_H */ diff --git a/lib/libntfs/orig/source/device.c b/lib/libntfs/orig/source/device.c new file mode 100644 index 0000000..db84010 --- /dev/null +++ b/lib/libntfs/orig/source/device.c @@ -0,0 +1,753 @@ +/** + * device.c - Low level device io functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2006 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_LINUX_HDREG_H +#include +#endif + +#include "types.h" +#include "mst.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#endif +#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#endif +#if defined(linux) && !defined(HDIO_GETGEO) +#define HDIO_GETGEO 0x0301 /* Get device geometry. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKSSZGET) +# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKBSZSET) +# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ +#endif + +/** + * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it + * @name: name of the device (must be present) + * @state: initial device state (usually zero) + * @dops: ntfs device operations to use with the device (must be present) + * @priv_data: pointer to private data (optional) + * + * Allocate an ntfs device structure and pre-initialize it with the user- + * specified device operations @dops, device state @state, device name @name, + * and optional private data @priv_data. + * + * Note, @name is copied and can hence be freed after this functions returns. + * + * On success return a pointer to the allocated ntfs device structure and on + * error return NULL with errno set to the error code returned by ntfs_malloc(). + */ +struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data) +{ + struct ntfs_device *dev; + + if (!name) { + errno = EINVAL; + return NULL; + } + + dev = ntfs_malloc(sizeof(struct ntfs_device)); + if (dev) { + if (!(dev->d_name = strdup(name))) { + int eo = errno; + free(dev); + errno = eo; + return NULL; + } + dev->d_ops = dops; + dev->d_state = state; + dev->d_private = priv_data; + } + return dev; +} + +/** + * ntfs_device_free - free an ntfs device structure + * @dev: ntfs device structure to free + * + * Free the ntfs device structure @dev. + * + * Return 0 on success or -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid pointer @dev. + * EBUSY Device is still open. Close it before freeing it! + */ +int ntfs_device_free(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + free(dev->d_name); + free(dev); + return 0; +} + +/* + * Sync the device + * + * returns zero if successful. + */ + +int ntfs_device_sync(struct ntfs_device *dev) +{ + int ret; + struct ntfs_device_operations *dops; + + if (NDevDirty(dev)) { + dops = dev->d_ops; + ret = dops->sync(dev); + } else + ret = 0; + return ret; +} + +/** + * ntfs_pread - positioned read from disk + * @dev: device to read from + * @pos: position in device to read from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes from device @dev at position @pos into + * the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that we have either reached end of file or + * encountered an error during the read so that the read is partial. 0 means + * end of file or nothing to read (@count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of either seek, read, or set to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) +{ + s64 br, total; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + + dops = dev->d_ops; + + for (total = 0; count; count -= br, total += br) { + br = dops->pread(dev, (char*)b + total, count, pos + total); + /* If everything ok, continue. */ + if (br > 0) + continue; + /* If EOF or error return number of bytes read. */ + if (!br || total) + return total; + /* Nothing read and error, return error status. */ + return br; + } + /* Finally, return the number of bytes read. */ + return total; +} + +/** + * ntfs_pwrite - positioned write to disk + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the device @dev + * at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + */ +s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b) +{ + s64 written, total, ret = -1; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + goto out; + } + if (!count) + return 0; + if (NDevReadOnly(dev)) { + errno = EROFS; + goto out; + } + + dops = dev->d_ops; + + NDevSetDirty(dev); + for (total = 0; count; count -= written, total += written) { + written = dops->pwrite(dev, (const char*)b + total, count, + pos + total); + /* If everything ok, continue. */ + if (written > 0) + continue; + /* + * If nothing written or error return number of bytes written. + */ + if (!written || total) + break; + /* Nothing written and error, return error status. */ + total = written; + break; + } + if (NDevSync(dev) && total && dops->sync(dev)) { + total--; /* on sync error, return partially written */ + } + ret = total; +out: + return ret; +} + +/** + * ntfs_mst_pread - multi sector transfer (mst) positioned read + * @dev: device to read from + * @pos: position in file descriptor to read from + * @count: number of blocks to read + * @bksize: size of each block that needs mst deprotecting + * @b: output data buffer + * + * Multi sector transfer (mst) positioned read. This function will read @count + * blocks of size @bksize bytes each from device @dev at position @pos into the + * the data buffer @b. + * + * On success, return the number of successfully read blocks. If this number is + * lower than @count this means that we have reached end of file, that the read + * was interrupted, or that an error was encountered during the read so that + * the read is partial. 0 means end of file or nothing was read (also return 0 + * when @count or @bksize are 0). + * + * On error and nothing was read, return -1 with errno set appropriately to the + * return code of either seek, read, or set to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer has been detected the magic + * will have been changed to magic_BAAD but no error will be returned. Thus it + * is possible that we return count blocks as being read but that any number + * (between zero and count!) of these blocks is actually subject to a multi + * sector transfer error. This should be detected by the caller by checking for + * the magic being "BAAD". + */ +s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 br, i; + + if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + /* Do the read. */ + br = ntfs_pread(dev, pos, count * bksize, b); + if (br < 0) + return br; + /* + * Apply fixups to successfully read data, disregarding any errors + * returned from the MST fixup function. This is because we want to + * fixup everything possible and we rely on the fact that the "BAAD" + * magic will be detected later on. + */ + count = br / bksize; + for (i = 0; i < count; ++i) + ntfs_mst_post_read_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + /* Finally, return the number of complete blocks read. */ + return count; +} + +/** + * ntfs_mst_pwrite - multi sector transfer (mst) positioned write + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of blocks to write + * @bksize: size of each block that needs mst protecting + * @b: data buffer to write to disk + * + * Multi sector transfer (mst) positioned write. This function will write + * @count blocks of size @bksize bytes each from data buffer @b to the device + * @dev at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @count this means that the write has been interrupted or that + * an error was encountered during the write so that the write is partial. 0 + * means nothing was written (also return 0 when @count or @bksize are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 written, i; + + if (count < 0 || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < count; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + if (err < 0) { + /* Abort write at this position. */ + if (!i) + return err; + count = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_pwrite(dev, pos, count * bksize, b); + /* Quickly deprotect the data again. */ + for (i = 0; i < count; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bksize; +} + +/** + * ntfs_cluster_read - read ntfs clusters + * @vol: volume to read from + * @lcn: starting logical cluster number + * @count: number of clusters to read + * @b: output data buffer + * + * Read @count ntfs clusters starting at logical cluster number @lcn from + * volume @vol into buffer @b. Return number of clusters read or -1 on error, + * with errno set to the error code. + */ +s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, + void *b) +{ + s64 br; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to read outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + if (br < 0) { + ntfs_log_perror("Error reading cluster(s)"); + return br; + } + return br >> vol->cluster_size_bits; +} + +/** + * ntfs_cluster_write - write ntfs clusters + * @vol: volume to write to + * @lcn: starting logical cluster number + * @count: number of clusters to write + * @b: data buffer to write to disk + * + * Write @count ntfs clusters starting at logical cluster number @lcn from + * buffer @b to volume @vol. Return the number of clusters written or -1 on + * error, with errno set to the error code. + */ +s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b) +{ + s64 bw; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to write outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + if (!NVolReadOnly(vol)) + bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + else + bw = count << vol->cluster_size_bits; + if (bw < 0) { + ntfs_log_perror("Error writing cluster(s)"); + return bw; + } + return bw >> vol->cluster_size_bits; +} + +/** + * ntfs_device_offset_valid - test if a device offset is valid + * @dev: open device + * @ofs: offset to test for validity + * + * Test if the offset @ofs is an existing location on the device described + * by the open device structure @dev. + * + * Return 0 if it is valid and -1 if it is not valid. + */ +static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) +{ + char ch; + + if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && + dev->d_ops->read(dev, &ch, 1) == 1) + return 0; + return -1; +} + +/** + * ntfs_device_size_get - return the size of a device in blocks + * @dev: open device + * @block_size: block size in bytes in which to return the result + * + * Return the number of @block_size sized blocks in the device described by the + * open device @dev. + * + * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. + * + * On error return -1 with errno set to the error code. + */ +s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) +{ + s64 high, low; + + if (!dev || block_size <= 0 || (block_size - 1) & block_size) { + errno = EINVAL; + return -1; + } +#ifdef BLKGETSIZE64 + { u64 size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef BLKGETSIZE + { unsigned long size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", + size, size); + return (s64)size * 512 / block_size; + } + } +#endif +#ifdef FDGETPRM + { struct floppy_struct this_floppy; + + if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", + (unsigned long)this_floppy.size, + (unsigned long)this_floppy.size); + return (s64)this_floppy.size * 512 / block_size; + } + } +#endif + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; + + if (!ntfs_device_offset_valid(dev, mid)) + low = mid; + else + high = mid; + } + dev->d_ops->seek(dev, 0LL, SEEK_SET); + return (low + 1LL) / block_size; +} + +/** + * ntfs_device_partition_start_sector_get - get starting sector of a partition + * @dev: open device + * + * On success, return the starting sector of the partition @dev in the parent + * block device of @dev. On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", + geo.start, geo.start); + return geo.start; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_heads_get - get number of heads of device + * @dev: open device + * + * On success, return the number of heads on the device @dev. On error return + * -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +int ntfs_device_heads_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", + (unsigned)geo.heads, + (unsigned)geo.heads); + return geo.heads; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_sectors_per_track_get - get number of sectors per track of device + * @dev: open device + * + * On success, return the number of sectors per track on the device @dev. On + * error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", + (unsigned)geo.sectors, + (unsigned)geo.sectors); + return geo.sectors; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_sector_size_get - get sector size of a device + * @dev: open device + * + * On success, return the sector size in bytes of the device @dev. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKSSZGET ioctl + * ENOTTY @dev is a file or a device not supporting BLKSSZGET + */ +int ntfs_device_sector_size_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKSSZGET + { + int sect_size = 0; + + if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { + ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", + sect_size); + return sect_size; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_block_size_set - set block size of a device + * @dev: open device + * @block_size: block size to set @dev to + * + * On success, return 0. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKBSZSET ioctl + * ENOTTY @dev is a file or a device not supporting BLKBSZSET + */ +int ntfs_device_block_size_set(struct ntfs_device *dev, + int block_size __attribute__((unused))) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKBSZSET + { + size_t s_block_size = block_size; + if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { + ntfs_log_debug("Used BLKBSZSET to set block size to " + "%d bytes.\n", block_size); + return 0; + } + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + } +#else + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + errno = EOPNOTSUPP; +#endif + return -1; +} diff --git a/lib/libntfs/orig/source/device.h b/lib/libntfs/orig/source/device.h new file mode 100644 index 0000000..ad34ac5 --- /dev/null +++ b/lib/libntfs/orig/source/device.h @@ -0,0 +1,134 @@ +/* + * device.h - Exports for low level device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_H +#define _NTFS_DEVICE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "device_io.h" +#include "types.h" +#include "support.h" +#include "volume.h" + +/** + * enum ntfs_device_state_bits - + * + * Defined bits for the state field in the ntfs_device structure. + */ +typedef enum { + ND_Open, /* 1: Device is open. */ + ND_ReadOnly, /* 1: Device is read-only. */ + ND_Dirty, /* 1: Device is dirty, needs sync. */ + ND_Block, /* 1: Device is a block device. */ + ND_Sync, /* 1: Device is mounted with "-o sync" */ +} ntfs_device_state_bits; + +#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) +#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) +#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) + +#define NDevOpen(nd) test_ndev_flag(nd, Open) +#define NDevSetOpen(nd) set_ndev_flag(nd, Open) +#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) + +#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) +#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) +#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) + +#define NDevDirty(nd) test_ndev_flag(nd, Dirty) +#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) +#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) + +#define NDevBlock(nd) test_ndev_flag(nd, Block) +#define NDevSetBlock(nd) set_ndev_flag(nd, Block) +#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) + +#define NDevSync(nd) test_ndev_flag(nd, Sync) +#define NDevSetSync(nd) set_ndev_flag(nd, Sync) +#define NDevClearSync(nd) clear_ndev_flag(nd, Sync) + +/** + * struct ntfs_device - + * + * The ntfs device structure defining all operations needed to access the low + * level device underlying the ntfs volume. + */ +struct ntfs_device { + struct ntfs_device_operations *d_ops; /* Device operations. */ + unsigned long d_state; /* State of the device. */ + char *d_name; /* Name of device. */ + void *d_private; /* Private data used by the + device operations. */ +}; + +struct stat; + +/** + * struct ntfs_device_operations - + * + * The ntfs device operations defining all operations that can be performed on + * the low level device described by an ntfs device structure. + */ +struct ntfs_device_operations { + int (*open)(struct ntfs_device *dev, int flags); + int (*close)(struct ntfs_device *dev); + s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); + s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); + s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); + s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); + s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, + s64 offset); + int (*sync)(struct ntfs_device *dev); + int (*stat)(struct ntfs_device *dev, struct stat *buf); + int (*ioctl)(struct ntfs_device *dev, int request, void *argp); +}; + +extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data); +extern int ntfs_device_free(struct ntfs_device *dev); +extern int ntfs_device_sync(struct ntfs_device *dev); + +extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, + void *b); +extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b); + +extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); +extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); + +extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, + const s64 count, void *b); +extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b); + +extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); +extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); +extern int ntfs_device_heads_get(struct ntfs_device *dev); +extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); +extern int ntfs_device_sector_size_get(struct ntfs_device *dev); +extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); + +#endif /* defined _NTFS_DEVICE_H */ diff --git a/lib/libntfs/orig/source/device_io.c b/lib/libntfs/orig/source/device_io.c new file mode 100644 index 0000000..f76bf70 --- /dev/null +++ b/lib/libntfs/orig/source/device_io.c @@ -0,0 +1,40 @@ +/* + * device_io.c - Default device io operations. Originated from the Linux-NTFS project. + * + * Copyright (c) 2003 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifndef GEKKO +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +#ifndef __CYGWIN32__ + +/* Not on Cygwin; use standard Unix style low level device operations. */ +#include "unix_io.c" + +#else /* __CYGWIN32__ */ + +/* On Cygwin; use Win32 low level device operations. */ +#include "win32_io.c" + +#endif /* __CYGWIN32__ */ + +#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ +#endif /* GEKKO */ diff --git a/lib/libntfs/orig/source/device_io.h b/lib/libntfs/orig/source/device_io.h new file mode 100644 index 0000000..fad4d85 --- /dev/null +++ b/lib/libntfs/orig/source/device_io.h @@ -0,0 +1,82 @@ +/* + * device_io.h - Exports for default device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_IO_H +#define _NTFS_DEVICE_IO_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +#ifndef __CYGWIN32__ + +#ifndef GEKKO +/* Not on Cygwin; use standard Unix style low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_unix_io_ops +#else +/* Wii i/o device. */ +#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +#endif + +#else /* __CYGWIN32__ */ + +#ifndef HDIO_GETGEO +# define HDIO_GETGEO 0x301 +/** + * struct hd_geometry - + */ +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; +#endif +#ifndef BLKGETSIZE +# define BLKGETSIZE 0x1260 +#endif +#ifndef BLKSSZGET +# define BLKSSZGET 0x1268 +#endif +#ifndef BLKGETSIZE64 +# define BLKGETSIZE64 0x80041272 +#endif +#ifndef BLKBSZSET +# define BLKBSZSET 0x40041271 +#endif + +/* On Cygwin; use Win32 low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_win32_io_ops + +#endif /* __CYGWIN32__ */ + + +/* Forward declaration. */ +struct ntfs_device_operations; + +extern struct ntfs_device_operations ntfs_device_default_io_ops; + +#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ + +#endif /* defined _NTFS_DEVICE_IO_H */ + diff --git a/lib/libntfs/orig/source/dir.c b/lib/libntfs/orig/source/dir.c new file mode 100644 index 0000000..198bc29 --- /dev/null +++ b/lib/libntfs/orig/source/dir.c @@ -0,0 +1,2661 @@ +/** + * dir.c - Directory handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "param.h" +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "ntfstime.h" +#include "lcnalloc.h" +#include "logging.h" +#include "cache.h" +#include "misc.h" +#include "security.h" +#include "reparse.h" +#include "object_id.h" + +#ifdef HAVE_SETXATTR +#include +#endif + +/* + * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" + * and "$Q" as global constants. + */ +ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), + const_cpu_to_le16('3'), const_cpu_to_le16('0'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('I'), const_cpu_to_le16('I'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('D'), const_cpu_to_le16('H'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), + const_cpu_to_le16('\0') }; + +#if CACHE_INODE_SIZE + +/* + * Pathname hashing + * + * Based on first char and second char (which may be '\0') + */ + +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) +{ + const char *path; + const unsigned char *name; + + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); +} + +/* + * Pathname comparing for entering/fetching from cache + */ + +static int inode_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (!cached->variable + || strcmp(cached->variable, wanted->variable)); +} + +/* + * Pathname comparing for invalidating entries in cache + * + * A partial path is compared in order to invalidate all paths + * related to a renamed directory + * inode numbers are also checked, as deleting a long name may + * imply deleting a short name and conversely + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; + + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); +} + +#endif + +#if CACHE_LOOKUP_SIZE + +/* + * File name comparing for entering/fetching from lookup cache + */ + +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); +} + +/* + * Inode number comparing for invalidating lookup cache + * + * All entries with designated inode number are invalidated + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); +} + +/* + * Lookup hashing + * + * Based on first, second and and last char + */ + +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) +{ + const unsigned char *name; + int count; + unsigned int val; + + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); +} + +#endif + +/** + * ntfs_inode_lookup_by_name - find an inode in a directory given its name + * @dir_ni: ntfs inode of the directory in which to search for the name + * @uname: Unicode name for which to search in the directory + * @uname_len: length of the name @uname in Unicode characters + * + * Look for an inode with name @uname in the directory with inode @dir_ni. + * ntfs_inode_lookup_by_name() walks the contents of the directory looking for + * the Unicode name. If the name is found in the directory, the corresponding + * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it + * is a 64-bit number containing the sequence number. + * + * On error, return -1 with errno set to the error code. If the inode is is not + * found errno is ENOENT. + * + * Note, @uname_len does not include the (optional) terminating NULL character. + * + * Note, we look for a case sensitive match first but we also look for a case + * insensitive match at the same time. If we find a case insensitive match, we + * save that for the case that we don't find an exact match, where we return + * the mft reference of the case insensitive match. + * + * If the volume is mounted with the case sensitive flag set, then we only + * allow exact matches. + */ +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) +{ + VCN vcn; + u64 mref = 0; + s64 br; + ntfs_volume *vol = dir_ni->vol; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; + u8 *index_end; + ntfs_attr *ia_na; + int eo, rc; + u32 index_block_size, index_vcn_size; + u8 index_vcn_size_bits; + + ntfs_log_trace("Entering\n"); + + if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { + errno = EINVAL; + return -1; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + return -1; + + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto put_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + mref = le64_to_cpu(ie->indexed_file); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present return error code + * ENOENT, unless we have got the mft reference of a matching name + * cached in mref in which case return mref. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_attr_put_search_ctx(ctx); + if (mref) + return mref; + ntfs_log_debug("Entry not found - between root entries.\n"); + errno = ENOENT; + return -1; + } /* Child node present, descend into it. */ + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + ntfs_log_perror("Failed to open index allocation (inode %lld)", + (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) { + ntfs_attr_close(ia_na); + goto put_err_out; + } + + /* Determine the size of a vcn in the directory index. */ + if (vol->cluster_size <= index_block_size) { + index_vcn_size = vol->cluster_size; + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size = vol->sector_size; + index_vcn_size_bits = vol->sector_size_bits; + } + + /* Get the starting vcn of the index_block holding the child node. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + +descend_into_child_node: + + /* Read the index block starting at vcn. */ + br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read vcn 0x%llx", + (unsigned long long)vcn); + goto close_err_out; + } + + if (sle64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx).\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)vcn); + errno = EIO; + goto close_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)vcn, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, + (unsigned)index_block_size); + errno = EIO; + goto close_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "0x%llx exceeds maximum size.\n", + (long long)vcn, (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds check. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto close_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + mref = le64_to_cpu(ie->indexed_file); + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node. + */ + if (ie->ie_flags & INDEX_ENTRY_NODE) { + if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* Child node present, descend into it. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + if (vcn >= 0) + goto descend_into_child_node; + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + /* + * No child node present, return error code ENOENT, unless we have got + * the mft reference of a matching name cached in mref in which case + * return mref. + */ + if (mref) + return mref; + ntfs_log_debug("Entry not found.\n"); + errno = ENOENT; + return -1; +put_err_out: + eo = EIO; + ntfs_log_debug("Corrupt directory. Aborting lookup.\n"); +eo_put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return -1; +close_err_out: + eo = errno; + free(ia); + ntfs_attr_close(ia_na); + goto eo_put_err_out; +} + +/* + * Lookup a file in a directory from its UTF-8 name + * + * The name is first fetched from cache if one is defined + * + * Returns the inode number + * or -1 if not possible (errno tells why) + */ + +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) +{ + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; + + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { +#if CACHE_LOOKUP_SIZE + + /* + * fetch inode from cache + */ + + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else +#endif + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); +} + +/* + * Update a cache lookup record when a name has been defined + * + * The UTF-8 name is required + */ + +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) +{ +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; + + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } +#endif +} + +/** + * ntfs_pathname_to_inode - Find the inode which represents the given pathname + * @vol: An ntfs volume obtained from ntfs_mount + * @parent: A directory inode to begin the search (may be NULL) + * @pathname: Pathname to be located + * + * Take an ASCII pathname and find the inode that represents it. The function + * splits the path and then descends the directory tree. If @parent is NULL, + * then the root directory '.' will be used as the base for the search. + * + * Return: inode Success, the pathname was valid + * NULL Error, the pathname was invalid, or some other error occurred + */ +ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname) +{ + u64 inum; + int len, err = 0; + char *p, *q; + ntfs_inode *ni; + ntfs_inode *result = NULL; + ntfschar *unicode = NULL; + char *ascii = NULL; +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + struct CACHED_INODE *cached; + char *fullname; +#endif + + if (!vol || !pathname) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("path: '%s'\n", pathname); + + ascii = strdup(pathname); + if (!ascii) { + ntfs_log_error("Out of memory.\n"); + err = ENOMEM; + goto out; + } + + p = ascii; + /* Remove leading /'s. */ + while (p && *p && *p == PATH_SEP) + p++; +#if CACHE_INODE_SIZE + fullname = p; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",ascii); +#endif + if (parent) { + ni = parent; + } else { +#if CACHE_INODE_SIZE + /* + * fetch inode for full path from cache + */ + if (*fullname) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + } else + cached = (struct CACHED_INODE*)NULL; + if (cached) { + /* + * return opened inode if found in cache + */ + inum = MREF(cached->inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + } + result = ni; + goto out; + } +#endif + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_debug("Couldn't open the inode of the root " + "directory.\n"); + err = EIO; + result = (ntfs_inode*)NULL; + goto out; + } + } + + while (p && *p) { + /* Find the end of the first token. */ + q = strchr(p, PATH_SEP); + if (q != NULL) { + *q = '\0'; + } +#if CACHE_INODE_SIZE + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } +#else + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); +#endif + if (inum == (u64) -1) { + ntfs_log_debug("Couldn't find name '%s' in pathname " + "'%s'.\n", p, pathname); + err = ENOENT; + goto close; + } + + if (ni != parent) + if (ntfs_inode_close(ni)) { + err = errno; + goto out; + } + + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + goto close; + } + + free(unicode); + unicode = NULL; + + if (q) *q++ = PATH_SEP; /* JPA */ + p = q; + while (p && *p && *p == PATH_SEP) + p++; + } + + result = ni; + ni = NULL; +close: + if (ni && (ni != parent)) + if (ntfs_inode_close(ni) && !err) + err = errno; +out: + free(ascii); + free(unicode); + if (err) + errno = err; + return result; +} + +/* + * The little endian Unicode string ".." for ntfs_readdir(). + */ +static const ntfschar dotdot[3] = { const_cpu_to_le16('.'), + const_cpu_to_le16('.'), + const_cpu_to_le16('\0') }; + +/* + * union index_union - + * More helpers for ntfs_readdir(). + */ +typedef union { + INDEX_ROOT *ir; + INDEX_ALLOCATION *ia; +} index_union __attribute__((__transparent_union__)); + +/** + * enum INDEX_TYPE - + * More helpers for ntfs_readdir(). + */ +typedef enum { + INDEX_TYPE_ROOT, /* index root */ + INDEX_TYPE_ALLOCATION, /* index allocation */ +} INDEX_TYPE; + +/** + * ntfs_filldir - ntfs specific filldir method + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @ivcn_bits: log(2) of index vcn size + * @index_type: specifies whether @iu is an index root or an index allocation + * @iu: index root or index block to which @ie belongs + * @ie: current index entry + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Pass information specifying the current directory entry @ie to the @filldir + * callback. + */ +static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, + const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, + void *dirent, ntfs_filldir_t filldir) +{ + FILE_NAME_ATTR *fn = &ie->key.file_name; + unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; + + ntfs_log_trace("Entering.\n"); + + /* Advance the position even if going to skip the entry. */ + if (index_type == INDEX_TYPE_ALLOCATION) + *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu( + iu.ia->index_block_vcn) << ivcn_bits) + + dir_ni->vol->mft_record_size; + else /* if (index_type == INDEX_TYPE_ROOT) */ + *pos = (u8*)ie - (u8*)iu.ir; + /* Skip root directory self reference entry. */ + if (MREF_LE(ie->indexed_file) == FILE_root) + return 0; + if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) + dt_type = NTFS_DT_DIR; + else if (fn->file_attributes & FILE_ATTR_SYSTEM) + dt_type = NTFS_DT_UNKNOWN; + else + dt_type = NTFS_DT_REG; + + /* return metadata files and hidden files if requested */ + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); +} + +/** + * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode + * @ni: ntfs inode whose parent directory to find + * + * Find the parent directory of the ntfs inode @ni. To do this, find the first + * file name attribute in the mft record of @ni and return the parent mft + * reference from that. + * + * Note this only makes sense for directories, since files can be hard linked + * from multiple directories and there is no way for us to tell which one is + * being looked for. + * + * Technically directories can have hard links, too, but we consider that as + * illegal as Linux/UNIX do not support directory hard links. + * + * Return the mft reference of the parent directory on success or -1 on error + * with errno set to the error code. + */ +static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) +{ + MFT_REF mref; + ntfs_attr_search_ctx *ctx; + FILE_NAME_ATTR *fn; + int eo; + + ntfs_log_trace("Entering.\n"); + + if (!ni) { + errno = EINVAL; + return ERR_MREF(-1); + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ERR_MREF(-1); + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + if (ctx->attr->non_resident) { + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); + goto io_err_out; + } + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > + (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); + goto io_err_out; + } + mref = le64_to_cpu(fn->parent_directory); + ntfs_attr_put_search_ctx(ctx); + return mref; +io_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return ERR_MREF(-1); +} + +/** + * ntfs_readdir - read the contents of an ntfs directory + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Parse the index root and the index blocks that are marked in use in the + * index bitmap and hand each found directory entry to the @filldir callback + * supplied by the caller. + * + * Return 0 on success or -1 on error with errno set to the error code. + * + * Note: Index blocks are parsed in ascending vcn order, from which follows + * that the directory entries are not returned sorted. + */ +int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir) +{ + s64 i_size, br, ia_pos, bmp_pos, ia_start; + ntfs_volume *vol; + ntfs_attr *ia_na, *bmp_na = NULL; + ntfs_attr_search_ctx *ctx = NULL; + u8 *index_end, *bmp = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia = NULL; + int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; + u32 index_block_size, index_vcn_size; + u8 index_block_size_bits, index_vcn_size_bits; + + ntfs_log_trace("Entering.\n"); + + if (!dir_ni || !pos || !filldir) { + errno = EINVAL; + return -1; + } + + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = ENOTDIR; + return -1; + } + + vol = dir_ni->vol; + + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", + (unsigned long long)dir_ni->mft_no, (long long)*pos); + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to open index allocation attribute. " + "Directory inode %lld is corrupt or bug", + (unsigned long long)dir_ni->mft_no); + return -1; + } + i_size = 0; + } else + i_size = ia_na->data_size; + + rc = 0; + + /* Are we at end of dir yet? */ + if (*pos >= i_size + vol->mft_record_size) + goto done; + + /* Emulate . and .. for all directories. */ + if (!*pos) { + rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos, + MK_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)), + NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + if (*pos == 1) { + MFT_REF parent_mref; + + parent_mref = ntfs_mft_get_parent_ref(dir_ni); + if (parent_mref == ERR_MREF(-1)) { + ntfs_log_perror("Parent directory not found"); + goto dir_err_out; + } + + rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, + parent_mref, NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + goto err_out; + + /* Get the offset into the index root attribute. */ + ir_pos = (int)*pos; + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + /* Determine the size of a vcn in the directory index. */ + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto dir_err_out; + } + index_block_size_bits = ffs(index_block_size) - 1; + if (vol->cluster_size <= index_block_size) { + index_vcn_size = vol->cluster_size; + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size = vol->sector_size; + index_vcn_size_bits = vol->sector_size_bits; + } + + /* Are we jumping straight into the index allocation attribute? */ + if (*pos >= vol->mft_record_size) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto skip_index_root; + } + + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until filldir tells us it has had enough + * or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) + goto dir_err_out; + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index root entry if continuing previous readdir. */ + if (ir_pos > (u8*)ie - (u8*)ir) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ROOT, ir, ie, dirent, filldir); + if (rc) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto err_out; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* If there is no index allocation attribute we are finished. */ + if (!ia_na) + goto EOD; + + /* Advance *pos to the beginning of the index allocation. */ + *pos = vol->mft_record_size; + +skip_index_root: + + if (!ia_na) + goto done; + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) + goto err_out; + + bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!bmp_na) { + ntfs_log_perror("Failed to open index bitmap attribute"); + goto dir_err_out; + } + + /* Get the offset into the index allocation attribute. */ + ia_pos = *pos - vol->mft_record_size; + + bmp_pos = ia_pos >> index_block_size_bits; + if (bmp_pos >> 3 >= bmp_na->data_size) { + ntfs_log_error("Current index position exceeds index bitmap " + "size.\n"); + goto dir_err_out; + } + + bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096); + bmp = ntfs_malloc(bmp_buf_size); + if (!bmp) + goto err_out; + + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + + bmp_buf_pos = 0; + /* If the index block is not in use find the next one that is. */ + while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) { +find_next_index_buffer: + bmp_pos++; + bmp_buf_pos++; + /* If we have reached the end of the bitmap, we are done. */ + if (bmp_pos >> 3 >= bmp_na->data_size) + goto EOD; + ia_pos = bmp_pos << index_block_size_bits; + if (bmp_buf_pos >> 3 < bmp_buf_size) + continue; + /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; + if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) + bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + } + + ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos); + + /* Read the index block starting at bmp_pos. */ + br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read index block"); + goto err_out; + } + + ia_start = ia_pos & ~(s64)(index_block_size - 1); + if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> + index_vcn_size_bits) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx) in inode 0x%llx.\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)ia_start >> + index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + + 0x18, (unsigned)index_block_size); + goto dir_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "%lld exceeds maximum size.\n", + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until ntfs_filldir tells us it has had + * enough or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index allocation, offset 0x%llx.\n", + (long long)ia_start + ((u8*)ie - (u8*)ia)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory inode " + "%lld.\n", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index entry if continuing previous readdir. */ + if (ia_pos - ia_start > (u8*)ie - (u8*)ia) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); + if (rc) + goto err_out; + } + goto find_next_index_buffer; +EOD: + /* We are finished, set *pos to EOD. */ + *pos = i_size + vol->mft_record_size; +done: + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); + return 0; +dir_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_log_trace("failed.\n"); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + errno = eo; + return -1; +} + + +/** + * __ntfs_create - create object on ntfs volume + * @dir_ni: ntfs inode for directory in which create new object + * @securid: id of inheritable security descriptor, 0 if none + * @name: unicode name of new object + * @name_len: length of the name in unicode characters + * @type: type of the object to create + * @dev: major and minor device numbers (obtained from makedev()) + * @target: target in unicode (only for symlinks) + * @target_len: length of target in unicode characters + * + * Internal, use ntfs_create{,_device,_symlink} wrappers instead. + * + * @type can be: + * S_IFREG to create regular file + * S_IFDIR to create directory + * S_IFBLK to create block device + * S_IFCHR to create character device + * S_IFLNK to create symbolic link + * S_IFIFO to create FIFO + * S_IFSOCK to create socket + * other values are invalid. + * + * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value + * ignored. + * + * @target and @target_len are used only if @type is S_IFLNK, in other cases + * their value ignored. + * + * Return opened ntfs inode that describes created object on success or NULL + * on error with errno set to the error code. + */ +static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev, + ntfschar *target, int target_len) +{ + ntfs_inode *ni; + int rollback_data = 0, rollback_sd = 0; + FILE_NAME_ATTR *fn = NULL; + STANDARD_INFORMATION *si = NULL; + int err, fn_len, si_len; + + ntfs_log_trace("Entering.\n"); + + /* Sanity checks. */ + if (!dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return NULL; + } + + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + return NULL; + } + + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); + if (!ni) + return NULL; +#if CACHE_NIDATA_SIZE + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); +#endif + /* + * Create STANDARD_INFORMATION attribute. + * JPA Depending on available inherited security descriptor, + * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3 + */ + if (securid) + si_len = sizeof(STANDARD_INFORMATION); + else + si_len = offsetof(STANDARD_INFORMATION, v1_end); + si = ntfs_calloc(si_len); + if (!si) { + err = errno; + goto err_out; + } + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; + if (securid) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = si->owner_id = 0; + ni->security_id = si->security_id = securid; + ni->quota_charged = si->quota_charged = const_cpu_to_le64(0); + ni->usn = si->usn = const_cpu_to_le64(0); + } else + clear_nino_flag(ni, v3_Extensions); + if (!S_ISREG(type) && !S_ISDIR(type)) { + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ + if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (S_ISREG(type) || S_ISDIR(type))) + ni->flags |= FILE_ATTR_COMPRESSED; + /* Add STANDARD_INFORMATION to inode. */ + if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + (u8*)si, si_len)) { + err = errno; + ntfs_log_error("Failed to add STANDARD_INFORMATION " + "attribute.\n"); + goto err_out; + } + + if (!securid) { + if (ntfs_sd_add_everyone(ni)) { + err = errno; + goto err_out; + } + } + rollback_sd = 1; + + if (S_ISDIR(type)) { + INDEX_ROOT *ir = NULL; + INDEX_ENTRY *ie; + int ir_len, index_len; + + /* Create INDEX_ROOT attribute. */ + index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); + ir_len = offsetof(INDEX_ROOT, index) + index_len; + ir = ntfs_calloc(ir_len); + if (!ir) { + err = errno; + goto err_out; + } + ir->type = AT_FILE_NAME; + ir->collation_rule = COLLATION_FILE_NAME; + ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); + if (ni->vol->cluster_size <= ni->vol->indx_record_size) + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->cluster_size_bits; + else + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->sector_size_bits; + ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); + ir->index.index_length = cpu_to_le32(index_len); + ir->index.allocated_size = cpu_to_le32(index_len); + ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + ie->key_length = 0; + ie->ie_flags = INDEX_ENTRY_END; + /* Add INDEX_ROOT attribute to inode. */ + if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, + (u8*)ir, ir_len)) { + err = errno; + free(ir); + ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); + goto err_out; + } + free(ir); + } else { + INTX_FILE *data; + int data_len; + + switch (type) { + case S_IFBLK: + case S_IFCHR: + data_len = offsetof(INTX_FILE, device_end); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic = INTX_CHARACTER_DEVICE; + break; + case S_IFLNK: + data_len = sizeof(INTX_FILE_TYPES) + + target_len * sizeof(ntfschar); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, + target_len * sizeof(ntfschar)); + break; + case S_IFSOCK: + data = NULL; + data_len = 1; + break; + default: /* FIFO or regular file. */ + data = NULL; + data_len = 0; + break; + } + /* Add DATA attribute to inode. */ + if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, + data_len)) { + err = errno; + ntfs_log_error("Failed to add DATA attribute.\n"); + free(data); + goto err_out; + } + rollback_data = 1; + free(data); + } + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + if (S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; + if (!S_ISREG(type) && !S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_SYSTEM; + else + fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + err = errno; + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + goto err_out; + } + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add entry to the index"); + goto err_out; + } + /* Set hard links count and directory flag. */ + ni->mrec->link_count = cpu_to_le16(1); + if (S_ISDIR(type)) + ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; + ntfs_inode_mark_dirty(ni); + /* Done! */ + free(fn); + free(si); + ntfs_log_trace("Done.\n"); + return ni; +err_out: + ntfs_log_trace("Failed.\n"); + + if (rollback_sd) + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + + if (rollback_data) + ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0); + /* + * Free extent MFT records (should not exist any with current + * ntfs_create implementation, but for any case if something will be + * changed in the future). + */ + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + if (ntfs_mft_record_free(ni->vol, ni)) + ntfs_log_error("Failed to free MFT record. " + "Leaving inconsistent metadata. Run chkdsk.\n"); + free(fn); + free(si); + errno = err; + return NULL; +} + +/** + * Some wrappers around __ntfs_create() ... + */ + +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name, + u8 name_len, mode_t type) +{ + if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && + type != S_IFSOCK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, 0, NULL, 0); +} + +ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev) +{ + if (type != S_IFCHR && type != S_IFBLK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, dev, NULL, 0); +} + +ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len) +{ + if (!target || !target_len) { + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, S_IFLNK, 0, + target, target_len); +} + +int ntfs_check_empty_dir(ntfs_inode *ni) +{ + ntfs_attr *na; + int ret = 0; + + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) + return 0; + + na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!na) { + errno = EIO; + ntfs_log_perror("Failed to open directory"); + return -1; + } + + /* Non-empty directory? */ + if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ + /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ + errno = ENOTEMPTY; + ntfs_log_debug("Directory is not empty\n"); + ret = -1; + } + + ntfs_attr_close(na); + return ret; +} + +static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) +{ + int link_count = le16_to_cpu(ni->mrec->link_count); + int ret; + + ret = ntfs_check_empty_dir(ni); + if (!ret || errno != ENOTEMPTY) + return ret; + /* + * Directory is non-empty, so we can unlink only if there is more than + * one "real" hard link, i.e. links aren't different DOS and WIN32 names + */ + if ((link_count == 1) || + (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { + errno = ENOTEMPTY; + ntfs_log_debug("Non-empty directory without hard links\n"); + goto no_hardlink; + } + + ret = 0; +no_hardlink: + return ret; +} + +/** + * ntfs_delete - delete file or directory from ntfs volume + * @ni: ntfs inode for object to delte + * @dir_ni: ntfs inode for directory in which delete object + * @name: unicode name of the object to delete + * @name_len: length of the name in unicode characters + * + * @ni is always closed after the call to this function (even if it failed), + * user does not need to call ntfs_inode_close himself. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_delete(ntfs_volume *vol, const char *pathname, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +{ + ntfs_attr_search_ctx *actx = NULL; + FILE_NAME_ATTR *fn = NULL; + BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; + BOOL case_sensitive_match = TRUE; + int err = 0; +#if CACHE_NIDATA_SIZE + int i; +#endif +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + const char *p; + u64 inum = (u64)-1; + int count; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP lkitem; +#endif + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + if (dir_ni->nr_extents == -1) + dir_ni = dir_ni->base_ni; + /* + * Search for FILE_NAME attribute with such name. If it's in POSIX or + * WIN32_AND_DOS namespace, then simply remove it from index and inode. + * If filename in DOS or in WIN32 namespace, then remove DOS name first, + * only then remove WIN32 name. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + goto err_out; +search: + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *s; + IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; + + errno = 0; + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + s = ntfs_attr_name_get(fn->file_name, fn->file_name_length); + ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d " + "case: %d\n", s, fn->file_name_type, + looking_for_dos_name, looking_for_win32_name, + case_sensitive_match); + ntfs_attr_name_free(&s); + if (looking_for_dos_name) { + if (fn->file_name_type == FILE_NAME_DOS) + break; + else + continue; + } + if (looking_for_win32_name) { + if (fn->file_name_type == FILE_NAME_WIN32) + break; + else + continue; + } + + /* Ignore hard links from other directories */ + if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { + ntfs_log_debug("MFT record numbers don't match " + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); + continue; + } + if (case_sensitive_match + || ((fn->file_name_type == FILE_NAME_POSIX) + && NVolCaseSensitive(ni->vol))) + case_sensitive = CASE_SENSITIVE; + + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, + name, name_len, case_sensitive, + ni->vol->upcase, ni->vol->upcase_len)){ + + if (fn->file_name_type == FILE_NAME_WIN32) { + looking_for_dos_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + continue; + } + if (fn->file_name_type == FILE_NAME_DOS) + looking_for_dos_name = TRUE; + break; + } + } + if (errno) { + /* + * If case sensitive search failed, then try once again + * ignoring case. + */ + if (errno == ENOENT && case_sensitive_match) { + case_sensitive_match = FALSE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + goto err_out; + } + + if (ntfs_check_unlinkable_dir(ni, fn) < 0) + goto err_out; + + if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length))) + goto err_out; + + if (ntfs_attr_record_rm(actx)) + goto err_out; + + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) - 1); + + ntfs_inode_mark_dirty(ni); + if (looking_for_dos_name) { + looking_for_dos_name = FALSE; + looking_for_win32_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + /* TODO: Update object id, quota and securiry indexes if required. */ + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ +#if CACHE_LOOKUP_SIZE + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); +#endif +#if CACHE_INODE_SIZE + inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); +#endif + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + goto ok; + } + if (ntfs_delete_reparse_index(ni)) { + /* + * Failed to remove the reparse index : proceed anyway + * This is not a critical error, the entry is useless + * because of sequence_number, and stopping file deletion + * would be much worse as the file is not referenced now. + */ + err = errno; + } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } + ntfs_attr_reinit_search_ctx(actx); + while (!ntfs_attrs_walk(actx)) { + if (actx->attr->non_resident) { + runlist *rl; + + rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, + NULL); + if (!rl) { + err = errno; + ntfs_log_error("Failed to decompress runlist. " + "Leaving inconsistent metadata.\n"); + continue; + } + if (ntfs_cluster_free_from_rl(ni->vol, rl)) { + err = errno; + ntfs_log_error("Failed to free clusters. " + "Leaving inconsistent metadata.\n"); + continue; + } + free(rl); + } + } + if (errno != ENOENT) { + err = errno; + ntfs_log_error("Attribute enumeration failed. " + "Probably leaving inconsistent metadata.\n"); + } + /* All extents should be attached after attribute walk. */ +#if CACHE_NIDATA_SIZE + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; +#else + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } +#endif + if (ntfs_mft_record_free(ni->vol, ni)) { + err = errno; + ntfs_log_error("Failed to free base MFT record. " + "Leaving inconsistent metadata.\n"); + } + ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); +out: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) + err = errno; + if (err) { + errno = err; + ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); + return -1; + } + ntfs_log_trace("Done.\n"); + return 0; +err_out: + err = errno; + goto out; +} + +/** + * ntfs_link - create hard link for file or directory + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters + * + * NOTE: At present we allow creating hardlinks to directories, we use them + * in a temporary state during rename. But it's defenitely bad idea to have + * hard links to directories as a result of operation. + * FIXME: Create internal __ntfs_link that allows hard links to a directories + * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len, FILE_NAME_TYPE_FLAGS nametype) +{ + FILE_NAME_ATTR *fn = NULL; + int fn_len, err; + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len || + ni->mft_no == dir_ni->mft_no) { + err = EINVAL; + ntfs_log_perror("ntfs_link wrong arguments"); + goto err_out; + } + + if ((ni->flags & FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + err = EOPNOTSUPP; + goto err_out; + } + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = nametype; + fn->file_attributes = ni->flags; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add filename to the index"); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; + /* Try to remove just added attribute from index. */ + if (ntfs_index_remove(dir_ni, ni, fn, fn_len)) + goto rollback_failed; + goto err_out; + } + /* Increment hard links count. */ + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) + 1); + /* Done! */ + ntfs_inode_mark_dirty(ni); + free(fn); + ntfs_log_trace("Done.\n"); + return 0; +rollback_failed: + ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n"); +err_out: + free(fn); + errno = err; + return -1; +} + +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +{ + return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); +} + +/* + * Get a parent directory from an inode entry + * + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. + * + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link + */ + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) +{ + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); + + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); +} + +#ifdef HAVE_SETXATTR + +#define MAX_DOS_NAME_LENGTH 12 + +/* + * Get a DOS name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_DOS) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a DOS or WIN32+DOS name for the entry + * copy name, after truncation for safety + */ + outsize = fn->file_name_length; +/* TODO : reject if name is too long ? */ + if (outsize > MAX_DOS_NAME_LENGTH) + outsize = MAX_DOS_NAME_LENGTH; + memcpy(dosname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + +/* + * Get a long name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + +/* + * Get the ntfs DOS name into an extended attribute + */ + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int outsize = 0; + char *outname = (char*)NULL; + u64 dnum; + int doslen; + ntfschar dosname[MAX_DOS_NAME_LENGTH]; + + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); + outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); + } + } else { + if (doslen == 0) + errno = ENODATA; + outsize = -errno; + } + return (outsize); +} + +/* + * Change the name space of an existing file or directory + * + * Returns the old namespace if successful + * -1 if an error occurred (described by errno) + */ + +static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *name, int len, + FILE_NAME_TYPE_FLAGS nametype) +{ + ntfs_attr_search_ctx *actx; + ntfs_index_context *icx; + FILE_NAME_ATTR *fnx; + FILE_NAME_ATTR *fn = NULL; + BOOL found; + int lkup; + int ret; + + ret = -1; + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (actx) { + found = FALSE; + do { + lkup = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx); + if (!lkup) { + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + found = (MREF_LE(fn->parent_directory) + == dir_ni->mft_no) + && !memcmp(fn->file_name, name, + len*sizeof(ntfschar)); + } + } while (!lkup && !found); + if (found) { + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + lkup = ntfs_index_lookup((char*)fn, len, icx); + if (!lkup && icx->data && icx->data_len) { + fnx = (FILE_NAME_ATTR*)icx->data; + ret = fn->file_name_type; + fn->file_name_type = nametype; + fnx->file_name_type = nametype; + ntfs_inode_mark_dirty(ni); + ntfs_index_entry_mark_dirty(icx); + } + ntfs_index_ctx_put(icx); + } + } + ntfs_attr_put_search_ctx(actx); + } + return (ret); +} + +/* + * Set a DOS name to a file and adjust name spaces + * + * If the new names are collapsible (same uppercased chars) : + * + * - the existing DOS name or DOS+Win32 name is made Posix + * - if it was a real DOS name, the existing long name is made DOS+Win32 + * and the existing DOS name is deleted + * - finally the existing long name is made DOS+Win32 unless already done + * + * If the new names are not collapsible : + * + * - insert the short name as a DOS name + * - delete the old long name or existing short name + * - insert the new long name (as a Win32 or DOS+Win32 name) + * + * Deleting the old long name will not delete the file + * provided the old name was in the Posix name space, + * because the alternate name has been set before. + * + * The inodes of file and parent directory are always closed + * + * Returns 0 if successful + * -1 if failed + */ + +static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *shortname, int shortlen, + ntfschar *longname, int longlen, + ntfschar *deletename, int deletelen, BOOL existed) +{ + unsigned int linkcount; + ntfs_volume *vol; + BOOL collapsible; + BOOL deleted; + BOOL done; + FILE_NAME_TYPE_FLAGS oldnametype; + u64 dnum; + u64 fnum; + int res; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + fnum = ni->mft_no; + /* save initial link count */ + linkcount = le16_to_cpu(ni->mrec->link_count); + + /* check whether the same name may be used as DOS and WIN32 */ + collapsible = ntfs_collapsible_chars(ni->vol, shortname, shortlen, + longname, longlen); + if (collapsible) { + deleted = FALSE; + done = FALSE; + if (existed) { + oldnametype = set_namespace(ni, dir_ni, deletename, + deletelen, FILE_NAME_POSIX); + if (oldnametype == FILE_NAME_DOS) { + if (set_namespace(ni, dir_ni, longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, dir_ni, + deletename, deletelen)) + res = 0; + deleted = TRUE; + } else + done = TRUE; + } + } + if (!deleted) { + if (!done && (set_namespace(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0)) + res = 0; + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } else { + if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, + FILE_NAME_DOS) + /* make sure a new link was recorded */ + && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { + /* delete the existing long name or short name */ +// is it ok to not provide the path ? + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { + /* delete closes the inodes, so have to open again */ + dir_ni = ntfs_inode_open(vol, dnum); + if (dir_ni) { + ni = ntfs_inode_open(vol, fnum); + if (ni) { + if (!ntfs_link_i(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32)) + res = 0; + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; + } + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } + } else { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + } + return (res); +} + + +/* + * Set the ntfs DOS name into an extended attribute + * + * The DOS name will be added as another file name attribute + * using the existing file name information from the original + * name or overwriting the DOS Name if one exists. + * + * The inode of the file is always closed + */ + +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res = 0; + int longlen = 0; + int shortlen = 0; + char newname[MAX_DOS_NAME_LENGTH + 1]; + ntfschar oldname[MAX_DOS_NAME_LENGTH]; + int oldlen; + ntfs_volume *vol; + u64 fnum; + u64 dnum; + BOOL closed = FALSE; + ntfschar *shortname = NULL; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + vol = ni->vol; + fnum = ni->mft_no; + /* convert the string to the NTFS wide chars */ + if (size > MAX_DOS_NAME_LENGTH) + size = MAX_DOS_NAME_LENGTH; + strncpy(newname, value, size); + newname[size] = 0; + shortlen = ntfs_mbstoucs(newname, &shortname); + /* make sure the short name has valid chars */ + if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + res = -errno; + return res; + } + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + oldlen = get_dos_name(ni, dnum, oldname); + if ((oldlen >= 0) + && !ntfs_forbidden_chars(longname, longlen)) { + if (oldlen > 0) { + if (flags & XATTR_CREATE) { + res = -1; + errno = EEXIST; + } else + if ((shortlen == oldlen) + && !memcmp(shortname,oldname, + oldlen*sizeof(ntfschar))) + /* already set, done */ + res = 0; + else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + oldname, oldlen, TRUE); + closed = TRUE; + } + } else { + if (flags & XATTR_REPLACE) { + res = -1; + errno = ENODATA; + } else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + longname, longlen, FALSE); + closed = TRUE; + } + } + } else + res = -1; + } else { + res = -1; + errno = ENOENT; + } + free(shortname); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res ? -1 : 0); +} + +/* + * Delete the ntfs DOS name + */ + +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + int oldnametype; + int longlen = 0; + int shortlen; + u64 dnum; + ntfs_volume *vol; + BOOL deleted = FALSE; + ntfschar shortname[MAX_DOS_NAME_LENGTH]; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + shortlen = get_dos_name(ni, dnum, shortname); + if (shortlen >= 0) { + /* migrate the long name as Posix */ + oldnametype = set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_POSIX); + switch (oldnametype) { + case FILE_NAME_WIN32_AND_DOS : + /* name was Win32+DOS : done */ + res = 0; + break; + case FILE_NAME_DOS : + /* name was DOS, make it back to DOS */ + set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_DOS); + errno = ENOENT; + break; + case FILE_NAME_WIN32 : + /* name was Win32, make it Posix and delete */ + if (set_namespace(ni,dir_ni,shortname,shortlen, + FILE_NAME_POSIX) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, + dir_ni, shortname, + shortlen)) + res = 0; + deleted = TRUE; + } else { + /* + * DOS name has been found, but cannot + * migrate to Posix : something bad + * has happened + */ + errno = EIO; + ntfs_log_error("Could not change" + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); + } + break; + default : + /* name was Posix or not found : error */ + errno = ENOENT; + break; + } + } + } else { + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res); +} + +#endif diff --git a/lib/libntfs/orig/source/dir.h b/lib/libntfs/orig/source/dir.h new file mode 100644 index 0000000..56e76fe --- /dev/null +++ b/lib/libntfs/orig/source/dir.h @@ -0,0 +1,128 @@ +/* + * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DIR_H +#define _NTFS_DIR_H + +#include "types.h" + +#define PATH_SEP '/' + +/* + * We do not have these under DJGPP, so define our version that do not conflict + * with other S_IFs defined under DJGPP. + */ +#ifdef DJGPP +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#ifndef S_IFSOCK +#define S_IFSOCK 0140000 +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#endif + +/* + * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R + * as a global constant. + */ +extern ntfschar NTFS_INDEX_I30[5]; +extern ntfschar NTFS_INDEX_SII[5]; +extern ntfschar NTFS_INDEX_SDH[5]; +extern ntfschar NTFS_INDEX_O[3]; +extern ntfschar NTFS_INDEX_Q[3]; +extern ntfschar NTFS_INDEX_R[3]; + +extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); + +extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname); +extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type); +extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev); +extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len); +extern int ntfs_check_empty_dir(ntfs_inode *ni); +extern int ntfs_delete(ntfs_volume *vol, const char *path, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); + +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); + +/* + * File types (adapted from include ) + */ +#define NTFS_DT_UNKNOWN 0 +#define NTFS_DT_FIFO 1 +#define NTFS_DT_CHR 2 +#define NTFS_DT_DIR 4 +#define NTFS_DT_BLK 6 +#define NTFS_DT_REG 8 +#define NTFS_DT_LNK 10 +#define NTFS_DT_SOCK 12 +#define NTFS_DT_WHT 14 + +/* + * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let + * the caller specify what kind of dirent layout it wants to have. + * This allows the caller to read directories into their application or + * to have different dirent layouts depending on the binary type. + */ +typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, + const int name_len, const int name_type, const s64 pos, + const MFT_REF mref, const unsigned dt_type); + +extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir); + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_INODE_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); + +#endif + +#endif /* defined _NTFS_DIR_H */ + diff --git a/lib/libntfs/orig/source/efs.c b/lib/libntfs/orig/source/efs.c new file mode 100644 index 0000000..6ccec20 --- /dev/null +++ b/lib/libntfs/orig/source/efs.c @@ -0,0 +1,439 @@ +/** + * efs.c - Limited processing of encrypted files + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Martin Bene + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "efs.h" +#include "index.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +static ntfschar logged_utility_stream_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('E'), + const_cpu_to_le16('F'), + const_cpu_to_le16('S'), + const_cpu_to_le16(0) +} ; + + +/* + * Get the ntfs EFS info into an extended attribute + */ + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) +{ + EFS_ATTR_HEADER *efs_info; + s64 attr_size = 0; + + if (ni) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, + &attr_size); + if (efs_info + && (le32_to_cpu(efs_info->length) == attr_size)) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,efs_info,attr_size); + else { + errno = EFAULT; + attr_size = 0; + } + } else + if (size) { + errno = ERANGE; + attr_size = 0; + } + free (efs_info); + } else { + if (efs_info) { + free(efs_info); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); + } else { + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); + } + errno = EIO; + attr_size = 0; + } + } else { + errno = ENODATA; + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); + } + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Fix all encrypted AT_DATA attributes of an inode + * + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. + * + * Returns zero if successful. + * -1 if there is a problem. + */ + +static int fixup_loop(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + BOOL first; + int cnt; + int maxcnt; + int res = 0; + + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + first = FALSE; + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); +} + +/* + * Set the efs data from an extended attribute + * Warning : the new data is not checked + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + +{ + int res; + int written; + ntfs_attr *na; + const EFS_ATTR_HEADER *info_header; + + res = 0; + if (ni && value && size) { + if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); + errno = EEXIST; + } else { + /* + * Possible problem : if encrypted file was + * restored in a compressed directory, it was + * restored as compressed. + * TODO : decompress first. + */ + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); + errno = EIO; + } + return -1; + } + info_header = (const EFS_ATTR_HEADER*)value; + /* make sure we get a likely efsinfo */ + if (le32_to_cpu(info_header->length) != size) { + errno = EINVAL; + return (-1); + } + if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, + (ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no logged_utility_stream attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name,4, + (u8*)NULL,(s64)size); + } else { + errno = ENODATA; + res = -1; + } + } else { + errno = EEXIST; + res = -1; + } + if (!res) { + /* + * open and update the existing efs data + */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name, 4); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to " + "update efs data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + } else + res = -1; + } + if (!res) { + /* Don't handle AT_DATA Attribute(s) if inode is a directory */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* iterate over AT_DATA attributes */ + /* set encrypted flag, truncate attribute to match padding bytes */ + + if (fixup_loop(ni)) + return -1; + } + ni->flags |= FILE_ATTR_ENCRYPTED; + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Fixup raw encrypted AT_DATA Attribute + * read padding length from last two bytes + * truncate attribute, make non-resident, + * set data size to match padding length + * set ATTR_IS_ENCRYPTED flag on attribute + * + * Return 0 if successful + * -1 if failed (errno tells why) + */ + +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) +{ + u64 newsize; + u64 oldsize; + le16 appended_bytes; + u16 padding_length; + ntfs_inode *ni; + BOOL close_ctx = FALSE; + + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); + goto err_out; + } + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + goto err_out; + } + close_ctx = TRUE; + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; + } + } + + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; + + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } + } + ni = na->ni; + if (!na->name_len) { + ni->data_size = newsize; + ni->allocated_size = na->allocated_size; + } + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + + ctx->attr->data_size = cpu_to_le64(newsize); + if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->initialized_size = ctx->attr->data_size; + ctx->attr->flags |= ATTR_IS_ENCRYPTED; + if (close_ctx) + ntfs_attr_put_search_ctx(ctx); + + return (0); +err_out: + if (close_ctx && ctx) + ntfs_attr_put_search_ctx(ctx); + return (-1); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/orig/source/efs.h b/lib/libntfs/orig/source/efs.h new file mode 100644 index 0000000..6eada06 --- /dev/null +++ b/lib/libntfs/orig/source/efs.h @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2009 Martin Bene + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFS_H +#define EFS_H + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); + +#endif /* EFS_H */ diff --git a/lib/libntfs/orig/source/endians.h b/lib/libntfs/orig/source/endians.h new file mode 100644 index 0000000..397f1c2 --- /dev/null +++ b/lib/libntfs/orig/source/endians.h @@ -0,0 +1,203 @@ +/* + * endians.h - Definitions related to handling of byte ordering. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ENDIANS_H +#define _NTFS_ENDIANS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * Notes: + * We define the conversion functions including typecasts since the + * defaults don't necessarily perform appropriate typecasts. + * Also, using our own functions means that we can change them if it + * turns out that we do need to use the unaligned access macros on + * architectures requiring aligned memory accesses... + */ + +#ifdef HAVE_ENDIAN_H +#include +#endif +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif +#ifdef HAVE_MACHINE_ENDIAN_H +#include +#endif +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef __BYTE_ORDER +# if defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# elif defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(__BYTE_ORDER__) +# define __BYTE_ORDER __BYTE_ORDER__ +# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ +# define __BIG_ENDIAN __BIG_ENDIAN__ +# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ + defined(WORDS_LITTLEENDIAN) +# define __BYTE_ORDER 1 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ + defined(WORDS_BIGENDIAN) +# define __BYTE_ORDER 0 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# else +# error "__BYTE_ORDER is not defined." +# endif +#endif + +#define __ntfs_bswap_constant_16(x) \ + (u16)((((u16)(x) & 0xff00) >> 8) | \ + (((u16)(x) & 0x00ff) << 8)) + +#define __ntfs_bswap_constant_32(x) \ + (u32)((((u32)(x) & 0xff000000u) >> 24) | \ + (((u32)(x) & 0x00ff0000u) >> 8) | \ + (((u32)(x) & 0x0000ff00u) << 8) | \ + (((u32)(x) & 0x000000ffu) << 24)) + +#define __ntfs_bswap_constant_64(x) \ + (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ + (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ + (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ + (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ + (((u64)(x) & 0x00000000ff000000ull) << 8) | \ + (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ + (((u64)(x) & 0x000000000000ff00ull) << 40) | \ + (((u64)(x) & 0x00000000000000ffull) << 56)) + +#ifdef HAVE_BYTESWAP_H +# include +#else +# define bswap_16(x) __ntfs_bswap_constant_16(x) +# define bswap_32(x) __ntfs_bswap_constant_32(x) +# define bswap_64(x) __ntfs_bswap_constant_64(x) +#endif + +#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) + +#define __le16_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le64_to_cpu(x) (x) + +#define __cpu_to_le16(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le64(x) (x) + +#define __constant_le16_to_cpu(x) (x) +#define __constant_le32_to_cpu(x) (x) +#define __constant_le64_to_cpu(x) (x) + +#define __constant_cpu_to_le16(x) (x) +#define __constant_cpu_to_le32(x) (x) +#define __constant_cpu_to_le64(x) (x) + +#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) + +#define __le16_to_cpu(x) bswap_16(x) +#define __le32_to_cpu(x) bswap_32(x) +#define __le64_to_cpu(x) bswap_64(x) + +#define __cpu_to_le16(x) bswap_16(x) +#define __cpu_to_le32(x) bswap_32(x) +#define __cpu_to_le64(x) bswap_64(x) + +#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) + +#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) + +#else + +#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." + +#endif + +/* Unsigned from LE to CPU conversion. */ + +#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) +#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) +#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) + +#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) +#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) +#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) + +/* Signed from LE to CPU conversion. */ + +#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) +#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) +#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) + +#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) +#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) +#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) + +/* Unsigned from CPU to LE conversion. */ + +#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) +#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) +#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) + +#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) +#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) +#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) + +/* Signed from CPU to LE conversion. */ + +#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) +#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) +#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) + +#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) +#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) +#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) + +/* Constant endianness conversion defines. */ + +#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) +#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) +#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) + +#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) +#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) +#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) + +#endif /* defined _NTFS_ENDIANS_H */ diff --git a/lib/libntfs/orig/source/gekko_io.c b/lib/libntfs/orig/source/gekko_io.c new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/lib/libntfs/orig/source/gekko_io.c @@ -0,0 +1,658 @@ +/** + * gekko_io.c - Gekko style disk io functions. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "ntfs.h" +#include "types.h" +#include "logging.h" +#include "device_io.h" +#include "gekko_io.h" +#include "cache.h" +#include "device.h" +#include "bootsect.h" + +#define DEV_FD(dev) ((gekko_fd *)dev->d_private) + +/* Prototypes */ +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf); +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer); +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf); +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer); + +/** + * + */ +static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) +{ + ntfs_log_trace("dev %p, flags %i\n", dev, flags); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Start the device interface and ensure that it is inserted + if (!interface->startup()) { + ntfs_log_perror("device failed to start\n"); + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + ntfs_log_perror("device media is not inserted\n"); + errno = EIO; + return -1; + } + + // Check that the device isn't already open (used by another volume?) + if (NDevOpen(dev)) { + ntfs_log_perror("device is busy (already open)\n"); + errno = EBUSY; + return -1; + } + + // Check that there is a valid NTFS boot sector at the start of the device + NTFS_BOOT_SECTOR *boot = (NTFS_BOOT_SECTOR *) ntfs_alloc(MAX_SECTOR_SIZE); + if(boot == NULL) { + errno = ENOMEM; + return -1; + } + + if (!interface->readSectors(fd->startSector, 1, boot)) { + ntfs_log_perror("read failure @ sector %d\n", fd->startSector); + errno = EIO; + ntfs_free(boot); + return -1; + } + + if (!ntfs_boot_sector_is_ntfs(boot)) { + errno = EINVALPART; + ntfs_free(boot); + return -1; + } + + // Parse the boot sector + fd->hiddenSectors = le32_to_cpu(boot->bpb.hidden_sectors); + fd->sectorSize = le16_to_cpu(boot->bpb.bytes_per_sector); + fd->sectorCount = sle64_to_cpu(boot->number_of_sectors); + fd->pos = 0; + fd->len = (fd->sectorCount * fd->sectorSize); + fd->ino = le64_to_cpu(boot->volume_serial_number); + + // Free memory for boot sector + ntfs_free(boot); + + // Mark the device as read-only (if required) + if (flags & O_RDONLY) { + NDevSetReadOnly(dev); + } + + // Create the cache + fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); + + // Mark the device as open + NDevSetBlock(dev); + NDevSetOpen(dev); + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_close(struct ntfs_device *dev) +{ + ntfs_log_trace("dev %p\n", dev); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Check that the device is actually open + if (!NDevOpen(dev)) { + ntfs_log_perror("device is not open\n"); + errno = EIO; + return -1; + } + + // Mark the device as closed + NDevClearOpen(dev); + NDevClearBlock(dev); + + // Flush the device (if dirty and not read-only) + if (NDevDirty(dev) && !NDevReadOnly(dev)) { + ntfs_log_debug("device is dirty, will now sync\n"); + + // ...? + + // Mark the device as clean + NDevClearDirty(dev); + + } + + // Flush and destroy the cache (if required) + if (fd->cache) { + _NTFS_cache_flush(fd->cache); + _NTFS_cache_destructor(fd->cache); + } + + // Shutdown the device interface + /*const DISC_INTERFACE* interface = fd->interface; + if (interface) { + interface->shutdown(); + }*/ + + // Free the device driver private data + ntfs_free(dev->d_private); + dev->d_private = NULL; + + return 0; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_seek(struct ntfs_device *dev, s64 offset, int whence) +{ + ntfs_log_trace("dev %p, offset %Li, whence %i\n", dev, offset, whence); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Set the current position on the device (in bytes) + switch(whence) { + case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break; + case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break; + case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break; + } + + return 0; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_read(struct ntfs_device *dev, void *buf, s64 count) +{ + return ntfs_device_gekko_io_readbytes(dev, DEV_FD(dev)->pos, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_write(struct ntfs_device *dev, const void *buf, s64 count) +{ + return ntfs_device_gekko_io_writebytes(dev, DEV_FD(dev)->pos, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset) +{ + return ntfs_device_gekko_io_readbytes(dev, offset, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_pwrite(struct ntfs_device *dev, const void *buf, s64 count, s64 offset) +{ + return ntfs_device_gekko_io_writebytes(dev, offset, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf) +{ + ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(offset < 0) + { + errno = EROFS; + return -1; + } + + if(!count) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this read + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // If this read happens to be on the sector boundaries then do the read straight into the destination buffer + + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { + + // Read from the device + ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + + // Else read into a buffer and copy over only what was requested + } + else + { + + // Allocate a buffer to hold the read data + buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + + // Read from the device + ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_free(buffer); + errno = EIO; + return -1; + } + + // Copy what was requested to the destination buffer + memcpy(buf, buffer + buffer_offset, count); + ntfs_free(buffer); + + } + + return count; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf) +{ + ntfs_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Check that the device can be written to + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this write + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // If this write happens to be on the sector boundaries then do the write straight to disc + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Write to the device + ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else write from a buffer aligned to the sector boundaries + } + else + { + // Allocate a buffer to hold the write data + buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + // Read the first and last sectors of the buffer from disc (if required) + // NOTE: This is done because the data does not line up with the sector boundaries, + // we just read in the buffer edges where the data overlaps with the rest of the disc + if(buffer_offset != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ntfs_log_perror("read failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + + // Copy the data into the write buffer + memcpy(buffer + buffer_offset, buf, count); + + // Write to the device + ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered write failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + + // Free the buffer + ntfs_free(buffer); + } + + // Mark the device as dirty (if we actually wrote anything) + NDevSetDirty(dev); + + return count; +} + +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + // Read the sectors from disc (or cache, if enabled) + if (fd->cache) + return _NTFS_cache_readSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->readSectors(sector, numSectors, buffer); + + return false; +} + +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + + // Write the sectors to disc (or cache, if enabled) + if (fd->cache) + return _NTFS_cache_writeSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->writeSectors(sector, numSectors, buffer); + + return false; +} + +/** + * + */ +static int ntfs_device_gekko_io_sync(struct ntfs_device *dev) +{ + gekko_fd *fd = DEV_FD(dev); + ntfs_log_trace("dev %p\n", dev); + + // Check that the device can be written to + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + + // Mark the device as clean + NDevClearDirty(dev); + NDevClearSync(dev); + + // Flush any sectors in the disc cache (if required) + if (fd->cache) { + if (!_NTFS_cache_flush(fd->cache)) { + errno = EIO; + return -1; + } + } + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_stat(struct ntfs_device *dev, struct stat *buf) +{ + ntfs_log_trace("dev %p, buf %p\n", dev, buf); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Build the device mode + mode_t mode = (S_IFBLK) | + (S_IRUSR | S_IRGRP | S_IROTH) | + ((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct stat)); + + // Build the device stats + buf->st_dev = fd->interface->ioType; + buf->st_ino = fd->ino; + buf->st_mode = mode; + buf->st_rdev = fd->interface->ioType; + buf->st_blksize = fd->sectorSize; + buf->st_blocks = fd->sectorCount; + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void *argp) +{ + ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Figure out which i/o control was requested + switch (request) { + + // Get block device size (sectors) + #if defined(BLKGETSIZE) + case BLKGETSIZE: { + *(u32*)argp = fd->sectorCount; + return 0; + } + #endif + + // Get block device size (bytes) + #if defined(BLKGETSIZE64) + case BLKGETSIZE64: { + *(u64*)argp = (fd->sectorCount * fd->sectorSize); + return 0; + } + #endif + + // Get hard drive geometry + #if defined(HDIO_GETGEO) + case HDIO_GETGEO: { + struct hd_geometry *geo = (struct hd_geometry*)argp; + geo->sectors = 0; + geo->heads = 0; + geo->cylinders = 0; + geo->start = fd->hiddenSectors; + return -1; + } + #endif + + // Get block device sector size (bytes) + #if defined(BLKSSZGET) + case BLKSSZGET: { + *(int*)argp = fd->sectorSize; + return 0; + } + #endif + + // Set block device block size (bytes) + #if defined(BLKBSZSET) + case BLKBSZSET: { + int sectorSize = *(int*)argp; + fd->sectorSize = sectorSize; + return 0; + } + #endif + + // Unimplemented ioctrl + default: { + ntfs_log_perror("Unimplemented ioctrl %i\n", request); + errno = EOPNOTSUPP; + return -1; + } + + } + + return 0; +} + +/** + * Device operations for working with gekko style devices and files. + */ +struct ntfs_device_operations ntfs_device_gekko_io_ops = { + .open = ntfs_device_gekko_io_open, + .close = ntfs_device_gekko_io_close, + .seek = ntfs_device_gekko_io_seek, + .read = ntfs_device_gekko_io_read, + .write = ntfs_device_gekko_io_write, + .pread = ntfs_device_gekko_io_pread, + .pwrite = ntfs_device_gekko_io_pwrite, + .sync = ntfs_device_gekko_io_sync, + .stat = ntfs_device_gekko_io_stat, + .ioctl = ntfs_device_gekko_io_ioctl, +}; diff --git a/lib/libntfs/orig/source/gekko_io.h b/lib/libntfs/orig/source/gekko_io.h new file mode 100644 index 0000000..bc5516a --- /dev/null +++ b/lib/libntfs/orig/source/gekko_io.h @@ -0,0 +1,58 @@ +/* +* gekko_io.h - Platform specifics for device io. +* +* Copyright (c) 2009 Rhys "Shareese" Koedijk +* +* This program/include file is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published +* by the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program/include file is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _GEKKO_IO_H +#define _GEKKO_IO_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "types.h" +#include "cache2.h" +#include +#include + +#define MAX_SECTOR_SIZE 4096 + +/** + * gekko_fd - Gekko device driver descriptor + */ +typedef struct _gekko_fd { + const DISC_INTERFACE* interface; /* Device disc interface */ + sec_t startSector; /* LBA of partition start */ + sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */ + u16 sectorSize; /* Device sector size (in bytes) */ + u64 sectorCount; /* Total number of sectors in partition */ + u64 pos; /* Current position within the partition (in bytes) */ + u64 len; /* Total length of partition (in bytes) */ + ino_t ino; /* Device identifier */ + NTFS_CACHE *cache; /* Cache */ + u32 cachePageCount; /* The number of pages in the cache */ + u32 cachePageSize; /* The number of sectors per cache page */ +} gekko_fd; + +/* Forward declarations */ +struct ntfs_device_operations; + +/* Gekko device driver i/o operations */ +extern struct ntfs_device_operations ntfs_device_gekko_io_ops; + +#endif /* _GEKKO_IO_H */ diff --git a/lib/libntfs/orig/source/index.c b/lib/libntfs/orig/source/index.c new file mode 100644 index 0000000..01438a0 --- /dev/null +++ b/lib/libntfs/orig/source/index.c @@ -0,0 +1,2085 @@ +/** + * index.c - NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2007 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "index.h" +#include "collate.h" +#include "mst.h" +#include "dir.h" +#include "logging.h" +#include "bitmap.h" +#include "reparse.h" +#include "misc.h" + +/** + * ntfs_index_entry_mark_dirty - mark an index entry dirty + * @ictx: ntfs index context describing the index entry + * + * Mark the index entry described by the index entry context @ictx dirty. + * + * If the index entry is in the index root attribute, simply mark the inode + * containing the index root attribute dirty. This ensures the mftrecord, and + * hence the index root attribute, will be written out to disk later. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, set ib_dirty to TRUE, thus index block will be updated during + * ntfs_index_ctx_put. + */ +void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + ntfs_inode_mark_dirty(ictx->actx->ntfs_ino); + else + ictx->ib_dirty = TRUE; +} + +static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return vcn << icx->vcn_size_bits; +} + +static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return pos >> icx->vcn_size_bits; +} + +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), + 1, icx->block_size, ib); + if (ret != 1) { + ntfs_log_perror("Failed to write index block %lld, inode %llu", + (long long)vcn, (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_icx_ib_write(ntfs_index_context *icx) +{ + if (ntfs_ib_write(icx, icx->ib)) + return STATUS_ERROR; + + icx->ib_dirty = FALSE; + + return STATUS_OK; +} + +/** + * ntfs_index_ctx_get - allocate and initialize a new index context + * @ni: ntfs inode with which to initialize the context + * @name: name of the which context describes + * @name_len: length of the index name + * + * Allocate a new index context, initialize it with @ni and return it. + * Return NULL if allocation failed. + */ +ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len) +{ + ntfs_index_context *icx; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + errno = EINVAL; + return NULL; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + icx = ntfs_calloc(sizeof(ntfs_index_context)); + if (icx) + *icx = (ntfs_index_context) { + .ni = ni, + .name = name, + .name_len = name_len, + }; + return icx; +} + +static void ntfs_index_ctx_free(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (!icx->entry) + return; + + if (icx->actx) + ntfs_attr_put_search_ctx(icx->actx); + + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); + } + + ntfs_attr_close(icx->ia_na); +} + +/** + * ntfs_index_ctx_put - release an index context + * @icx: index context to free + * + * Release the index context @icx, releasing all associated resources. + */ +void ntfs_index_ctx_put(ntfs_index_context *icx) +{ + ntfs_index_ctx_free(icx); + free(icx); +} + +/** + * ntfs_index_ctx_reinit - reinitialize an index context + * @icx: index context to reinitialize + * + * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. + */ +void ntfs_index_ctx_reinit(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + ntfs_index_ctx_free(icx); + + *icx = (ntfs_index_context) { + .ni = icx->ni, + .name = icx->name, + .name_len = icx->name_len, + }; +} + +static VCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) +{ + return (VCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); +} + +/** + * Get the subnode vcn to which the index entry refers. + */ +VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) +{ + return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie)); +} + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); +} + +static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie) +{ + return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length)); +} + +static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) +{ + /* FIXME: check if it isn't overflowing the index block size */ + return (u8 *)ih + le32_to_cpu(ih->index_length); +} + +static int ntfs_ie_end(INDEX_ENTRY *ie) +{ + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; +} + +/** + * Find the last entry in the index block + */ +static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end) +{ + ntfs_log_trace("Entering\n"); + + while ((char *)ie < ies_end && !ntfs_ie_end(ie)) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("pos: %d\n", pos); + + ie = ntfs_ie_get_first(ih); + + while (pos-- > 0) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + INDEX_ENTRY *ie_prev = NULL; + INDEX_ENTRY *tmp; + + ntfs_log_trace("Entering\n"); + + tmp = ntfs_ie_get_first(ih); + + while (tmp != ie) { + ie_prev = tmp; + tmp = ntfs_ie_get_next(tmp); + } + + return ie_prev; +} + +char *ntfs_ie_filename_get(INDEX_ENTRY *ie) +{ + FILE_NAME_ATTR *fn; + + fn = (FILE_NAME_ATTR *)&ie->key; + return ntfs_attr_name_get(fn->file_name, fn->file_name_length); +} + +void ntfs_ie_filename_dump(INDEX_ENTRY *ie) +{ + char *s; + + s = ntfs_ie_filename_get(ie); + ntfs_log_debug("'%s' ", s); + ntfs_attr_name_free(&s); +} + +void ntfs_ih_filename_dump(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + while (!ntfs_ie_end(ie)) { + ntfs_ie_filename_dump(ie); + ie = ntfs_ie_get_next(ie); + } +} + +static int ntfs_ih_numof_entries(INDEX_HEADER *ih) +{ + int n; + INDEX_ENTRY *ie; + u8 *end; + + ntfs_log_trace("Entering\n"); + + end = ntfs_ie_get_end(ih); + ie = ntfs_ie_get_first(ih); + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) + ie = ntfs_ie_get_next(ie); + return n; +} + +static int ntfs_ih_one_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 1); +} + +static int ntfs_ih_zero_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 0); +} + +static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + u32 new_size; + + ntfs_log_trace("Entering\n"); + + new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); + ih->index_length = cpu_to_le32(new_size); + memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), + new_size - ((u8 *)ie - (u8 *)ih)); +} + +static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) +{ + *ntfs_ie_get_vcn_addr(ie) = cpu_to_le64(vcn); +} + +/** + * Insert @ie index entry at @pos entry. Used @ih values should be ok already. + */ +static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos) +{ + int ie_size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); + memmove((u8 *)pos + ie_size, pos, + le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size); + memcpy(pos, ie, ie_size); +} + +static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + + ntfs_log_trace("Entering\n"); + + dup = ntfs_malloc(le16_to_cpu(ie->length)); + if (dup) + memcpy(dup, ie, le16_to_cpu(ie->length)); + + return dup; +} + +static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + int size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + if (ie->ie_flags & INDEX_ENTRY_NODE) + size -= sizeof(VCN); + + dup = ntfs_malloc(size); + if (dup) { + memcpy(dup, ie, size); + dup->ie_flags &= ~INDEX_ENTRY_NODE; + dup->length = cpu_to_le16(size); + } + return dup; +} + +static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) +{ + u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; + + ntfs_log_trace("Entering\n"); + + if (!ntfs_is_indx_record(ib->magic)) { + + ntfs_log_error("Corrupt index block signature: vcn %lld inode " + "%llu\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (sle64_to_cpu(ib->index_block_vcn) != vcn) { + + ntfs_log_error("Corrupt index block: VCN (%lld) is different " + "from expected VCN (%lld) in inode %llu\n", + (long long)sle64_to_cpu(ib->index_block_vcn), + (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (ib_size != icx->block_size) { + + ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " + "has a size (%u) differing from the index " + "specified size (%u)\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no, ib_size, + icx->block_size); + return -1; + } + return 0; +} + +static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, + u32 name_len, ntfs_attr_search_ctx **ctx) +{ + ATTR_RECORD *a; + INDEX_ROOT *ir = NULL; + + ntfs_log_trace("Entering\n"); + + *ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!*ctx) + return NULL; + + if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) { + ntfs_log_perror("Failed to lookup $INDEX_ROOT"); + goto err_out; + } + + a = (*ctx)->attr; + if (a->non_resident) { + errno = EINVAL; + ntfs_log_perror("Non-resident $INDEX_ROOT detected"); + goto err_out; + } + + ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); +err_out: + if (!ir) { + ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; +} + +static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) +{ + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); + return ir; +} + +/** + * Find a key in the index block. + * + * Return values: + * STATUS_OK with errno set to ESUCCESS if we know for sure that the + * entry exists and @ie_out points to this entry. + * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the + * entry doesn't exist and @ie_out is the insertion point. + * STATUS_KEEP_SEARCHING if we can't answer the above question and + * @vcn will contain the node index block. + * STATUS_ERROR with errno set if on unexpected error during lookup. + */ +static int ntfs_ie_lookup(const void *key, const int key_len, + ntfs_index_context *icx, INDEX_HEADER *ih, + VCN *vcn, INDEX_ENTRY **ie_out) +{ + INDEX_ENTRY *ie; + u8 *index_end; + int rc, item = 0; + + ntfs_log_trace("Entering\n"); + + index_end = ntfs_ie_get_end(ih); + + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { + /* Bounds checks. */ + if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + errno = ERANGE; + ntfs_log_error("Index entry out of bounds in inode " + "%llu.\n", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ntfs_ie_end(ie)) + break; + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); + if (rc == NTFS_COLLATION_ERROR) { + ntfs_log_error("Collation error. Perhaps a filename " + "contains invalid characters?\n"); + errno = ERANGE; + return STATUS_ERROR; + } + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + + if (!rc) { + *ie_out = ie; + errno = 0; + icx->parent_pos[icx->pindex] = item; + return STATUS_OK; + } + + item++; + } + /* + * We have finished with this index block without success. Check for the + * presence of a child node and if not present return with errno ENOENT, + * otherwise we will keep searching in another index block. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_log_debug("Index entry wasn't found.\n"); + *ie_out = ie; + errno = ENOENT; + return STATUS_NOT_FOUND; + } + + /* Get the starting vcn of the index_block holding the child node. */ + *vcn = ntfs_ie_get_vcn(ie); + if (*vcn < 0) { + errno = EINVAL; + ntfs_log_perror("Negative vcn in inode %llu", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + ntfs_log_trace("Parent entry number %d\n", item); + icx->parent_pos[icx->pindex] = item; + + return STATUS_KEEP_SEARCHING; +} + +static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) +{ + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open index allocation of inode " + "%llu", (unsigned long long)ni->mft_no); + return NULL; + } + + return na; +} + +static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) +{ + s64 pos, ret; + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + pos = ntfs_ib_vcn_to_pos(icx, vcn); + + ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, (u8 *)dst); + if (ret != 1) { + if (ret == -1) + ntfs_log_perror("Failed to read index block"); + else + ntfs_log_error("Failed to read full index block at " + "%lld\n", (long long)pos); + return -1; + } + + if (ntfs_ia_check(icx, dst, vcn)) + return -1; + + return 0; +} + +static int ntfs_icx_parent_inc(ntfs_index_context *icx) +{ + icx->pindex++; + if (icx->pindex >= MAX_PARENT_VCN) { + errno = EOPNOTSUPP; + ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN); + return STATUS_ERROR; + } + return STATUS_OK; +} + +static int ntfs_icx_parent_dec(ntfs_index_context *icx) +{ + icx->pindex--; + if (icx->pindex < 0) { + errno = EINVAL; + ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex); + return STATUS_ERROR; + } + return STATUS_OK; +} + +/** + * ntfs_index_lookup - find a key in an index and return its index entry + * @key: [IN] key for which to search in the index + * @key_len: [IN] length of @key in bytes + * @icx: [IN/OUT] context describing the index and the returned entry + * + * Before calling ntfs_index_lookup(), @icx must have been obtained from a + * call to ntfs_index_ctx_get(). + * + * Look for the @key in the index specified by the index lookup context @icx. + * ntfs_index_lookup() walks the contents of the index looking for the @key. + * + * If the @key is found in the index, 0 is returned and @icx is setup to + * describe the index entry containing the matching @key. @icx->entry is the + * index entry and @icx->data and @icx->data_len are the index entry data and + * its length in bytes, respectively. + * + * If the @key is not found in the index, -1 is returned, errno = ENOENT and + * @icx is setup to describe the index entry whose key collates immediately + * after the search @key, i.e. this is the position in the index at which + * an index entry with a key of @key would need to be inserted. + * + * If an error occurs return -1, set errno to error code and @icx is left + * untouched. + * + * When finished with the entry and its data, call ntfs_index_ctx_put() to free + * the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) +{ + VCN old_vcn, vcn; + ntfs_inode *ni = icx->ni; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + int ret, err = 0; + + ntfs_log_trace("Entering\n"); + + if (!key || key_len <= 0) { + errno = EINVAL; + ntfs_log_perror("key: %p key_len: %d", key, key_len); + return -1; + } + + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); + if (!ir) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + icx->block_size = le32_to_cpu(ir->index_block_size); + if (icx->block_size < NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("Index block size (%d) is smaller than the " + "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); + goto err_out; + } + + if (ni->vol->cluster_size <= icx->block_size) + icx->vcn_size_bits = ni->vol->cluster_size_bits; + else + icx->vcn_size_bits = ni->vol->sector_size_bits; + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { + err = errno = EOPNOTSUPP; + ntfs_log_perror("Unknown collation rule 0x%x", + (unsigned)le32_to_cpu(ir->collation_rule)); + goto err_out; + } + + old_vcn = VCN_INDEX_ROOT_PARENT; + /* + * FIXME: check for both ir and ib that the first index entry is + * within the index block. + */ + ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); + if (ret == STATUS_ERROR) { + err = errno; + goto err_out; + } + + icx->ir = ir; + + if (ret != STATUS_KEEP_SEARCHING) { + /* STATUS_OK or STATUS_NOT_FOUND */ + err = errno; + icx->is_in_root = TRUE; + icx->parent_vcn[icx->pindex] = old_vcn; + goto done; + } + + /* Child node present, descend into it. */ + + icx->ia_na = ntfs_ia_open(icx, ni); + if (!icx->ia_na) + goto err_out; + + ib = ntfs_malloc(icx->block_size); + if (!ib) { + err = errno; + goto err_out; + } + +descend_into_child_node: + + icx->parent_vcn[icx->pindex] = old_vcn; + if (ntfs_icx_parent_inc(icx)) { + err = errno; + goto err_out; + } + old_vcn = vcn; + + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); + + if (ntfs_ib_read(icx, vcn, ib)) + goto err_out; + + ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); + if (ret != STATUS_KEEP_SEARCHING) { + err = errno; + if (ret == STATUS_ERROR) + goto err_out; + + /* STATUS_OK or STATUS_NOT_FOUND */ + icx->is_in_root = FALSE; + icx->ib = ib; + icx->parent_vcn[icx->pindex] = vcn; + goto done; + } + + if ((ib->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in inode 0x%llx.\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + + goto descend_into_child_node; +err_out: + free(ib); + if (!err) + err = EIO; + errno = err; + return -1; +done: + icx->entry = ie; + icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); + icx->data_len = le16_to_cpu(ie->key_length); + ntfs_log_trace("Done.\n"); + if (err) { + errno = err; + return -1; + } + return 0; + +} + +static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, + INDEX_HEADER_FLAGS node_type) +{ + INDEX_BLOCK *ib; + int ih_size = sizeof(INDEX_HEADER); + + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); + + ib = ntfs_calloc(ib_size); + if (!ib) + return NULL; + + ib->magic = magic_INDX; + ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); + ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); + /* Set USN to 1 */ + *(u16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); + ib->lsn = cpu_to_le64(0); + + ib->index_block_vcn = cpu_to_sle64(ib_vcn); + + ib->index.entries_offset = cpu_to_le32((ih_size + + le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); + ib->index.index_length = 0; + ib->index.allocated_size = cpu_to_le32(ib_size - + (sizeof(INDEX_BLOCK) - ih_size)); + ib->index.ih_flags = node_type; + + return ib; +} + +/** + * Find the median by going through all the entries + */ +static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie, *ie_start; + u8 *ie_end; + int i = 0, median; + + ntfs_log_trace("Entering\n"); + + ie = ie_start = ntfs_ie_get_first(ih); + ie_end = (u8 *)ntfs_ie_get_end(ih); + + while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { + ie = ntfs_ie_get_next(ie); + i++; + } + /* + * NOTE: this could be also the entry at the half of the index block. + */ + median = i / 2 - 1; + + ntfs_log_trace("Entries: %d median: %d\n", i, median); + + for (i = 0, ie = ie_start; i <= median; i++) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size; +} + +static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); +} + +static int ntfs_ibm_add(ntfs_index_context *icx) +{ + u8 bmp[8]; + + ntfs_log_trace("Entering\n"); + + if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) + return STATUS_OK; + /* + * AT_BITMAP must be at least 8 bytes. + */ + memset(bmp, 0, sizeof(bmp)); + if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, + bmp, sizeof(bmp))) { + ntfs_log_perror("Failed to add AT_BITMAP"); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) +{ + u8 byte; + s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); + u32 bpos = pos / 8; + u32 bit = 1 << (pos % 8); + ntfs_attr *na; + int ret = STATUS_ERROR; + + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); + + na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open $BITMAP attribute"); + return -1; + } + + if (set) { + if (na->data_size < bpos + 1) { + if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { + ntfs_log_perror("Failed to truncate AT_BITMAP"); + goto err_na; + } + } + } + + if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to read $BITMAP"); + goto err_na; + } + + if (set) + byte |= bit; + else + byte &= ~bit; + + if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to write $Bitmap"); + goto err_na; + } + + ret = STATUS_OK; +err_na: + ntfs_attr_close(na); + return ret; +} + + +static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 1); +} + +static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 0); +} + +static VCN ntfs_ibm_get_free(ntfs_index_context *icx) +{ + u8 *bm; + int bit; + s64 vcn, byte, size; + + ntfs_log_trace("Entering\n"); + + bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len, + &size); + if (!bm) + return (VCN)-1; + + for (byte = 0; byte < size; byte++) { + + if (bm[byte] == 255) + continue; + + for (bit = 0; bit < 8; bit++) { + if (!(bm[byte] & (1 << bit))) { + vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); + goto out; + } + } + } + + vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); +out: + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); + + if (ntfs_ibm_set(icx, vcn)) + vcn = (VCN)-1; + + free(bm); + return vcn; +} + +static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn) +{ + INDEX_BLOCK *ib; + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + int i; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), LEAF_NODE); + if (!ib) + return NULL; + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Copy all entries, including the termination entry + * as well, which can never have any data. + */ + i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); + memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); + + ib->index.ih_flags = ir->index.ih_flags; + ib->index.index_length = cpu_to_le32(i + + le32_to_cpu(ib->index.entries_offset)); + return ib; +} + +static void ntfs_ir_nill(INDEX_ROOT *ir) +{ + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + + ntfs_log_trace("Entering\n"); + /* + * TODO: This function could be much simpler. + */ + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Move the index root termination entry forward + */ + if ((char *)ie_last > ies_start) { + memmove(ies_start, (char *)ie_last, le16_to_cpu(ie_last->length)); + ie_last = (INDEX_ENTRY *)ies_start; + } +} + +static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, + INDEX_ENTRY *median, VCN new_vcn) +{ + u8 *ies_end; + INDEX_ENTRY *ie_head; /* first entry after the median */ + int tail_size, ret; + INDEX_BLOCK *dst; + + ntfs_log_trace("Entering\n"); + + dst = ntfs_ib_alloc(new_vcn, icx->block_size, + src->index.ih_flags & NODE_MASK); + if (!dst) + return STATUS_ERROR; + + ie_head = ntfs_ie_get_next(median); + + ies_end = (u8 *)ntfs_ie_get_end(&src->index); + tail_size = ies_end - (u8 *)ie_head; + memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); + + dst->index.index_length = cpu_to_le32(tail_size + + le32_to_cpu(dst->index.entries_offset)); + ret = ntfs_ib_write(icx, dst); + + free(dst); + return ret; +} + +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, + INDEX_ENTRY *ie) +{ + char *ies_start, *ies_end; + INDEX_ENTRY *ie_last; + + ntfs_log_trace("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); + + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + if (ie_last->ie_flags & INDEX_ENTRY_NODE) + ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); + + memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); + + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); + + if (ntfs_ib_write(icx, ib)) + return STATUS_ERROR; + + return STATUS_OK; +} + +static int ntfs_ia_add(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (ntfs_ibm_add(icx)) + return -1; + + if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { + + if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, + icx->name_len, NULL, 0)) { + ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); + return -1; + } + } + + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return -1; + + return 0; +} + +static int ntfs_ir_reparent(ntfs_index_context *icx) +{ + ntfs_attr_search_ctx *ctx = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + VCN new_ib_vcn; + int ix_root_size; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto out; + + if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) + if (ntfs_ia_add(icx)) + goto out; + + new_ib_vcn = ntfs_ibm_get_free(icx); + if (new_ib_vcn == -1) + goto out; + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto clear_bmp; + + ib = ntfs_ir_to_ib(ir, new_ib_vcn); + if (ib == NULL) { + ntfs_log_perror("Failed to move index root to index block"); + goto clear_bmp; + } + + if (ntfs_ib_write(icx, ib)) + goto clear_bmp; + +retry : + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + if (!ir) + goto clear_bmp; + + ntfs_ir_nill(ir); + + ie = ntfs_ie_get_first(&ir->index); + ie->ie_flags |= INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); + + ir->index.ih_flags = LARGE_INDEX; + ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + + le16_to_cpu(ie->length)); + ir->index.allocated_size = ir->index.index_length; + ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + + le32_to_cpu(ir->index.allocated_size); + if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + ix_root_size)) { + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((errno == ENOSPC) + && !ctx->al_entry + && !ntfs_inode_add_attrlist(icx->ni)) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, + &ctx); + if (ir + && !ntfs_attr_record_move_away(ctx, ix_root_size + - le32_to_cpu(ctx->attr->value_length))) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + goto retry; + } + } + /* FIXME: revert index root */ + goto clear_bmp; + } + /* + * FIXME: do it earlier if we have enough space in IR (should always), + * so in error case we wouldn't lose the IB. + */ + ntfs_ie_set_vcn(ie, new_ib_vcn); + + ret = STATUS_OK; +err_out: + free(ib); + ntfs_attr_put_search_ctx(ctx); +out: + return ret; +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; +} + +/** + * ntfs_ir_truncate - Truncate index root attribute + * + * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. + */ +static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open INDEX_ROOT"); + return STATUS_ERROR; + } + /* + * INDEX_ROOT must be resident and its entries can be moved to + * INDEX_BLOCK, so ENOSPC isn't a real error. + */ + ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); + if (ret == STATUS_OK) { + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + icx->ir->index.allocated_size = cpu_to_le32(data_size); + + } else if (ret == STATUS_ERROR) + ntfs_log_perror("Failed to truncate INDEX_ROOT"); + + ntfs_attr_close(na); + return ret; +} + +/** + * ntfs_ir_make_space - Make more space for the index root attribute + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) +{ + int ret; + + ntfs_log_trace("Entering\n"); + + ret = ntfs_ir_truncate(icx, data_size); + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); + } + + return ret; +} + +/* + * NOTE: 'ie' must be a copy of a real index entry. + */ +static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) +{ + INDEX_ENTRY *p, *old = *ie; + + old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); + p = realloc(old, le16_to_cpu(old->length)); + if (!p) + return STATUS_ERROR; + + p->ie_flags |= INDEX_ENTRY_NODE; + *ie = p; + + return STATUS_OK; +} + +static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, + int pos) +{ + INDEX_ENTRY *ie_node, *ie; + int ret = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_dup(orig_ie); + if (!ie) + return STATUS_ERROR; + + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) + if (ntfs_ie_add_vcn(&ie)) + goto out; + + ie_node = ntfs_ie_get_by_pos(ih, pos); + old_vcn = ntfs_ie_get_vcn(ie_node); + ntfs_ie_set_vcn(ie_node, new_vcn); + + ntfs_ie_insert(ih, ie, ie_node); + ntfs_ie_set_vcn(ie_node, old_vcn); + ret = STATUS_OK; +out: + free(ie); + + return ret; +} + +static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx) +{ + return icx->parent_vcn[icx->pindex]; +} + +static VCN ntfs_icx_parent_pos(ntfs_index_context *icx) +{ + return icx->parent_pos[icx->pindex]; +} + + +static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, + VCN new_vcn) +{ + u32 new_size; + int ret; + + ntfs_log_trace("Entering\n"); + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + new_size = le32_to_cpu(icx->ir->index.index_length) + + le16_to_cpu(median->length); + if (!(median->ie_flags & INDEX_ENTRY_NODE)) + new_size += sizeof(VCN); + + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + return ret; + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + return ntfs_ih_insert(&icx->ir->index, median, new_vcn, + ntfs_icx_parent_pos(icx)); +} + +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib); + +/** + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) +{ + INDEX_BLOCK *ib; + u32 idx_size, allocated_size; + int err = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return -1; + + old_vcn = ntfs_icx_parent_vcn(icx); + + if (ntfs_ib_read(icx, old_vcn, ib)) + goto err_out; + + idx_size = le32_to_cpu(ib->index.index_length); + allocated_size = le32_to_cpu(ib->index.allocated_size); + /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ + if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { + err = ntfs_ib_split(icx, ib); + if (err == STATUS_OK) + err = STATUS_KEEP_SEARCHING; + goto err_out; + } + + if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) + goto err_out; + + if (ntfs_ib_write(icx, ib)) + goto err_out; + + err = STATUS_OK; +err_out: + free(ib); + return err; +} + +/** + * ntfs_ib_split - Split an index block + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return is STATUS_ERROR. + */ +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *median; + VCN new_vcn; + int ret; + + ntfs_log_trace("Entering\n"); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + median = ntfs_ie_get_median(&ib->index); + new_vcn = ntfs_ibm_get_free(icx); + if (new_vcn == -1) + return STATUS_ERROR; + + if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) { + ntfs_ibm_clear(icx, new_vcn); + return STATUS_ERROR; + } + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ret = ntfs_ir_insert_median(icx, median, new_vcn); + else + ret = ntfs_ib_insert(icx, median, new_vcn); + + if (ret != STATUS_OK) { + ntfs_ibm_clear(icx, new_vcn); + return ret; + } + + ret = ntfs_ib_cut_tail(icx, ib, median); + + return ret; +} + +/* JPA static */ +int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) +{ + INDEX_HEADER *ih; + int allocated_size, new_size; + int ret = STATUS_ERROR; + +#ifdef DEBUG +/* removed by JPA to make function usable for security indexes + char *fn; + fn = ntfs_ie_filename_get(ie); + ntfs_log_trace("file: '%s'\n", fn); + ntfs_attr_name_free(&fn); +*/ +#endif + + while (1) { + + if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx)) { + errno = EEXIST; + ntfs_log_perror("Index already have such entry"); + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("Failed to find place for new entry"); + goto err_out; + } + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + allocated_size = le32_to_cpu(ih->allocated_size); + new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); + + if (new_size <= allocated_size) + break; + + ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", + allocated_size, new_size); + + if (icx->is_in_root) { + if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) + goto err_out; + } else { + if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) + goto err_out; + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_ie_insert(ih, ie, icx->entry); + ntfs_index_entry_mark_dirty(icx); + + ret = STATUS_OK; +err_out: + ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); + return ret; +} + +/** + * ntfs_index_add_filename - add filename to directory index + * @ni: ntfs inode describing directory to which index add filename + * @fn: FILE_NAME attribute to add + * @mref: reference of the inode which @fn describes + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) +{ + INDEX_ENTRY *ie; + ntfs_index_context *icx; + int fn_size, ie_size, err, ret = -1; + + ntfs_log_trace("Entering\n"); + + if (!ni || !fn) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + fn_size = (fn->file_name_length * sizeof(ntfschar)) + + sizeof(FILE_NAME_ATTR); + ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; + + ie = ntfs_calloc(ie_size); + if (!ie) + return -1; + + ie->indexed_file = cpu_to_le64(mref); + ie->length = cpu_to_le16(ie_size); + ie->key_length = cpu_to_le16(fn_size); + memcpy(&ie->key, fn, fn_size); + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + goto out; + + ret = ntfs_ie_add(icx, ie); + err = errno; + ntfs_index_ctx_put(icx); + errno = err; +out: + free(ie); + return ret; +} + +static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_ENTRY *ie, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie_roam; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ie_roam = ntfs_ie_dup_novcn(ie); + if (!ie_roam) + return STATUS_ERROR; + + ntfs_ie_delete(ih, ie); + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + else + if (ntfs_ib_write(icx, ib)) + goto out; + + ntfs_index_ctx_reinit(icx); + + ret = ntfs_ie_add(icx, ie_roam); +out: + free(ie_roam); + return ret; +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is the only one there. + */ +static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + ie->ie_flags &= ~INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN)); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - sizeof(VCN)); + ih->ih_flags &= ~LARGE_INDEX; + + /* Not fatal error */ + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is not the only one there. + */ +static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie, *ie_prev; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); + ie_prev = ntfs_ie_prev(ih, ie); + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); + + return ntfs_ih_takeout(icx, ih, ie_prev, ib); +} + +static int ntfs_index_rm_leaf(ntfs_index_context *icx) +{ + INDEX_BLOCK *ib = NULL; + INDEX_HEADER *parent_ih; + INDEX_ENTRY *ie; + int ret = STATUS_ERROR; + + ntfs_log_trace("pindex: %d\n", icx->pindex); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1])) + return STATUS_ERROR; + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + parent_ih = &icx->ir->index; + else { + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib)) + goto out; + + parent_ih = &ib->index; + } + + ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); + if (!ntfs_ie_end(ie)) { + ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); + goto out; + } + + if (ntfs_ih_zero_entry(parent_ih)) { + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ntfs_ir_leafify(icx, parent_ih); + goto ok; + } + + ret = ntfs_index_rm_leaf(icx); + goto out; + } + + if (ntfs_ih_reparent_end(icx, parent_ih, ib)) + goto out; +ok: + ret = STATUS_OK; +out: + free(ib); + return ret; +} + +static int ntfs_index_rm_node(ntfs_index_context *icx) +{ + int entry_pos, pindex; + VCN vcn; + INDEX_BLOCK *ib = NULL; + INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; + INDEX_HEADER *ih; + u32 new_size; + int delta, ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + if (!icx->ia_na) { + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return STATUS_ERROR; + } + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + ie_succ = ntfs_ie_get_next(icx->entry); + entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; +descend: + vcn = ntfs_ie_get_vcn(ie_succ); + if (ntfs_ib_read(icx, vcn, ib)) + goto out; + + ie_succ = ntfs_ie_get_first(&ib->index); + + if (ntfs_icx_parent_inc(icx)) + goto out; + + icx->parent_vcn[icx->pindex] = vcn; + icx->parent_pos[icx->pindex] = 0; + + if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE) + goto descend; + + if (ntfs_ih_zero_entry(&ib->index)) { + errno = EIO; + ntfs_log_perror("Empty index block"); + goto out; + } + + ie = ntfs_ie_dup(ie_succ); + if (!ie) + goto out; + + if (ntfs_ie_add_vcn(&ie)) + goto out2; + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); + new_size = le32_to_cpu(ih->index_length) + delta; + if (delta > 0) { + if (icx->is_in_root) { + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + goto out2; + + ih = &icx->ir->index; + entry = ntfs_ie_get_by_pos(ih, entry_pos); + + } else if (new_size > le32_to_cpu(ih->allocated_size)) { + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + goto out2; + } + } + + ntfs_ie_delete(ih, entry); + ntfs_ie_insert(ih, ie, entry); + + if (icx->is_in_root) { + if (ntfs_ir_truncate(icx, new_size)) + goto out2; + } else + if (ntfs_icx_ib_write(icx)) + goto out2; + + ntfs_ie_delete(&ib->index, ie_succ); + + if (ntfs_ih_zero_entry(&ib->index)) { + if (ntfs_index_rm_leaf(icx)) + goto out2; + } else + if (ntfs_ib_write(icx, ib)) + goto out2; + + ret = STATUS_OK; +out2: + free(ie); +out: + free(ib); + return ret; +} + +/** + * ntfs_index_rm - remove entry from the index + * @icx: index context describing entry to delete + * + * Delete entry described by @icx from the index. Index context is always + * reinitialized after use of this function, so it can be used for index + * lookup once again. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +/*static JPA*/ +int ntfs_index_rm(ntfs_index_context *icx) +{ + INDEX_HEADER *ih; + int err, ret = STATUS_OK; + + ntfs_log_trace("Entering\n"); + + if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { + + ret = ntfs_index_rm_node(icx); + + } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { + + ntfs_ie_delete(ih, icx->entry); + + if (icx->is_in_root) { + err = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + if (err != STATUS_OK) + goto err_out; + } else + if (ntfs_icx_ib_write(icx)) + goto err_out; + } else { + if (ntfs_index_rm_leaf(icx)) + goto err_out; + } +out: + return ret; +err_out: + ret = STATUS_ERROR; + goto out; +} + +int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen) +{ + int ret = STATUS_ERROR; + ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; + + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; + + if ((((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; +} + +/** + * ntfs_index_root_get - read the index root of an attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @attr: attribute for which we want its index root + * + * This function will read the related index root an ntfs attribute. + * + * On success a buffer is allocated with the content of the index root + * and which needs to be freed when it's not needed anymore. + * + * On error NULL is returned with errno set to the error code. + */ +INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *name; + INDEX_ROOT *root = NULL; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + + if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx)) + return NULL; + + root = ntfs_malloc(sizeof(INDEX_ROOT)); + if (!root) + goto out; + + *root = *((INDEX_ROOT *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset))); +out: + ntfs_attr_put_search_ctx(ctx); + return root; +} + + +/* + * Walk down the index tree (leaf bound) + * until there are no subnode in the first index entry + * returns the entry at the bottom left in subnode + */ + +static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + do { + vcn = ntfs_ie_get_vcn(entry); + if (ictx->is_in_root) { + + /* down from level zero */ + + ictx->ir = (INDEX_ROOT*)NULL; + ictx->ib = (INDEX_BLOCK*)ntfs_malloc(ictx->block_size); + ictx->pindex = 1; + ictx->is_in_root = FALSE; + } else { + + /* down from non-zero level */ + + ictx->pindex++; + } + ictx->parent_pos[ictx->pindex] = 0; + ictx->parent_vcn[ictx->pindex] = vcn; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + ictx->entry = ntfs_ie_get_first(&ictx->ib->index); + entry = ictx->entry; + } else + entry = (INDEX_ENTRY*)NULL; + } while (entry && (entry->ie_flags & INDEX_ENTRY_NODE)); + return (entry); +} + +/* + * Walk up the index tree (root bound) + * until there is a valid data entry in parent + * returns the parent entry or NULL if no more parent + */ + +static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + if (ictx->pindex > 0) { + do { + ictx->pindex--; + if (!ictx->pindex) { + + /* we have reached the root */ + + free(ictx->ib); + ictx->ib = (INDEX_BLOCK*)NULL; + ictx->is_in_root = TRUE; + /* a new search context is to be allocated */ + if (ictx->actx) + free(ictx->actx); + ictx->ir = ntfs_ir_lookup(ictx->ni, + ictx->name, ictx->name_len, + &ictx->actx); + if (ictx->ir) + entry = ntfs_ie_get_by_pos( + &ictx->ir->index, + ictx->parent_pos[ictx->pindex]); + else + entry = (INDEX_ENTRY*)NULL; + } else { + /* up into non-root node */ + vcn = ictx->parent_vcn[ictx->pindex]; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + entry = ntfs_ie_get_by_pos( + &ictx->ib->index, + ictx->parent_pos[ictx->pindex]); + } else + entry = (INDEX_ENTRY*)NULL; + } + ictx->entry = entry; + } while (entry && (ictx->pindex > 0) + && (entry->ie_flags & INDEX_ENTRY_END)); + } else + entry = (INDEX_ENTRY*)NULL; + return (entry); +} + +/* + * Get next entry in an index according to collating sequence. + * Must be initialized through a ntfs_index_lookup() + * + * Returns next entry or NULL if none + * + * Sample layout : + * + * +---+---+---+---+---+---+---+---+ n ptrs to subnodes + * | | | 10| 25| 33| | | | n-1 keys in between + * +---+---+---+---+---+---+---+---+ no key in last entry + * | A | A + * | | | +-------------------------------+ + * +--------------------------+ | +-----+ | + * | +--+ | | + * V | V | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | 11| 12| 13| 14| 15| 16| 17| | | | 26| 27| 28| 29| 30| 31| 32| | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | | + * +-----------------------+ | + * | | + * +---+---+---+---+---+---+---+---+ + * | 18| 19| 20| 21| 22| 23| 24| | + * +---+---+---+---+---+---+---+---+ + */ + +INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx) +{ + INDEX_ENTRY *next; + int flags; + + /* + * lookup() may have returned an invalid node + * when searching for a partial key + * if this happens, walk up + */ + + if (ie->ie_flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(ie, ictx); + else { + /* + * get next entry in same node + * there is always one after any entry with data + */ + + next = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + ++ictx->parent_pos[ictx->pindex]; + flags = next->ie_flags; + + /* walk down if it has a subnode */ + + if (flags & INDEX_ENTRY_NODE) { + next = ntfs_index_walk_down(next,ictx); + } else { + + /* walk up it has no subnode, nor data */ + + if (flags & INDEX_ENTRY_END) { + next = ntfs_index_walk_up(next, ictx); + } + } + } + /* return NULL if stuck at end of a block */ + + if (next && (next->ie_flags & INDEX_ENTRY_END)) + next = (INDEX_ENTRY*)NULL; + return (next); +} + + diff --git a/lib/libntfs/orig/source/index.h b/lib/libntfs/orig/source/index.h new file mode 100644 index 0000000..c0e7618 --- /dev/null +++ b/lib/libntfs/orig/source/index.h @@ -0,0 +1,167 @@ +/* + * index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INDEX_H +#define _NTFS_INDEX_H + +/* Convenience macros to test the versions of gcc. + * Use them like this: + * #if __GNUC_PREREQ (2,8) + * ... code requiring gcc 2.8 or later ... + * #endif + * Note - they won't work for gcc1 or glibc1, since the _MINOR macros + * were not defined then. + */ + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +/* allows us to warn about unused results of certain function calls */ +#ifndef __attribute_warn_unused_result__ +# if __GNUC_PREREQ (3,4) +# define __attribute_warn_unused_result__ \ + __attribute__ ((__warn_unused_result__)) +# else +# define __attribute_warn_unused_result__ /* empty */ +# endif +#endif + +#include "attrib.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "mft.h" + +#define VCN_INDEX_ROOT_PARENT ((VCN)-2) + +#define MAX_PARENT_VCN 32 + +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); + +/** + * struct ntfs_index_context - + * @ni: inode containing the @entry described by this context + * @name: name of the index described by this context + * @name_len: length of the index name + * @entry: index entry (points into @ir or @ia) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia + * @ir: index root if @is_in_root or NULL otherwise + * @actx: attribute search context if in root or NULL otherwise + * @ia: index block if @is_in_root is FALSE or NULL otherwise + * @ia_na: opened INDEX_ALLOCATION attribute + * @parent_pos: parent entries' positions in the index block + * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT + * @new_vcn: new VCN if we need to create a new index block + * @median: move to the parent if splitting index blocks + * @ib_dirty: TRUE if index block was changed + * @block_size: index block size + * @vcn_size_bits: VCN size bits for this index block + * + * @ni is the inode this context belongs to. + * + * @entry is the index entry described by this context. @data and @data_len + * are the index entry data and its length in bytes, respectively. @data + * simply points into @entry. This is probably what the user is interested in. + * + * If @is_in_root is TRUE, @entry is in the index root attribute @ir described + * by the attribute search context @actx and inode @ni. @ia and + * @ib_dirty are undefined in this case. + * + * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia + * point to the index allocation block and VCN where it's placed, + * respectively. @ir and @actx are NULL in this case. @ia_na is opened + * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and + * FALSE otherwise. + * + * To obtain a context call ntfs_index_ctx_get(). + * + * When finished with the @entry and its @data, call ntfs_index_ctx_put() to + * free the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +typedef struct { + ntfs_inode *ni; + ntfschar *name; + u32 name_len; + INDEX_ENTRY *entry; + void *data; + u16 data_len; + COLLATE collate; + BOOL is_in_root; + INDEX_ROOT *ir; + ntfs_attr_search_ctx *actx; + INDEX_BLOCK *ib; + ntfs_attr *ia_na; + int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ + VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ + int pindex; /* maximum it's the number of the parent nodes */ + BOOL ib_dirty; + u32 block_size; + u8 vcn_size_bits; +} ntfs_index_context; + +extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len); +extern void ntfs_index_ctx_put(ntfs_index_context *ictx); +extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); + +extern int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx) __attribute_warn_unused_result__; + +extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, + ntfs_index_context *ictx); + +extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, + MFT_REF mref); +extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen); + +extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); + +extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); + +extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); + +extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); +extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); +extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); + +/* the following was added by JPA for use in security.c */ +extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie); +extern int ntfs_index_rm(ntfs_index_context *icx); + +#endif /* _NTFS_INDEX_H */ + diff --git a/lib/libntfs/orig/source/inode.c b/lib/libntfs/orig/source/inode.c new file mode 100644 index 0000000..c51e523 --- /dev/null +++ b/lib/libntfs/orig/source/inode.c @@ -0,0 +1,1575 @@ +/** + * inode.c - Inode handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SETXATTR +#include +#endif + +#include "param.h" +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "cache.h" +#include "inode.h" +#include "attrib.h" +#include "debug.h" +#include "mft.h" +#include "attrlist.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "index.h" +#include "dir.h" +#include "ntfstime.h" +#include "logging.h" +#include "misc.h" + +ntfs_inode *ntfs_inode_base(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + return ni->base_ni; + return ni; +} + +/** + * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty + * @ni: ntfs inode to set dirty + * + * Set the inode @ni dirty so it is written out later (at the latest at + * ntfs_inode_close() time). If @ni is an extent inode, set the base inode + * dirty, too. + * + * This function cannot fail. + */ +void ntfs_inode_mark_dirty(ntfs_inode *ni) +{ + NInoSetDirty(ni); + if (ni->nr_extents == -1) + NInoSetDirty(ni->base_ni); +} + +/** + * __ntfs_inode_allocate - Create and initialise an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) +{ + ntfs_inode *ni; + + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); + if (ni) + ni->vol = vol; + return ni; +} + +/** + * ntfs_inode_allocate - Create an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) +{ + return __ntfs_inode_allocate(vol); +} + +/** + * __ntfs_inode_release - Destroy an NTFS inode object + * @ni: + * + * Description... + * + * Returns: + */ +static void __ntfs_inode_release(ntfs_inode *ni) +{ + if (NInoDirty(ni)) + ntfs_log_error("Releasing dirty inode %lld!\n", + (long long)ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + free(ni->attr_list); + free(ni->mrec); + free(ni); + return; +} + +/** + * ntfs_inode_open - open an inode ready for access + * @vol: volume to get the inode from + * @mref: inode number / mft record number to open + * + * Allocate an ntfs_inode structure and initialize it for the given inode + * specified by @mref. @mref specifies the inode number / mft record to read, + * including the sequence number, which can be 0 if no sequence number checking + * is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @vol, and attach it to the ntfs_inode structure (->mrec). The + * mft record is mst deprotected and sanity checked for validity and we abort + * if deprotection or checks fail. + * + * Finally, search for an attribute list attribute in the mft record and if one + * is found, load the attribute list attribute value and attach it to the + * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate + * this. + * + * Return a pointer to the ntfs_inode structure on success or NULL on error, + * with errno set to the error code. + */ +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) +{ + s64 l; + ntfs_inode *ni = NULL; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + le32 lthle; + int olderrno; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + if (!vol) { + errno = EINVAL; + goto out; + } + ni = __ntfs_inode_allocate(vol); + if (!ni) + goto out; + if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) + goto err_out; + if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { + errno = ENOENT; + goto err_out; + } + ni->mft_no = MREF(mref); + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + /* Receive some basic information about inode. */ + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (!ni->mrec->base_mft_record) + ntfs_log_perror("No STANDARD_INFORMATION in base record" + " %lld", (long long)MREF(mref)); + goto put_err_out; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + ni->flags = std_info->file_attributes; + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; + /* JPA insert v3 extensions if present */ + /* length may be seen as 72 (v1.x) or 96 (v3.x) */ + lthle = ctx->attr->length; + if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = std_info->owner_id; + ni->security_id = std_info->security_id; + ni->quota_charged = std_info->quota_charged; + ni->usn = std_info->usn; + } else { + clear_nino_flag(ni, v3_Extensions); + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); + } + /* Set attribute list information. */ + olderrno = errno; + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Attribute list attribute does not present. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + goto get_size; + } + NInoSetAttrList(ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (!l) + goto put_err_out; + if (l > 0x40000) { + errno = EIO; + ntfs_log_perror("Too large attrlist attribute (%lld), inode " + "%lld", (long long)l, (long long)MREF(mref)); + goto put_err_out; + } + ni->attr_list_size = l; + ni->attr_list = ntfs_malloc(ni->attr_list_size); + if (!ni->attr_list) + goto put_err_out; + l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); + if (!l) + goto put_err_out; + if (l != ni->attr_list_size) { + errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " + "%lld", (long long)l, ni->attr_list_size, + (long long)MREF(mref)); + goto put_err_out; + } +get_size: + olderrno = errno; + if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Directory or special file. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + ni->data_size = ni->allocated_size = 0; + } else { + if (ctx->attr->non_resident) { + ni->data_size = sle64_to_cpu(ctx->attr->data_size); + if (ctx->attr->flags & + (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) + ni->allocated_size = sle64_to_cpu( + ctx->attr->compressed_size); + else + ni->allocated_size = sle64_to_cpu( + ctx->attr->allocated_size); + } else { + ni->data_size = le32_to_cpu(ctx->attr->value_length); + ni->allocated_size = (ni->data_size + 7) & ~7; + } + set_nino_flag(ni,KnownSize); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ni; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_close - close an ntfs inode and free all associated memory + * @ni: ntfs inode to close + * + * Make sure the ntfs inode @ni is clean. + * + * If the ntfs inode @ni is a base inode, close all associated extent inodes, + * then deallocate all memory attached to it, and finally free the ntfs inode + * structure itself. + * + * If it is an extent inode, we disconnect it from its base inode before we + * destroy it. + * + * It is OK to pass NULL to this function, it is just noop in this case. + * + * Return 0 on success or -1 on error with errno set to the error code. On + * error, @ni has not been freed. The user should attempt to handle the error + * and call ntfs_inode_close() again. The following error codes are defined: + * + * EBUSY @ni and/or its attribute list runlist is/are dirty and the + * attempt to write it/them to disk failed. + * EINVAL @ni is invalid (probably it is an extent inode). + * EIO I/O error while trying to write inode to disk. + */ + +int ntfs_inode_real_close(ntfs_inode *ni) +{ + int ret = -1; + + if (!ni) + return 0; + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* If we have dirty metadata, write it out. */ + if (NInoDirty(ni) || NInoAttrListDirty(ni)) { + if (ntfs_inode_sync(ni)) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + /* Is this a base inode with mapped extent inodes? */ + if (ni->nr_extents > 0) { + while (ni->nr_extents > 0) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + } else if (ni->nr_extents == -1) { + ntfs_inode **tmp_nis; + ntfs_inode *base_ni; + s32 i; + + /* + * If the inode is an extent inode, disconnect it from the + * base inode before destroying it. + */ + base_ni = ni->base_ni; + for (i = 0; i < base_ni->nr_extents; ++i) { + tmp_nis = base_ni->extent_nis; + if (tmp_nis[i] != ni) + continue; + /* Found it. Disconnect. */ + memmove(tmp_nis + i, tmp_nis + i + 1, + (base_ni->nr_extents - i - 1) * + sizeof(ntfs_inode *)); + /* Buffer should be for multiple of four extents. */ + if ((--base_ni->nr_extents) & 3) { + i = -1; + break; + } + /* + * ElectricFence is unhappy with realloc(x,0) as free(x) + * thus we explicitly separate these two cases. + */ + if (base_ni->nr_extents) { + /* Resize the memory buffer. */ + tmp_nis = realloc(tmp_nis, base_ni->nr_extents * + sizeof(ntfs_inode *)); + /* Ignore errors, they don't really matter. */ + if (tmp_nis) + base_ni->extent_nis = tmp_nis; + } else if (tmp_nis) { + free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } + /* Allow for error checking. */ + i = -1; + break; + } + + /* + * We could successfully sync, so only log this error + * and try to sync other inode extents too. + */ + if (i != -1) + ntfs_log_error("Extent inode %lld was not found\n", + (long long)ni->mft_no); + } + + __ntfs_inode_release(ni); + ret = 0; +err: + ntfs_log_leave("\n"); + return ret; +} + +#if CACHE_NIDATA_SIZE + +/* + * Free an inode structure when there is not more space + * in the cache + */ + +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) +{ + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); +} + +/* + * Compute a hash value for an inode entry + */ + +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) +{ + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); +} + +/* + * inum comparing for entering/fetching from cache + */ + +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); +} + +/* + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. + */ + +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) +{ + struct CACHED_NIDATA item; + int count; + + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + count = ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); +} + +#endif + +/* + * Open an inode + * + * When possible, an entry recorded in the cache is reused + * + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. + */ + +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +{ + ntfs_inode *ni; +#if CACHE_NIDATA_SIZE + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; + + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } + if (!ni) { + debug_double_inode(item.inum, 0); + } +#else + ni = ntfs_inode_real_open(vol, mref); +#endif + return (ni); +} + +/* + * Close an inode entry + * + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. + * + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. + */ + +int ntfs_inode_close(ntfs_inode *ni) +{ + int res; +#if CACHE_NIDATA_SIZE + BOOL dirty; + struct CACHED_NIDATA item; + + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; + + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; +#else + res = ntfs_inode_real_close(ni); +#endif + return (res); +} + +/** + * ntfs_extent_inode_open - load an extent inode and attach it to its base + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load (in little endian) + * + * First check if the extent inode @mref is already attached to the base ntfs + * inode @base_ni, and if so, return a pointer to the attached extent inode. + * + * If the extent inode is not already attached to the base inode, allocate an + * ntfs_inode structure and initialize it for the given inode @mref. @mref + * specifies the inode number / mft record to read, including the sequence + * number, which can be 0 if no sequence number checking is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). + * The mft record is mst deprotected and sanity checked for validity and we + * abort if deprotection or checks fail. + * + * Finally attach the ntfs inode to its base inode @base_ni and return a + * pointer to the ntfs_inode structure on success or NULL on error, with errno + * set to the error code. + * + * Note, extent inodes are never closed directly. They are automatically + * disposed off by the closing of the base inode. + */ +ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) +{ + u64 mft_no = MREF_LE(mref); + ntfs_inode *ni = NULL; + ntfs_inode **extent_nis; + int i; + + if (!base_ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return NULL; + } + + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", + (unsigned long long)mft_no, + (unsigned long long)base_ni->mft_no); + + /* Is the extent inode already open and attached to the base inode? */ + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->extent_nis; + for (i = 0; i < base_ni->nr_extents; i++) { + u16 seq_no; + + ni = extent_nis[i]; + if (mft_no != ni->mft_no) + continue; + /* Verify the sequence number if given. */ + seq_no = MSEQNO_LE(mref); + if (seq_no && seq_no != le16_to_cpu( + ni->mrec->sequence_number)) { + errno = EIO; + ntfs_log_perror("Found stale extent mft " + "reference mft=%lld", + (long long)ni->mft_no); + goto out; + } + goto out; + } + } + /* Wasn't there, we need to load the extent inode. */ + ni = __ntfs_inode_allocate(base_ni->vol); + if (!ni) + goto out; + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) + goto err_out; + ni->mft_no = mft_no; + ni->nr_extents = -1; + ni->base_ni = base_ni; + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + + extent_nis = ntfs_malloc(i); + if (!extent_nis) + goto err_out; + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; +out: + ntfs_log_leave("\n"); + return ni; +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_attach_all_extents - attach all extents for target inode + * @ni: opened ntfs inode for which perform attach + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_inode_attach_all_extents(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + u64 prev_attached = 0; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + /* Inode haven't got attribute list, thus nothing to attach. */ + if (!NInoAttrList(ni)) + return 0; + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + /* Walk through attribute list and attach all extents. */ + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { + ntfs_log_trace("Couldn't attach extent inode.\n"); + return -1; + } + prev_attached = MREF_LE(ale->mft_reference); + } + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_inode_sync_standard_information - update standard information attribute + * @ni: ntfs inode to update standard information + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_standard_information(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u32 lth; + le32 lthle; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); + ntfs_attr_put_search_ctx(ctx); + return -1; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + std_info->file_attributes = ni->flags; + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; + } + + /* JPA update v3.x extensions, ensuring consistency */ + + lthle = ctx->attr->length; + lth = le32_to_cpu(lthle); + if (test_nino_flag(ni, v3_Extensions) + && (lth <= sizeof(STANDARD_INFORMATION))) + ntfs_log_error("bad sync of standard information\n"); + + if (lth > sizeof(STANDARD_INFORMATION)) { + std_info->owner_id = ni->owner_id; + std_info->security_id = ni->security_id; + std_info->quota_charged = ni->quota_charged; + std_info->usn = ni->usn; + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +} + +/** + * ntfs_inode_sync_file_name - update FILE_NAME attributes + * @ni: ntfs inode to update FILE_NAME attributes + * + * Update all FILE_NAME attributes for inode @ni in the index. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + ntfs_attr_search_ctx *ctx = NULL; + ntfs_index_context *ictx; + ntfs_inode *index_ni; + FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; + int err = 0; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } + /* Walk through all FILE_NAME attributes and update them. */ + while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) { + /* + * WARNING: We cheat here and obtain 2 attribute + * search contexts for one inode (first we obtained + * above, second will be obtained inside + * ntfs_index_lookup), it's acceptable for library, + * but will deadlock in the kernel. + */ + index_ni = ni; + } else + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); + if (!index_ni) { + if (!err) + err = errno; + ntfs_log_perror("Failed to open inode %lld with index", + (long long)le64_to_cpu(fn->parent_directory)); + continue; + } + ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); + if (!ictx) { + if (!err) + err = errno; + ntfs_log_perror("Failed to get index ctx, inode %lld", + (long long)index_ni->mft_no); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { + if (!err) { + if (errno == ENOENT) + err = EIO; + else + err = errno; + } + ntfs_log_perror("Index lookup failed, inode %lld", + (long long)index_ni->mft_no); + ntfs_index_ctx_put(ictx); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + /* Update flags and file size. */ + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + (ni->flags & FILE_ATTR_VALID_FLAGS); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + /* + * The file name record has also to be fixed if some + * attribute update implied the unnamed data to be + * made non-resident + */ + fn->allocated_size = fnx->allocated_size; + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; + } + ntfs_index_entry_mark_dirty(ictx); + ntfs_index_ctx_put(ictx); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Attribute lookup failed, inode %lld", + (long long)ni->mft_no); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err) { + errno = err; + return -1; + } + return 0; +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_inode_sync - write the inode (and its dirty extents) to disk + * @ni: ntfs inode to write + * + * Write the inode @ni to disk as well as its dirty extent inodes if such + * exist and @ni is a base inode. If @ni is an extent inode, only @ni is + * written completely disregarding its base inode and any other extent inodes. + * + * For a base inode with dirty extent inodes if any writes fail for whatever + * reason, the failing inode is skipped and the sync process is continued. At + * the end the error condition that brought about the failure is returned. Thus + * the smallest amount of data loss possible occurs. + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EBUSY - Inode and/or one of its extents is busy, try again later. + * EIO - I/O error while writing the inode (or one of its extents). + */ +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int ret = 0; + int err = 0; + if (!ni) { + errno = EINVAL; + ntfs_log_error("Failed to sync NULL inode\n"); + return -1; + } + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* Update STANDARD_INFORMATION. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + ntfs_inode_sync_standard_information(ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + } + + /* Update FILE_NAME's in the index. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoFileNameTestAndClearDirty(ni) && + ntfs_inode_sync_file_name(ni, dir_ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", + (long long)ni->mft_no); + NInoFileNameSetDirty(ni); + } + + /* Write out attribute list from cache to disk. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync failed " + "(open, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + goto sync_inode; + } + + if (na->data_size == ni->attr_list_size) { + if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, + ni->attr_list) != ni->attr_list_size) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync " + "failed (write, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + } + } else { + err = EIO; + ntfs_log_error("Attribute list sync failed (bad size, " + "inode %lld)\n", (long long)ni->mft_no); + NInoAttrListSetDirty(ni); + } + ntfs_attr_close(na); + } + +sync_inode: + /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ + if (NInoTestAndClearDirty(ni)) { + if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(ni); + ntfs_log_perror("MFT record sync failed, inode %lld", + (long long)ni->mft_no); + } + } + + /* If this is a base inode with extents write all dirty extents, too. */ + if (ni->nr_extents > 0) { + s32 i; + + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni; + + eni = ni->extent_nis[i]; + if (!NInoTestAndClearDirty(eni)) + continue; + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, + eni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(eni); + ntfs_log_perror("Extent MFT record sync failed," + " inode %lld/%lld", + (long long)ni->mft_no, + (long long)eni->mft_no); + } + } + } + + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; +} + +int ntfs_inode_sync(ntfs_inode *ni) +{ + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); +} + +/* + * Close an inode with an open parent inode + */ + +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); +} + +/** + * ntfs_inode_add_attrlist - add attribute list to inode and fill it + * @ni: opened ntfs inode to which add attribute list + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EEXIST - Attribute list already exist. + * EIO - Input/Ouput error occurred. + * ENOMEM - Not enough memory to perform add. + */ +int ntfs_inode_add_attrlist(ntfs_inode *ni) +{ + int err; + ntfs_attr_search_ctx *ctx; + u8 *al = NULL, *aln; + int al_len = 0; + ATTR_LIST_ENTRY *ale = NULL; + ntfs_attr *na; + + if (!ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); + + if (NInoAttrList(ni) || ni->nr_extents) { + errno = EEXIST; + ntfs_log_perror("Inode already has attribute list"); + return -1; + } + + /* Form attribute list. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Walk through all attributes. */ + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + + int ale_size; + + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { + err = EIO; + ntfs_log_perror("Attribute list already present"); + goto put_err_out; + } + + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + ctx->attr->name_length + 7) & ~7; + al_len += ale_size; + + aln = realloc(al, al_len); + if (!aln) { + err = errno; + ntfs_log_perror("Failed to realloc %d bytes", al_len); + goto put_err_out; + } + ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); + al = aln; + + memset(ale, 0, ale_size); + + /* Add attribute to attribute list. */ + ale->type = ctx->attr->type; + ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); + ale->name_length = ctx->attr->name_length; + ale->name_offset = (u8 *)ale->name - (u8 *)ale; + if (ctx->attr->non_resident) + ale->lowest_vcn = ctx->attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = MK_LE_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)); + ale->instance = ctx->attr->instance; + memcpy(ale->name, (u8 *)ctx->attr + + le16_to_cpu(ctx->attr->name_offset), + ctx->attr->name_length * sizeof(ntfschar)); + ale = (ATTR_LIST_ENTRY *)(al + al_len); + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed, inode %lld", + __FUNCTION__, (long long)ni->mft_no); + goto put_err_out; + } + + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + NInoAttrListSetDirty(ni); + + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) < + offsetof(ATTR_RECORD, resident_end)) { + if (ntfs_inode_free_space(ni, + offsetof(ATTR_RECORD, resident_end))) { + /* Failed to free space. */ + err = errno; + ntfs_log_perror("Failed to free space for attrlist"); + goto rollback; + } + } + + /* Add $ATTRIBUTE_LIST to mft record. */ + if (ntfs_resident_attr_record_add(ni, + AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { + err = errno; + ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); + goto rollback; + } + + /* Resize it. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); + goto remove_attrlist_record; + } + if (ntfs_attr_truncate(na, al_len)) { + err = errno; + ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); + ntfs_attr_close(na); + goto remove_attrlist_record;; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_attr_close(na); + return 0; + +remove_attrlist_record: + /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + /* Remove $ATTRIBUTE_LIST record. */ + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) + ntfs_log_perror("Rollback failed to remove attrlist"); + } else + ntfs_log_perror("Rollback failed to find attrlist"); + /* Setup back in-memory runlist. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); +rollback: + /* + * Scan attribute list for attributes that placed not in the base MFT + * record and move them to it. + */ + ntfs_attr_reinit_search_ctx(ctx); + ale = (ATTR_LIST_ENTRY*)al; + while ((u8*)ale < al + al_len) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) { + if (!ntfs_attr_lookup(ale->type, ale->name, + ale->name_length, + CASE_SENSITIVE, + sle64_to_cpu(ale->lowest_vcn), + NULL, 0, ctx)) { + if (ntfs_attr_record_move_to(ctx, ni)) + ntfs_log_perror("Rollback failed to " + "move attribute"); + } else + ntfs_log_perror("Rollback failed to find attr"); + ntfs_attr_reinit_search_ctx(ctx); + } + ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); + } + /* Remove in-memory attribute list. */ + ni->attr_list = NULL; + ni->attr_list_size = 0; + NInoClearAttrList(ni); + NInoAttrListClearDirty(ni); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(al); + errno = err; + return -1; +} + +/** + * ntfs_inode_free_space - free space in the MFT record of an inode + * @ni: ntfs inode in which MFT record needs more free space + * @size: amount of space needed to free + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_inode_free_space(ntfs_inode *ni, int size) +{ + ntfs_attr_search_ctx *ctx; + int freed; + + if (!ni || size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, size %d\n", + (unsigned long long)ni->mft_no, size); + + freed = (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use)); + + if (size <= freed) + return 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. + */ + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + goto put_err_out; + + while (1) { + int record_size; + /* + * Check whether attribute is from different MFT record. If so, + * find next, because we don't need such. + */ + while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: + if (ntfs_attr_position(AT_UNUSED, ctx)) + goto put_err_out; + } + + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + + record_size = le32_to_cpu(ctx->attr->length); + + if (ntfs_attr_record_move_away(ctx, 0)) { + ntfs_log_perror("Failed to move out attribute #2"); + break; + } + freed += record_size; + + /* Check whether we are done. */ + if (size <= freed) { + ntfs_attr_put_search_ctx(ctx); + return 0; + } + /* + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + break; + } +put_err_out: + ntfs_attr_put_search_ctx(ctx); + if (errno == ENOSPC) + ntfs_log_trace("No attributes left that could be moved out.\n"); + return -1; +} + +/** + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated + * + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). + */ +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + ntfs_time now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; + } + + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; + + now = ntfs_current_time(); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) + ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) + ni->last_mft_change_time = now; + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); +} + +/** + * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute + * @mft_no: mft record number where @attr is present + * @attr: attribute record used to check for the $Bad attribute + * + * Check if the mft record given by @mft_no and @attr contains the bad sector + * list. Please note that mft record numbers describing $Badclus extent inodes + * will not match the current $Badclus:$Bad check. + * + * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. + * On error return -1 with errno set to the error code. + */ +int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) +{ + int len, ret = 0; + ntfschar *ustr; + + if (!attr) { + ntfs_log_error("Invalid argument.\n"); + errno = EINVAL; + return -1; + } + + if (mft_no != FILE_BadClus) + return 0; + + if (attr->type != AT_DATA) + return 0; + + if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) { + ntfs_log_perror("Couldn't convert '$Bad' to Unicode"); + return -1; + } + + if (ustr && ntfs_names_are_equal(ustr, len, + (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)), + attr->name_length, 0, NULL, 0)) + ret = 1; + + ntfs_ucsfree(ustr); + + return ret; +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get high precision NTFS times + * + * They are returned in following order : create, update, access, change + * provided they fit in requested size. + * + * Returns the modified size if successfull (or 32 if buffer size is null) + * -errno if failed + */ + +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u64 *times; + int ret; + + ret = 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (value && (size >= 8)) { + times = (u64*)value; + times[0] = le64_to_cpu(std_info->creation_time); + ret = 8; + if (size >= 16) { + times[1] = le64_to_cpu(std_info->last_data_change_time); + ret = 16; + } + if (size >= 24) { + times[2] = le64_to_cpu(std_info->last_access_time); + ret = 24; + } + if (size >= 32) { + times[3] = le64_to_cpu(std_info->last_mft_change_time); + ret = 32; + } + } else + if (!size) + ret = 32; + else + ret = -ERANGE; + } + ntfs_attr_put_search_ctx(ctx); + } + return (ret ? ret : -errno); +} + +/* + * Set high precision NTFS times + * + * They are expected in this order : create, update, access + * provided they are present in input. The change time is set to + * current time. + * + * The times are inserted directly in the standard_information and + * file names attributes to avoid manipulating low precision times + * + * Returns 0 if success + * -1 if there were an error (described by errno) + */ + +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + FILE_NAME_ATTR *fn; + const u64 *times; + ntfs_time now; + int cnt; + int ret; + + ret = -1; + if ((size >= 8) && !(flags & XATTR_CREATE)) { + times = (const u64*)value; + now = ntfs_current_time(); + /* update the standard information attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() + */ + set_nino_flag(ni, TimesSet); + std_info->creation_time = cpu_to_le64(times[0]); + ni->creation_time + = std_info->creation_time; + if (size >= 16) { + std_info->last_data_change_time = cpu_to_le64(times[1]); + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { + std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } + std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); + + /* update the file names attributes */ + ntfs_attr_reinit_search_ctx(ctx); + cnt = 0; + while (!ntfs_attr_lookup(AT_FILE_NAME, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + fn->creation_time + = cpu_to_le64(times[0]); + if (size >= 16) + fn->last_data_change_time + = cpu_to_le64(times[1]); + if (size >= 24) + fn->last_access_time + = cpu_to_le64(times[2]); + fn->last_mft_change_time = now; + cnt++; + } + if (cnt) + ret = 0; + else { + ntfs_log_perror("Failed to get file names (inode %lld)", + (long long)ni->mft_no); + } + } + ntfs_attr_put_search_ctx(ctx); + } + } else + if (size < 8) + errno = ERANGE; + else + errno = EEXIST; + return (ret); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/orig/source/inode.h b/lib/libntfs/orig/source/inode.h new file mode 100644 index 0000000..5a6f7da --- /dev/null +++ b/lib/libntfs/orig/source/inode.h @@ -0,0 +1,225 @@ +/* + * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INODE_H +#define _NTFS_INODE_H + +/* Forward declaration */ +typedef struct _ntfs_inode ntfs_inode; + +#include "types.h" +#include "layout.h" +#include "support.h" +#include "volume.h" +#include "ntfstime.h" + +/** + * enum ntfs_inode_state_bits - + * + * Defined bits for the state field in the ntfs_inode structure. + * (f) = files only, (d) = directories only + */ +typedef enum { + NI_Dirty, /* 1: Mft record needs to be written to disk. */ + + /* The NI_AttrList* tests only make sense for base inodes. */ + NI_AttrList, /* 1: Mft record contains an attribute list. */ + NI_AttrListDirty, /* 1: Attribute list needs to be written to the + mft record and then to disk. */ + NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated + in the index. */ + NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ +} ntfs_inode_state_bits; + +#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) +#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) +#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) + +#define test_and_set_nino_flag(ni, flag) \ + test_and_set_bit(NI_##flag, (ni)->state) +#define test_and_clear_nino_flag(ni, flag) \ + test_and_clear_bit(NI_##flag, (ni)->state) + +#define NInoDirty(ni) test_nino_flag(ni, Dirty) +#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) +#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) +#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) +#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) + +#define NInoAttrList(ni) test_nino_flag(ni, AttrList) +#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) +#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) + + +#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) +#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) +#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) + +#define test_and_set_nino_al_flag(ni, flag) \ + test_and_set_nino_flag(ni, AttrList##flag) +#define test_and_clear_nino_al_flag(ni, flag) \ + test_and_clear_nino_flag(ni, AttrList##flag) + +#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) +#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) +#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) + +#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndSetDirty(ni) \ + test_and_set_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndClearDirty(ni) \ + test_and_clear_nino_flag(ni, FileNameDirty) + +/** + * struct _ntfs_inode - The NTFS in-memory inode structure. + * + * It is just used as an extension to the fields already provided in the VFS + * inode. + */ +struct _ntfs_inode { + u64 mft_no; /* Inode / mft record number. */ + MFT_RECORD *mrec; /* The actual mft record of the inode. */ + ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ + unsigned long state; /* NTFS specific flags describing this inode. + See ntfs_inode_state_bits above. */ + FILE_ATTR_FLAGS flags; /* Flags describing the file. + (Copy from STANDARD_INFORMATION) */ + /* + * Attribute list support (for use by the attribute lookup functions). + * Setup during ntfs_open_inode() for all inodes with attribute lists. + * Only valid if NI_AttrList is set in state. + */ + u32 attr_list_size; /* Length of attribute list value in bytes. */ + u8 *attr_list; /* Attribute list value itself. */ + /* Below fields are always valid. */ + s32 nr_extents; /* For a base mft record, the number of + attached extent inodes (0 if none), for + extent records this is -1. */ + union { /* This union is only used if nr_extents != 0. */ + ntfs_inode **extent_nis;/* For nr_extents > 0, array of the + ntfs inodes of the extent mft + records belonging to this base + inode which have been loaded. */ + ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs + inode of the base mft record. */ + }; + + /* Below fields are valid only for base inode. */ + + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. + */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ + s64 allocated_size; /* Allocated size stored in the filename + index. (NOTE: Equal to allocated size of + the unnamed data attribute for normal or + encrypted files and to compressed size + of the unnamed data attribute for sparse or + compressed files.) */ + + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; + /* NTFS 3.x extensions added by JPA */ + /* only if NI_v3_Extensions is set in state */ + le32 owner_id; + le32 security_id; + le64 quota_charged; + le64 usn; +}; + +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) +#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) + +extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); + +extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); + +extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); + +extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_NIDATA_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); + +#endif + + +extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, + const MFT_REF mref); + +extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); + +extern void ntfs_inode_mark_dirty(ntfs_inode *ni); + +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); + +extern int ntfs_inode_sync(ntfs_inode *ni); + +extern int ntfs_inode_add_attrlist(ntfs_inode *ni); + +extern int ntfs_inode_free_space(ntfs_inode *ni, int size); + +extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); + +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); + +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); + +/* debugging */ +#define debug_double_inode(num, type) +#define debug_cached_inode(ni) + +#endif /* defined _NTFS_INODE_H */ diff --git a/lib/libntfs/orig/source/layout.h b/lib/libntfs/orig/source/layout.h new file mode 100644 index 0000000..daff97d --- /dev/null +++ b/lib/libntfs/orig/source/layout.h @@ -0,0 +1,2662 @@ +/* + * layout.h - Ntfs on-disk layout structures. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LAYOUT_H +#define _NTFS_LAYOUT_H + +#include "types.h" +#include "endians.h" +#include "support.h" + +/* The NTFS oem_id */ +#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ +#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ + +/* + * Location of bootsector on partition: + * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. + * On NT4 and above there is one backup copy of the boot sector to + * be found on the last sector of the partition (not normally accessible + * from within Windows as the bootsector contained number of sectors + * value is one less than the actual value!). + * On versions of NT 3.51 and earlier, the backup copy was located at + * number of sectors/2 (integer divide), i.e. in the middle of the volume. + */ + +/** + * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. + */ +typedef struct { + u16 bytes_per_sector; /* Size of a sector in bytes. */ + u8 sectors_per_cluster; /* Size of a cluster in sectors. */ + u16 reserved_sectors; /* zero */ + u8 fats; /* zero */ + u16 root_entries; /* zero */ + u16 sectors; /* zero */ + u8 media_type; /* 0xf8 = hard disk */ + u16 sectors_per_fat; /* zero */ +/*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ +/*0x0f*/u16 heads; /* Required to boot Windows. */ +/*0x11*/u32 hidden_sectors; /* Offset to the start of the partition + relative to the disk in sectors. + Required to boot Windows. */ +/*0x15*/u32 large_sectors; /* zero */ +/* sizeof() = 25 (0x19) bytes */ +} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; + +/** + * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. + */ +typedef struct { + u8 jump[3]; /* Irrelevant (jump to boot up code).*/ + u64 oem_id; /* Magic "NTFS ". */ +/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ + u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ + u8 current_head; /* zero */ + u8 extended_boot_signature; /* 0x80 */ + u8 reserved2; /* zero */ +/*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives + maximum volume size of 2^63 sectors. + Assuming standard sector size of 512 + bytes, the maximum byte size is + approx. 4.7x10^21 bytes. (-; */ + s64 mft_lcn; /* Cluster location of mft data. */ + s64 mftmirr_lcn; /* Cluster location of copy of mft. */ + s8 clusters_per_mft_record; /* Mft record size in clusters. */ + u8 reserved0[3]; /* zero */ + s8 clusters_per_index_record; /* Index block size in clusters. */ + u8 reserved1[3]; /* zero */ + u64 volume_serial_number; /* Irrelevant (serial number). */ + u32 checksum; /* Boot sector checksum. */ +/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ + u16 end_of_sector_marker; /* End of bootsector magic. Always is + 0xaa55 in little endian. */ +/* sizeof() = 512 (0x200) bytes */ +} __attribute__((__packed__)) NTFS_BOOT_SECTOR; + +/** + * enum NTFS_RECORD_TYPES - + * + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + */ +typedef enum { + /* Found in $MFT/$DATA. */ + magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */ + magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */ + magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */ + + /* Found in $LogFile/$DATA. */ + magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */ + magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */ + + /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ + magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */ + + /* Found in all ntfs record containing records. */ + magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector + transfer was detected. */ + + /* + * Found in $LogFile/$DATA when a page is full or 0xff bytes and is + * thus not initialized. User has to initialize the page before using + * it. + */ + magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has + to be initialized before + it can be used. */ +} NTFS_RECORD_TYPES; + +/* + * Generic magic comparison macros. Finally found a use for the ## preprocessor + * operator! (-8 + */ +#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) +#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) + +/* + * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above. + */ +#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) +#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) +#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) +#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) +#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) +#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) +#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) +#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) + +#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) +#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) +#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) +#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) + +#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) +#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) + +#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) +#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) + +#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) +#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) + + +#define NTFS_BLOCK_SIZE 512 +#define NTFS_BLOCK_SIZE_BITS 9 + +/** + * struct NTFS_RECORD - + * + * The Update Sequence Array (usa) is an array of the u16 values which belong + * to the end of each sector protected by the update sequence record in which + * this array is contained. Note that the first entry is the Update Sequence + * Number (usn), a cyclic counter of how many times the protected record has + * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All + * last u16's of each sector have to be equal to the usn (during reading) or + * are set to it (during writing). If they are not, an incomplete multi sector + * transfer has occurred when the data was written. + * The maximum size for the update sequence array is fixed to: + * maximum size = usa_ofs + (usa_count * 2) = 510 bytes + * The 510 bytes comes from the fact that the last u16 in the array has to + * (obviously) finish before the last u16 of the first 512-byte sector. + * This formula can be used as a consistency check in that usa_ofs + + * (usa_count * 2) has to be less than or equal to 510. + */ +typedef struct { + NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the + record type and/or status. */ + u16 usa_ofs; /* Offset to the Update Sequence Array (usa) + from the start of the ntfs record. */ + u16 usa_count; /* Number of u16 sized entries in the usa + including the Update Sequence Number (usn), + thus the number of fixups is the usa_count + minus 1. */ +} __attribute__((__packed__)) NTFS_RECORD; + +/** + * enum NTFS_SYSTEM_FILES - System files mft record numbers. + * + * All these files are always marked as used in the bitmap attribute of the + * mft; presumably in order to avoid accidental allocation for random other + * mft records. Also, the sequence number for each of the system files is + * always equal to their mft record number and it is never modified. + */ +typedef enum { + FILE_MFT = 0, /* Master file table (mft). Data attribute + contains the entries and bitmap attribute + records which ones are in use (bit==1). */ + FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records + in data attribute. If cluster size > 4kiB, + copy of first N mft records, with + N = cluster_size / mft_record_size. */ + FILE_LogFile = 2, /* Journalling log in data attribute. */ + FILE_Volume = 3, /* Volume name attribute and volume information + attribute (flags and ntfs version). Windows + refers to this file as volume DASD (Direct + Access Storage Device). */ + FILE_AttrDef = 4, /* Array of attribute definitions in data + attribute. */ + FILE_root = 5, /* Root directory. */ + FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in + data attribute. */ + FILE_Boot = 7, /* Boot sector (always at cluster 0) in data + attribute. */ + FILE_BadClus = 8, /* Contains all bad clusters in the non-resident + data attribute. */ + FILE_Secure = 9, /* Shared security descriptors in data attribute + and two indexes into the descriptors. + Appeared in Windows 2000. Before that, this + file was named $Quota but was unused. */ + FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode + characters in data attribute. */ + FILE_Extend = 11, /* Directory containing other system files (eg. + $ObjId, $Quota, $Reparse and $UsnJrnl). This + is new to NTFS3.0. */ + FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_first_user = 16, /* First user file, used as test limit for + whether to allow opening a file or not. */ +} NTFS_SYSTEM_FILES; + +/** + * enum MFT_RECORD_FLAGS - + * + * These are the so far known MFT_RECORD_* flags (16-bit) which contain + * information about the mft record in which they are present. + * + * MFT_RECORD_IS_4 exists on all $Extend sub-files. + * It seems that it marks it is a metadata file with MFT record >24, however, + * it is unknown if it is limited to metadata files only. + * + * MFT_RECORD_IS_VIEW_INDEX exists on every metafile with a non directory + * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other + * than "$I30". It is unknown if it is limited to metadata files only. + */ +typedef enum { + MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001), + MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002), + MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004), + MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008), + MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags + 16-bit. */ +} __attribute__((__packed__)) MFT_RECORD_FLAGS; + +/* + * mft references (aka file references or file record segment references) are + * used whenever a structure needs to refer to a record in the mft. + * + * A reference consists of a 48-bit index into the mft and a 16-bit sequence + * number used to detect stale references. + * + * For error reporting purposes we treat the 48-bit index as a signed quantity. + * + * The sequence number is a circular counter (skipping 0) describing how many + * times the referenced mft record has been (re)used. This has to match the + * sequence number of the mft record being referenced, otherwise the reference + * is considered stale and removed (FIXME: only ntfsck or the driver itself?). + * + * If the sequence number is zero it is assumed that no sequence number + * consistency checking should be performed. + * + * FIXME: Since inodes are 32-bit as of now, the driver needs to always check + * for high_part being 0 and if not either BUG(), cause a panic() or handle + * the situation in some other way. This shouldn't be a problem as a volume has + * to become HUGE in order to need more than 32-bits worth of mft records. + * Assuming the standard mft record size of 1kb only the records (never mind + * the non-resident attributes, etc.) would require 4Tb of space on their own + * for the first 32 bits worth of records. This is only if some strange person + * doesn't decide to foul play and make the mft sparse which would be a really + * horrible thing to do as it would trash our current driver implementation. )-: + * Do I hear screams "we want 64-bit inodes!" ?!? (-; + * + * FIXME: The mft zone is defined as the first 12% of the volume. This space is + * reserved so that the mft can grow contiguously and hence doesn't become + * fragmented. Volume free space includes the empty part of the mft zone and + * when the volume's free 88% are used up, the mft zone is shrunk by a factor + * of 2, thus making more space available for more files/data. This process is + * repeated every time there is no more free space except for the mft zone until + * there really is no more free space. + */ + +/* + * Typedef the MFT_REF as a 64-bit value for easier handling. + * Also define two unpacking macros to get to the reference (MREF) and + * sequence number (MSEQNO) respectively. + * The _LE versions are to be applied on little endian MFT_REFs. + * Note: The _LE versions will return a CPU endian formatted value! + */ +#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL +#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU) + +typedef u64 MFT_REF; +typedef le64 leMFT_REF; /* a little-endian MFT_MREF */ + +#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU))) +#define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) + +#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) +#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) +#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) +#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) + +#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) +#define ERR_MREF(x) ((u64)((s64)(x))) +#define MREF_ERR(x) ((int)((s64)(x))) + +/** + * struct MFT_RECORD - An MFT record layout (NTFS 3.1+) + * + * The mft record header present at the beginning of every record in the mft. + * This is followed by a sequence of variable length attribute records which + * is terminated by an attribute of type AT_END which is a truncated attribute + * in that it only consists of the attribute type code AT_END and none of the + * other members of the attribute structure are present. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ +/* 42*/ u16 reserved; /* Reserved/alignment. */ +/* 44*/ u32 mft_record_number; /* Number of this mft record. */ +/* sizeof() = 48 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD; + +/** + * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) + * + * This is the version without the NTFS 3.1+ specific fields. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* sizeof() = 42 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD_OLD; + +/** + * enum ATTR_TYPES - System defined attributes (32-bit). + * + * Each attribute type has a corresponding attribute name (Unicode string of + * maximum 64 character length) as described by the attribute definitions + * present in the data attribute of the $AttrDef system file. + * + * On NTFS 3.0 volumes the names are just as the types are named in the below + * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing + * choice of symbol... (-; + */ +typedef enum { + AT_UNUSED = const_cpu_to_le32( 0), + AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10), + AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20), + AT_FILE_NAME = const_cpu_to_le32( 0x30), + AT_OBJECT_ID = const_cpu_to_le32( 0x40), + AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50), + AT_VOLUME_NAME = const_cpu_to_le32( 0x60), + AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70), + AT_DATA = const_cpu_to_le32( 0x80), + AT_INDEX_ROOT = const_cpu_to_le32( 0x90), + AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0), + AT_BITMAP = const_cpu_to_le32( 0xb0), + AT_REPARSE_POINT = const_cpu_to_le32( 0xc0), + AT_EA_INFORMATION = const_cpu_to_le32( 0xd0), + AT_EA = const_cpu_to_le32( 0xe0), + AT_PROPERTY_SET = const_cpu_to_le32( 0xf0), + AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100), + AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000), + AT_END = const_cpu_to_le32(0xffffffff), +} ATTR_TYPES; + +/** + * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc + * (32-bit). + * + * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary + * Unicode values, except that when a character can be uppercased, the + * upper case value collates before the lower case one. + * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation + * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea + * what the difference is. Perhaps the difference is that file names + * would treat some special characters in an odd way (see + * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] + * for what I mean but COLLATION_UNICODE_STRING would not give any special + * treatment to any characters at all, but this is speculation. + * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key + * values. E.g. used for $SII index in FILE_Secure, which sorts by + * security_id (u32). + * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. + * E.g. used for $O index in FILE_Extend/$Quota. + * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash + * values and second by ascending security_id values. E.g. used for $SDH + * index in FILE_Secure. + * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending + * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which + * sorts by object_id (16-byte), by splitting up the object_id in four + * u32 values and using them as individual keys. E.g. take the following + * two security_ids, stored as follows on disk: + * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 + * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 + * To compare them, they are split into four u32 values each, like so: + * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 + * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 + * Now, it is apparent why the 2nd object_id collates after the 1st: the + * first u32 value of the 1st object_id is less than the first u32 of + * the 2nd object_id. If the first u32 values of both object_ids were + * equal then the second u32 values would be compared, etc. + */ +typedef enum { + COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary + compare where the first byte is most + significant. */ + COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names + as Unicode strings. */ + COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode + strings by comparing their binary + Unicode values, except that when a + character can be uppercased, the upper + case value collates before the lower + case one. */ + COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), + COLLATION_NTOFS_SID = const_cpu_to_le32(17), + COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), + COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), +} COLLATION_RULES; + +/** + * enum ATTR_DEF_FLAGS - + * + * The flags (32-bit) describing attribute properties in the attribute + * definition structure. FIXME: This information is based on Regis's + * information and, according to him, it is not certain and probably + * incomplete. The INDEXABLE flag is fairly certainly correct as only the file + * name attribute has this flag set and this is the only attribute indexed in + * NT4. + */ +typedef enum { + ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be + indexed. */ + ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type + can be present multiple times in the + mft records of an inode. */ + ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value + must contain at least one non-zero + byte. */ + ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be + indexed and the attribute value must be + unique for the attribute type in all of + the mft records of an inode. */ + ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be + named and the name must be unique for + the attribute type in all of the mft + records of an inode. */ + ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be + resident. */ + ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log + modifications to this attribute, + regardless of whether it is resident or + non-resident. Without this, only log + modifications if the attribute is + resident. */ +} ATTR_DEF_FLAGS; + +/** + * struct ATTR_DEF - + * + * The data attribute of FILE_AttrDef contains a sequence of attribute + * definitions for the NTFS volume. With this, it is supposed to be safe for an + * older NTFS driver to mount a volume containing a newer NTFS version without + * damaging it (that's the theory. In practice it's: not damaging it too much). + * Entries are sorted by attribute type. The flags describe whether the + * attribute can be resident/non-resident and possibly other things, but the + * actual bits are unknown. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero + terminated. */ +/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ +/* 84*/ u32 display_rule; /* Default display rule. + FIXME: What does it mean? (AIA) */ +/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ +/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ +/* 90*/ s64 min_size; /* Optional minimum attribute size. */ +/* 98*/ s64 max_size; /* Maximum size of attribute. */ +/* sizeof() = 0xa0 or 160 bytes */ +} __attribute__((__packed__)) ATTR_DEF; + +/** + * enum ATTR_FLAGS - Attribute flags (16-bit). + */ +typedef enum { + ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001), + ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression + method mask. Also, first + illegal value. */ + ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000), + ATTR_IS_SPARSE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) ATTR_FLAGS; + +/* + * Attribute compression. + * + * Only the data attribute is ever compressed in the current ntfs driver in + * Windows. Further, compression is only applied when the data attribute is + * non-resident. Finally, to use compression, the maximum allowed cluster size + * on a volume is 4kib. + * + * The compression method is based on independently compressing blocks of X + * clusters, where X is determined from the compression_unit value found in the + * non-resident attribute record header (more precisely: X = 2^compression_unit + * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4). + * + * There are three different cases of how a compression block of X clusters + * can be stored: + * + * 1) The data in the block is all zero (a sparse block): + * This is stored as a sparse block in the runlist, i.e. the runlist + * entry has length = X and lcn = -1. The mapping pairs array actually + * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at + * all, which is then interpreted by the driver as lcn = -1. + * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then + * the same principles apply as above, except that the length is not + * restricted to being any particular value. + * + * 2) The data in the block is not compressed: + * This happens when compression doesn't reduce the size of the block + * in clusters. I.e. if compression has a small effect so that the + * compressed data still occupies X clusters, then the uncompressed data + * is stored in the block. + * This case is recognised by the fact that the runlist entry has + * length = X and lcn >= 0. The mapping pairs array stores this as + * normal with a run length of X and some specific delta_lcn, i.e. + * delta_lcn has to be present. + * + * 3) The data in the block is compressed: + * The common case. This case is recognised by the fact that the run + * list entry has length L < X and lcn >= 0. The mapping pairs array + * stores this as normal with a run length of X and some specific + * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is + * immediately followed by a sparse entry with length = X - L and + * lcn = -1. The latter entry is to make up the vcn counting to the + * full compression block size X. + * + * In fact, life is more complicated because adjacent entries of the same type + * can be coalesced. This means that one has to keep track of the number of + * clusters handled and work on a basis of X clusters at a time being one + * block. An example: if length L > X this means that this particular runlist + * entry contains a block of length X and part of one or more blocks of length + * L - X. Another example: if length L < X, this does not necessarily mean that + * the block is compressed as it might be that the lcn changes inside the block + * and hence the following runlist entry describes the continuation of the + * potentially compressed block. The block would be compressed if the + * following runlist entry describes at least X - L sparse clusters, thus + * making up the compression block length as described in point 3 above. (Of + * course, there can be several runlist entries with small lengths so that the + * sparse entry does not follow the first data containing entry with + * length < X.) + * + * NOTE: At the end of the compressed attribute value, there most likely is not + * just the right amount of data to make up a compression block, thus this data + * is not even attempted to be compressed. It is just stored as is, unless + * the number of clusters it occupies is reduced when compressed in which case + * it is stored as a compressed compression block, complete with sparse + * clusters at the end. + */ + +/** + * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit). + */ +typedef enum { + RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index + (has implications for deleting and + modifying the attribute). */ +} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS; + +/** + * struct ATTR_RECORD - Attribute record header. + * + * Always aligned to 8-byte boundary. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ +/* 4*/ u32 length; /* Byte size of the resident part of the + attribute (aligned to 8-byte boundary). + Used to get to the next attribute. */ +/* 8*/ u8 non_resident; /* If 0, attribute is resident. + If 1, attribute is non-resident. */ +/* 9*/ u8 name_length; /* Unicode character size of name of attribute. + 0 if unnamed. */ +/* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the + beginning of the name from the attribute + record. Note that the name is stored as a + Unicode string. When creating, place offset + just at the end of the record header. Then, + follow with attribute value or mapping pairs + array, resident and non-resident attributes + respectively, aligning to an 8-byte + boundary. */ +/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ +/* 14*/ u16 instance; /* The instance of this attribute record. This + number is unique within this mft record (see + MFT_RECORD/next_attribute_instance notes + above for more details). */ +/* 16*/ union { + /* Resident attributes. */ + struct { +/* 16 */ u32 value_length; /* Byte size of attribute value. */ +/* 20 */ u16 value_offset; /* Byte offset of the attribute + value from the start of the + attribute record. When creating, + align to 8-byte boundary if we + have a name present as this might + not have a length of a multiple + of 8-bytes. */ +/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ +/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte + boundary. */ +/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, + resident_end) to get size of + a resident attribute. */ + } __attribute__((__packed__)); + /* Non-resident attributes. */ + struct { +/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number + for this portion of the attribute value or + 0 if this is the only extent (usually the + case). - Only when an attribute list is used + does lowest_vcn != 0 ever occur. */ +/* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of + the attribute value. - Usually there is only one + portion, so this usually equals the attribute + value size in clusters minus 1. Can be -1 for + zero length files. Can be 0 for "single extent" + attributes. */ +/* 32*/ u16 mapping_pairs_offset; /* Byte offset from the + beginning of the structure to the mapping pairs + array which contains the mappings between the + vcns and the logical cluster numbers (lcns). + When creating, place this at the end of this + record header aligned to 8-byte boundary. */ +/* 34*/ u8 compression_unit; /* The compression unit expressed + as the log to the base 2 of the number of + clusters in a compression unit. 0 means not + compressed. (This effectively limits the + compression unit size to be a power of two + clusters.) WinNT4 only uses a value of 4. */ +/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ +/* The sizes below are only used when lowest_vcn is zero, as otherwise it would + be difficult to keep them up-to-date.*/ +/* 40*/ s64 allocated_size; /* Byte size of disk space + allocated to hold the attribute value. Always + is a multiple of the cluster size. When a file + is compressed, this field is a multiple of the + compression block size (2^compression_unit) and + it represents the logically allocated space + rather than the actual on disk usage. For this + use the compressed_size (see below). */ +/* 48*/ s64 data_size; /* Byte size of the attribute + value. Can be larger than allocated_size if + attribute value is compressed or sparse. */ +/* 56*/ s64 initialized_size; /* Byte size of initialized + portion of the attribute value. Usually equals + data_size. */ +/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, + non_resident_end) to get + size of a non resident + attribute. */ +/* sizeof(uncompressed attr) = 64*/ +/* 64*/ s64 compressed_size; /* Byte size of the attribute + value after compression. Only present when + compressed. Always is a multiple of the + cluster size. Represents the actual amount of + disk space being used on the disk. */ +/* 72 */ void *compressed_end[0]; + /* Use offsetof(ATTR_RECORD, compressed_end) to + get size of a compressed attribute. */ +/* sizeof(compressed attr) = 72*/ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) ATTR_RECORD; + +typedef ATTR_RECORD ATTR_REC; + +/** + * enum FILE_ATTR_FLAGS - File attribute flags (32-bit). + */ +typedef enum { + /* + * These flags are only present in the STANDARD_INFORMATION attribute + * (in the field file_attributes). + */ + FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001), + FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002), + FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004), + /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ + + FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), + /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved + for the DOS SUBDIRECTORY flag. */ + FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020), + FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040), + FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080), + + FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100), + FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200), + FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400), + FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800), + + FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), + FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), + FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), + + FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), + /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the + FILE_ATTR_DEVICE and preserves everything else. This mask + is used to obtain all flags that are valid for reading. */ + FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7), + /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the + FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, + FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED + and preserves the rest. This mask is used to to obtain all flags that + are valid for setting. */ + + /** + * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? + * + * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft + * record, telling us whether this is a directory or not, i.e. whether + * it has an index root attribute named "$I30" or not. + * + * This flag is only present in the FILE_NAME attribute (in the + * file_attributes field). + */ + FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000), + + /** + * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? + * + * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft + * record, telling us whether this file has a view index present (eg. + * object id index, quota index, one of the security indexes and the + * reparse points index). + * + * This flag is only present in the $STANDARD_INFORMATION and + * $FILE_NAME attributes. + */ + FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000), +} __attribute__((__packed__)) FILE_ATTR_FLAGS; + +/* + * NOTE on times in NTFS: All times are in MS standard time format, i.e. they + * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00 + * universal coordinated time (UTC). (In Linux time starts 1st January 1970, + * 00:00:00 UTC and is stored as the number of 1-second intervals since then.) + */ + +/** + * struct STANDARD_INFORMATION - Attribute: Standard information (0x10). + * + * NOTE: Always resident. + * NOTE: Present in all base file records on a volume. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*Ofs*/ +/* 0*/ s64 creation_time; /* Time file was created. Updated when + a filename is changed(?). */ +/* 8*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 16*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 24*/ s64 last_access_time; /* Approximate time when the file was + last accessed (obviously this is not + updated on read-only volumes). In + Windows this is only updated when + accessed if some time delta has + passed since the last update. Also, + last access times updates can be + disabled altogether for speed. */ +/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 36*/ union { + /* NTFS 1.2 (and previous, presumably) */ + struct { + /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte + boundary. */ + /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); +/* sizeof() = 48 bytes */ + /* NTFS 3.0 */ + struct { +/* + * If a volume has been upgraded from a previous NTFS version, then these + * fields are present only if the file has been accessed since the upgrade. + * Recognize the difference by comparing the length of the resident attribute + * value. If it is 48, then the following fields are missing. If it is 72 then + * the fields are present. Maybe just check like this: + * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { + * Assume NTFS 1.2- format. + * If (volume version is 3.0+) + * Upgrade attribute to NTFS 3.0 format. + * else + * Use NTFS 1.2- format for access. + * } else + * Use NTFS 3.0 format for access. + * Only problem is that it might be legal to set the length of the value to + * arbitrarily large values thus spoiling this check. - But chkdsk probably + * views that as a corruption, assuming that it behaves like this for all + * attributes. + */ + /* 36*/ u32 maximum_versions; /* Maximum allowed versions for + file. Zero if version numbering is disabled. */ + /* 40*/ u32 version_number; /* This file's version (if any). + Set to zero if maximum_versions is zero. */ + /* 44*/ u32 class_id; /* Class id from bidirectional + class id index (?). */ + /* 48*/ u32 owner_id; /* Owner_id of the user owning + the file. Translate via $Q index in FILE_Extend + /$Quota to the quota control entry for the user + owning the file. Zero if quotas are disabled. */ + /* 52*/ u32 security_id; /* Security_id for the file. + Translate via $SII index and $SDS data stream + in FILE_Secure to the security descriptor. */ + /* 56*/ u64 quota_charged; /* Byte size of the charge to + the quota for all streams of the file. Note: Is + zero if quotas are disabled. */ + /* 64*/ u64 usn; /* Last update sequence number + of the file. This is a direct index into the + change (aka usn) journal file. It is zero if + the usn journal is disabled. + NOTE: To disable the journal need to delete + the journal file itself and to then walk the + whole mft and set all Usn entries in all mft + records to zero! (This can take a while!) + The journal is FILE_Extend/$UsnJrnl. Win2k + will recreate the journal and initiate + logging if necessary when mounting the + partition. This, in contrast to disabling the + journal is a very fast process, so the user + won't even notice it. */ + /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* sizeof() = 72 bytes (NTFS 3.0) */ +} __attribute__((__packed__)) STANDARD_INFORMATION; + +/** + * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20). + * + * - Can be either resident or non-resident. + * - Value consists of a sequence of variable length, 8-byte aligned, + * ATTR_LIST_ENTRY records. + * - The attribute list attribute contains one entry for each attribute of + * the file in which the list is located, except for the list attribute + * itself. The list is sorted: first by attribute type, second by attribute + * name (if present), third by instance number. The extents of one + * non-resident attribute (if present) immediately follow after the initial + * extent. They are ordered by lowest_vcn and have their instance set to zero. + * It is not allowed to have two attributes with all sorting keys equal. + * - Further restrictions: + * - If not resident, the vcn to lcn mapping array has to fit inside the + * base mft record. + * - The attribute list attribute value has a maximum size of 256kb. This + * is imposed by the Windows cache manager. + * - Attribute lists are only used when the attributes of mft record do not + * fit inside the mft record despite all attributes (that can be made + * non-resident) having been made non-resident. This can happen e.g. when: + * - File has a large number of hard links (lots of file name + * attributes present). + * - The mapping pairs array of some non-resident attribute becomes so + * large due to fragmentation that it overflows the mft record. + * - The security descriptor is very complex (not applicable to + * NTFS 3.0 volumes). + * - There are many named streams. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ +/* 4*/ u16 length; /* Byte size of this entry. */ +/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the + attribute or 0 if unnamed. */ +/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name + (always set this to where the name would + start even if unnamed). */ +/* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion + of the attribute value. This is usually 0. It + is non-zero for the case where one attribute + does not fit into one mft record and thus + several mft records are allocated to hold + this attribute. In the latter case, each mft + record holds one extent of the attribute and + there is one attribute list entry for each + extent. NOTE: This is DEFINITELY a signed + value! The windows driver uses cmp, followed + by jg when comparing this, thus it treats it + as signed. */ +/* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding + the ATTR_RECORD for this portion of the + attribute value. */ +/* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the + attribute being referenced; otherwise 0. */ +/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use + name_offset to determine the location of the + name. */ +/* sizeof() = 26 + (attribute_name_length * 2) bytes */ +} __attribute__((__packed__)) ATTR_LIST_ENTRY; + +/* + * The maximum allowed length for a file name. + */ +#define NTFS_MAX_NAME_LEN 255 + +/** + * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs. + * (8-bit). + */ +typedef enum { + FILE_NAME_POSIX = 0x00, + /* This is the largest namespace. It is case sensitive and + allows all Unicode characters except for: '\0' and '/'. + Beware that in WinNT/2k files which eg have the same name + except for their case will not be distinguished by the + standard utilities and thus a "del filename" will delete + both "filename" and "fileName" without warning. */ + FILE_NAME_WIN32 = 0x01, + /* The standard WinNT/2k NTFS long filenames. Case insensitive. + All Unicode chars except: '\0', '"', '*', '/', ':', '<', + '>', '?', '\' and '|'. Further, names cannot end with a '.' + or a space. */ + FILE_NAME_DOS = 0x02, + /* The standard DOS filenames (8.3 format). Uppercase only. + All 8-bit characters greater space, except: '"', '*', '+', + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + FILE_NAME_WIN32_AND_DOS = 0x03, + /* 3 means that both the Win32 and the DOS filenames are + identical and hence have been saved in this single filename + record. */ +} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS; + +/** + * struct FILE_NAME_ATTR - Attribute: Filename (0x30). + * + * NOTE: Always resident. + * NOTE: All fields, except the parent_directory, are only updated when the + * filename is changed. Until then, they just become out of sync with + * reality and the more up to date values are present in the standard + * information attribute. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ MFT_REF parent_directory; /* Directory this filename is + referenced from. */ +/* 8*/ s64 creation_time; /* Time file was created. */ +/* 10*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 18*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 20*/ s64 last_access_time; /* Last time this mft record was + accessed. */ +/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space + for the data attribute. So for + normal $DATA, this is the + allocated_size from the unnamed + $DATA attribute and for compressed + and/or sparse $DATA, this is the + compressed_size from the unnamed + $DATA attribute. NOTE: This is a + multiple of the cluster size. */ +/* 30*/ s64 data_size; /* Byte size of actual data in data + attribute. */ +/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 3c*/ union { + /* 3c*/ struct { + /* 3c*/ u16 packed_ea_size; /* Size of the buffer needed to + pack the extended attributes + (EAs), if such are present.*/ + /* 3e*/ u16 reserved; /* Reserved for alignment. */ + } __attribute__((__packed__)); + /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, + present only in reparse + points and only if there are + no EAs. */ + } __attribute__((__packed__)); +/* 40*/ u8 file_name_length; /* Length of file name in + (Unicode) characters. */ +/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ +/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ +} __attribute__((__packed__)) FILE_NAME_ATTR; + +/** + * struct GUID - GUID structures store globally unique identifiers (GUID). + * + * A GUID is a 128-bit value consisting of one group of eight hexadecimal + * digits, followed by three groups of four hexadecimal digits each, followed + * by one group of twelve hexadecimal digits. GUIDs are Microsoft's + * implementation of the distributed computing environment (DCE) universally + * unique identifier (UUID). + * + * Example of a GUID: + * 1F010768-5A73-BC91-0010-A52216A7227B + */ +typedef struct { + u32 data1; /* The first eight hexadecimal digits of the GUID. */ + u16 data2; /* The first group of four hexadecimal digits. */ + u16 data3; /* The second group of four hexadecimal digits. */ + u8 data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} __attribute__((__packed__)) GUID; + +/** + * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O. + * + * This index contains all object_ids present on the volume as the index keys + * and the corresponding mft_record numbers as the index entry data parts. + * + * The data part (defined below) also contains three other object_ids: + * birth_volume_id - object_id of FILE_Volume on which the file was first + * created. Optional (i.e. can be zero). + * birth_object_id - object_id of file when it was first created. Usually + * equals the object_id. Optional (i.e. can be zero). + * domain_id - Reserved (always zero). + */ +typedef struct { + MFT_REF mft_reference; /* Mft record containing the object_id in + the index entry key. */ + union { + struct { + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; + +/** + * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). + * + * NOTE: Always resident. + */ +typedef struct { + GUID object_id; /* Unique id assigned to the + file.*/ + /* The following fields are optional. The attribute value size is 16 + bytes, i.e. sizeof(GUID), if these are not present at all. Note, + the entries can be present but one or more (or all) can be zero + meaning that that particular value(s) is(are) not defined. Note, + when the fields are missing here, it is well possible that they are + to be found within the $Extend/$ObjId system file indexed under the + above object_id. */ + union { + struct { + GUID birth_volume_id; /* Unique id of volume on which + the file was first created.*/ + GUID birth_object_id; /* Unique id of file when it was + first created. */ + GUID domain_id; /* Reserved, zero. */ + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJECT_ID_ATTR; + +#if 0 +/** + * enum IDENTIFIER_AUTHORITIES - + * + * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in + * the SID structure (see below). + */ +typedef enum { /* SID string prefix. */ + SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ + SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ + SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ + SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ + SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ + SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ +} IDENTIFIER_AUTHORITIES; +#endif + +/** + * enum RELATIVE_IDENTIFIERS - + * + * These relative identifiers (RIDs) are used with the above identifier + * authorities to make up universal well-known SIDs. + * + * Note: The relative identifier (RID) refers to the portion of a SID, which + * identifies a user or group in relation to the authority that issued the SID. + * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is + * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and + * the relative identifier SECURITY_CREATOR_OWNER_RID (0). + */ +typedef enum { /* Identifier authority. */ + SECURITY_NULL_RID = 0, /* S-1-0 */ + SECURITY_WORLD_RID = 0, /* S-1-1 */ + SECURITY_LOCAL_RID = 0, /* S-1-2 */ + + SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ + SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ + + SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ + SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ + + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_SERVICE_RID = 6, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID=9, + SECURITY_SERVER_LOGON_RID = 9, + SECURITY_PRINCIPAL_SELF_RID = 0xa, + SECURITY_AUTHENTICATED_USER_RID = 0xb, + SECURITY_RESTRICTED_CODE_RID = 0xc, + SECURITY_TERMINAL_SERVER_RID = 0xd, + + SECURITY_LOGON_IDS_RID = 5, + SECURITY_LOGON_IDS_RID_COUNT = 3, + + SECURITY_LOCAL_SYSTEM_RID = 0x12, + + SECURITY_NT_NON_UNIQUE = 0x15, + + SECURITY_BUILTIN_DOMAIN_RID = 0x20, + + /* + * Well-known domain relative sub-authority values (RIDs). + */ + + /* Users. */ + DOMAIN_USER_RID_ADMIN = 0x1f4, + DOMAIN_USER_RID_GUEST = 0x1f5, + DOMAIN_USER_RID_KRBTGT = 0x1f6, + + /* Groups. */ + DOMAIN_GROUP_RID_ADMINS = 0x200, + DOMAIN_GROUP_RID_USERS = 0x201, + DOMAIN_GROUP_RID_GUESTS = 0x202, + DOMAIN_GROUP_RID_COMPUTERS = 0x203, + DOMAIN_GROUP_RID_CONTROLLERS = 0x204, + DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, + DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, + DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207, + DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, + + /* Aliases. */ + DOMAIN_ALIAS_RID_ADMINS = 0x220, + DOMAIN_ALIAS_RID_USERS = 0x221, + DOMAIN_ALIAS_RID_GUESTS = 0x222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x223, + + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, + + DOMAIN_ALIAS_RID_REPLICATOR = 0x228, + DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, + DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, +} RELATIVE_IDENTIFIERS; + +/* + * The universal well-known SIDs: + * + * NULL_SID S-1-0-0 + * WORLD_SID S-1-1-0 + * LOCAL_SID S-1-2-0 + * CREATOR_OWNER_SID S-1-3-0 + * CREATOR_GROUP_SID S-1-3-1 + * CREATOR_OWNER_SERVER_SID S-1-3-2 + * CREATOR_GROUP_SERVER_SID S-1-3-3 + * + * (Non-unique IDs) S-1-4 + * + * NT well-known SIDs: + * + * NT_AUTHORITY_SID S-1-5 + * DIALUP_SID S-1-5-1 + * + * NETWORD_SID S-1-5-2 + * BATCH_SID S-1-5-3 + * INTERACTIVE_SID S-1-5-4 + * SERVICE_SID S-1-5-6 + * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) + * PROXY_SID S-1-5-8 + * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) + * SELF_SID S-1-5-10 (self RID) + * AUTHENTICATED_USER_SID S-1-5-11 + * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) + * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) + * + * (Logon IDs) S-1-5-5-X-Y + * + * (NT non-unique IDs) S-1-5-0x15-... + * + * (Built-in domain) S-1-5-0x20 + */ + +/** + * union SID_IDENTIFIER_AUTHORITY - A 48-bit value used in the SID structure + * + * NOTE: This is stored as a big endian number. + */ +typedef union { + struct { + u16 high_part; /* High 16-bits. */ + u32 low_part; /* Low 32-bits. */ + } __attribute__((__packed__)); + u8 value[6]; /* Value as individual bytes. */ +} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; + +/** + * struct SID - + * + * The SID structure is a variable-length structure used to uniquely identify + * users or groups. SID stands for security identifier. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * Example SID; the domain-relative SID of the local Administrators group on + * Windows NT/2k: + * S-1-5-32-544 + * This translates to a SID with: + * revision = 1, + * sub_authority_count = 2, + * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY + * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID + * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS + */ +typedef struct { + u8 revision; + u8 sub_authority_count; + SID_IDENTIFIER_AUTHORITY identifier_authority; + u32 sub_authority[1]; /* At least one sub_authority. */ +} __attribute__((__packed__)) SID; + +/** + * enum SID_CONSTANTS - Current constants for SIDs. + */ +typedef enum { + SID_REVISION = 1, /* Current revision level. */ + SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ + SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in + a future revision. */ +} SID_CONSTANTS; + +/** + * enum ACE_TYPES - The predefined ACE types (8-bit, see below). + */ +typedef enum { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ + ACCESS_MAX_MS_V2_ACE_TYPE = 3, + + ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, + + /* The following are Win2k only. */ + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + + ACCESS_MAX_MS_V4_ACE_TYPE = 8, + + /* This one is for WinNT&2k. */ + ACCESS_MAX_MS_ACE_TYPE = 8, +} __attribute__((__packed__)) ACE_TYPES; + +/** + * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance. + * + * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE + * types to indicate that a message is generated (in Windows!) for successful + * accesses. + * + * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types + * to indicate that a message is generated (in Windows!) for failed accesses. + */ +typedef enum { + /* The inheritance flags. */ + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, /* Win2k only. */ + VALID_INHERIT_FLAGS = 0x1f, + + /* The audit flags. */ + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +} __attribute__((__packed__)) ACE_FLAGS; + +/** + * struct ACE_HEADER - + * + * An ACE is an access-control entry in an access-control list (ACL). + * An ACE defines access to an object for a specific user or group or defines + * the types of access that generate system-administration messages or alarms + * for a specific user or group. The user or group is identified by a security + * identifier (SID). + * + * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary), + * which specifies the type and size of the ACE. The format of the subsequent + * data depends on the ACE type. + */ +typedef struct { + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ +} __attribute__((__packed__)) ACE_HEADER; + +/** + * enum ACCESS_MASK - The access mask (32-bit). + * + * Defines the access rights. + */ +typedef enum { + /* + * The specific rights (bits 0 to 15). Depend on the type of the + * object being secured by the ACE. + */ + + /* Specific rights for files and directories are as follows: */ + + /* Right to read data from the file. (FILE) */ + FILE_READ_DATA = const_cpu_to_le32(0x00000001), + /* Right to list contents of a directory. (DIRECTORY) */ + FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001), + + /* Right to write data to the file. (FILE) */ + FILE_WRITE_DATA = const_cpu_to_le32(0x00000002), + /* Right to create a file in the directory. (DIRECTORY) */ + FILE_ADD_FILE = const_cpu_to_le32(0x00000002), + + /* Right to append data to the file. (FILE) */ + FILE_APPEND_DATA = const_cpu_to_le32(0x00000004), + /* Right to create a subdirectory. (DIRECTORY) */ + FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004), + + /* Right to read extended attributes. (FILE/DIRECTORY) */ + FILE_READ_EA = const_cpu_to_le32(0x00000008), + + /* Right to write extended attributes. (FILE/DIRECTORY) */ + FILE_WRITE_EA = const_cpu_to_le32(0x00000010), + + /* Right to execute a file. (FILE) */ + FILE_EXECUTE = const_cpu_to_le32(0x00000020), + /* Right to traverse the directory. (DIRECTORY) */ + FILE_TRAVERSE = const_cpu_to_le32(0x00000020), + + /* + * Right to delete a directory and all the files it contains (its + * children), even if the files are read-only. (DIRECTORY) + */ + FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040), + + /* Right to read file attributes. (FILE/DIRECTORY) */ + FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080), + + /* Right to change file attributes. (FILE/DIRECTORY) */ + FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100), + + /* + * The standard rights (bits 16 to 23). Are independent of the type of + * object being secured. + */ + + /* Right to delete the object. */ + DELETE = const_cpu_to_le32(0x00010000), + + /* + * Right to read the information in the object's security descriptor, + * not including the information in the SACL. I.e. right to read the + * security descriptor and owner. + */ + READ_CONTROL = const_cpu_to_le32(0x00020000), + + /* Right to modify the DACL in the object's security descriptor. */ + WRITE_DAC = const_cpu_to_le32(0x00040000), + + /* Right to change the owner in the object's security descriptor. */ + WRITE_OWNER = const_cpu_to_le32(0x00080000), + + /* + * Right to use the object for synchronization. Enables a process to + * wait until the object is in the signalled state. Some object types + * do not support this access right. + */ + SYNCHRONIZE = const_cpu_to_le32(0x00100000), + + /* + * The following STANDARD_RIGHTS_* are combinations of the above for + * convenience and are defined by the Win32 API. + */ + + /* These are currently defined to READ_CONTROL. */ + STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000), + + /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ + STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000), + + /* + * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and + * SYNCHRONIZE access. + */ + STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000), + + /* + * The access system ACL and maximum allowed access types (bits 24 to + * 25, bits 26 to 27 are reserved). + */ + ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000), + MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000), + + /* + * The generic rights (bits 28 to 31). These map onto the standard and + * specific rights. + */ + + /* Read, write, and execute access. */ + GENERIC_ALL = const_cpu_to_le32(0x10000000), + + /* Execute access. */ + GENERIC_EXECUTE = const_cpu_to_le32(0x20000000), + + /* + * Write access. For files, this maps onto: + * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | + * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_WRITE = const_cpu_to_le32(0x40000000), + + /* + * Read access. For files, this maps onto: + * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | + * STANDARD_RIGHTS_READ | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_READ = const_cpu_to_le32(0x80000000), +} ACCESS_MASK; + +/** + * struct GENERIC_MAPPING - + * + * The generic mapping array. Used to denote the mapping of each generic + * access right to a specific access mask. + * + * FIXME: What exactly is this and what is it for? (AIA) + */ +typedef struct { + ACCESS_MASK generic_read; + ACCESS_MASK generic_write; + ACCESS_MASK generic_execute; + ACCESS_MASK generic_all; +} __attribute__((__packed__)) GENERIC_MAPPING; + +/* + * The predefined ACE type structures are as defined below. + */ + +/** + * struct ACCESS_DENIED_ACE - + * + * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, + SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; + +/** + * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit). + */ +typedef enum { + ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1), + ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2), +} OBJECT_ACE_FLAGS; + +/** + * struct ACCESS_ALLOWED_OBJECT_ACE - + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ +/* 12*/ GUID object_type; +/* 28*/ GUID inherited_object_type; +/* 44*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE, + ACCESS_DENIED_OBJECT_ACE, + SYSTEM_AUDIT_OBJECT_ACE, + SYSTEM_ALARM_OBJECT_ACE; + +/** + * struct ACL - An ACL is an access-control list (ACL). + * + * An ACL starts with an ACL header structure, which specifies the size of + * the ACL and the number of ACEs it contains. The ACL header is followed by + * zero or more access control entries (ACEs). The ACL as well as each ACE + * are aligned on 4-byte boundaries. + */ +typedef struct { + u8 revision; /* Revision of this ACL. */ + u8 alignment1; + u16 size; /* Allocated space in bytes for ACL. Includes this + header, the ACEs and the remaining free space. */ + u16 ace_count; /* Number of ACEs in the ACL. */ + u16 alignment2; +/* sizeof() = 8 bytes */ +} __attribute__((__packed__)) ACL; + +/** + * enum ACL_CONSTANTS - Current constants for ACLs. + */ +typedef enum { + /* Current revision. */ + ACL_REVISION = 2, + ACL_REVISION_DS = 4, + + /* History of revisions. */ + ACL_REVISION1 = 1, + MIN_ACL_REVISION = 2, + ACL_REVISION2 = 2, + ACL_REVISION3 = 3, + ACL_REVISION4 = 4, + MAX_ACL_REVISION = 4, +} ACL_CONSTANTS; + +/** + * enum SECURITY_DESCRIPTOR_CONTROL - + * + * The security descriptor control flags (16-bit). + * + * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the + * SID pointed to by the Owner field was provided by a + * defaulting mechanism rather than explicitly provided by the + * original provider of the security descriptor. This may + * affect the treatment of the SID with respect to inheritance + * of an owner. + * + * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the + * SID in the Group field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of + * the security descriptor. This may affect the treatment of + * the SID with respect to inheritance of a primary group. + * + * SE_DACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a discretionary ACL. If this + * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is + * null, then a null ACL is explicitly being specified. + * + * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Dacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the DaclPresent flag is not set. + * + * SE_SACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a system ACL pointed to by the + * Sacl field. If this flag is set and the Sacl field of the + * SECURITY_DESCRIPTOR is null, then an empty (but present) + * ACL is being specified. + * + * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Sacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the SaclPresent flag is not set. + * + * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the + * security descriptor is in self-relative form. In this form, + * all fields of the security descriptor are contiguous in memory + * and all pointer fields are expressed as offsets from the + * beginning of the security descriptor. + */ +typedef enum { + SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), + SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002), + SE_DACL_PRESENT = const_cpu_to_le16(0x0004), + SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008), + SE_SACL_PRESENT = const_cpu_to_le16(0x0010), + SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020), + SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100), + SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200), + SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400), + SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800), + SE_DACL_PROTECTED = const_cpu_to_le16(0x1000), + SE_SACL_PROTECTED = const_cpu_to_le16(0x2000), + SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000), + SE_SELF_RELATIVE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL; + +/** + * struct SECURITY_DESCRIPTOR_RELATIVE - + * + * Self-relative security descriptor. Contains the owner and group SIDs as well + * as the sacl and dacl ACLs inside the security descriptor itself. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + u32 owner; /* Byte offset to a SID representing an object's + owner. If this is NULL, no owner SID is present in + the descriptor. */ + u32 group; /* Byte offset to a SID representing an object's + primary group. If this is NULL, no primary group + SID is present in the descriptor. */ + u32 sacl; /* Byte offset to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +/* sizeof() = 0x14 bytes */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE; + +/** + * struct SECURITY_DESCRIPTOR - Absolute security descriptor. + * + * Does not contain the owner and group SIDs, nor the sacl and dacl ACLs inside + * the security descriptor. Instead, it contains pointers to these structures + * in memory. Obviously, absolute security descriptors are only useful for in + * memory representations of security descriptors. + * + * On disk, a self-relative security descriptor is used. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + SID *owner; /* Points to a SID representing an object's owner. If + this is NULL, no owner SID is present in the + descriptor. */ + SID *group; /* Points to a SID representing an object's primary + group. If this is NULL, no primary group SID is + present in the descriptor. */ + ACL *sacl; /* Points to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + ACL *dacl; /* Points to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR; + +/** + * enum SECURITY_DESCRIPTOR_CONSTANTS - + * + * Current constants for security descriptors. + */ +typedef enum { + /* Current revision. */ + SECURITY_DESCRIPTOR_REVISION = 1, + SECURITY_DESCRIPTOR_REVISION1 = 1, + + /* The sizes of both the absolute and relative security descriptors is + the same as pointers, at least on ia32 architecture are 32-bit. */ + SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR), +} SECURITY_DESCRIPTOR_CONSTANTS; + +/* + * Attribute: Security descriptor (0x50). + * + * A standard self-relative security descriptor. + * + * NOTE: Can be resident or non-resident. + * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally + * in FILE_Secure and the correct descriptor is found using the security_id + * from the standard information attribute. + */ +typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; + +/* + * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one + * referenced instance of each unique security descriptor is stored. + * + * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It + * does, however, contain two indexes ($SDH and $SII) as well as a named data + * stream ($SDS). + * + * Every unique security descriptor is assigned a unique security identifier + * (security_id, not to be confused with a SID). The security_id is unique for + * the NTFS volume and is used as an index into the $SII index, which maps + * security_ids to the security descriptor's storage location within the $SDS + * data attribute. The $SII index is sorted by ascending security_id. + * + * A simple hash is computed from each security descriptor. This hash is used + * as an index into the $SDH index, which maps security descriptor hashes to + * the security descriptor's storage location within the $SDS data attribute. + * The $SDH index is sorted by security descriptor hash and is stored in a B+ + * tree. When searching $SDH (with the intent of determining whether or not a + * new security descriptor is already present in the $SDS data stream), if a + * matching hash is found, but the security descriptors do not match, the + * search in the $SDH index is continued, searching for a next matching hash. + * + * When a precise match is found, the security_id corresponding to the security + * descriptor in the $SDS attribute is read from the found $SDH index entry and + * is stored in the $STANDARD_INFORMATION attribute of the file/directory to + * which the security descriptor is being applied. The $STANDARD_INFORMATION + * attribute is present in all base mft records (i.e. in all files and + * directories). + * + * If a match is not found, the security descriptor is assigned a new unique + * security_id and is added to the $SDS data attribute. Then, entries + * referencing the this security descriptor in the $SDS data attribute are + * added to the $SDH and $SII indexes. + * + * Note: Entries are never deleted from FILE_Secure, even if nothing + * references an entry any more. + */ + +/** + * struct SECURITY_DESCRIPTOR_HEADER - + * + * This header precedes each security descriptor in the $SDS data stream. + * This is also the index entry data part of both the $SII and $SDH indexes. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; + +/** + * struct SDH_INDEX_DATA - + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ + u32 reserved_II; /* Padding - always unicode "II" or zero. This field + isn't counted in INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) SDH_INDEX_DATA; + +/** + * struct SII_INDEX_DATA - + */ +typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; + +/** + * struct SDS_ENTRY - + * + * The $SDS data stream contains the security descriptors, aligned on 16-byte + * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot + * cross 256kib boundaries (this restriction is imposed by the Windows cache + * manager). Each security descriptor is contained in a SDS_ENTRY structure. + * Also, each security descriptor is stored twice in the $SDS stream with a + * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size) + * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the + * the first copy of the security descriptor will be at offset 0x51d0 in the + * $SDS data stream and the second copy will be at offset 0x451d0. + */ +typedef struct { +/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like + unnamed structs. */ + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security + descriptor. */ +} __attribute__((__packed__)) SDS_ENTRY; + +/** + * struct SII_INDEX_KEY - The index entry key used in the $SII index. + * + * The collation type is COLLATION_NTOFS_ULONG. + */ +typedef struct { + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SII_INDEX_KEY; + +/** + * struct SDH_INDEX_KEY - The index entry key used in the $SDH index. + * + * The keys are sorted first by hash and then by security_id. + * The collation rule is COLLATION_NTOFS_SECURITY_HASH. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SDH_INDEX_KEY; + +/** + * struct VOLUME_NAME - Attribute: Volume name (0x60). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + */ +typedef struct { + ntfschar name[0]; /* The name of the volume in Unicode. */ +} __attribute__((__packed__)) VOLUME_NAME; + +/** + * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). + */ +typedef enum { + VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001), + VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002), + VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004), + VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008), + VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010), + VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020), + VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000), + VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000), + VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f), +} __attribute__((__packed__)) VOLUME_FLAGS; + +/** + * struct VOLUME_INFORMATION - Attribute: Volume information (0x70). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses + * NTFS 1.2. I haven't personally seen other values yet. + */ +typedef struct { + u64 reserved; /* Not used (yet?). */ + u8 major_ver; /* Major version of the ntfs format. */ + u8 minor_ver; /* Minor version of the ntfs format. */ + VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ +} __attribute__((__packed__)) VOLUME_INFORMATION; + +/** + * struct DATA_ATTR - Attribute: Data attribute (0x80). + * + * NOTE: Can be resident or non-resident. + * + * Data contents of a file (i.e. the unnamed stream) or of a named stream. + */ +typedef struct { + u8 data[0]; /* The file's data contents. */ +} __attribute__((__packed__)) DATA_ATTR; + +/** + * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). + */ +typedef enum { + /* When index header is in an index root attribute: */ + SMALL_INDEX = 0, /* The index is small enough to fit inside the + index root attribute and there is no index + allocation attribute present. */ + LARGE_INDEX = 1, /* The index is too large to fit in the index + root attribute and/or an index allocation + attribute is present. */ + /* + * When index header is in an index block, i.e. is part of index + * allocation attribute: + */ + LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more + nodes branching off it. */ + INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a + leaf node. */ + NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ +} __attribute__((__packed__)) INDEX_HEADER_FLAGS; + +/** + * struct INDEX_HEADER - + * + * This is the header for indexes, describing the INDEX_ENTRY records, which + * follow the INDEX_HEADER. Together the index header and the index entries + * make up a complete index. + * + * IMPORTANT NOTE: The offset, length and size structure members are counted + * relative to the start of the index header structure and not relative to the + * start of the index root or index allocation structures themselves. + */ +typedef struct { +/* 0*/ u32 entries_offset; /* Byte offset from the INDEX_HEADER to first + INDEX_ENTRY, aligned to 8-byte boundary. */ +/* 4*/ u32 index_length; /* Data size in byte of the INDEX_ENTRY's, + including the INDEX_HEADER, aligned to 8. */ +/* 8*/ u32 allocated_size; /* Allocated byte size of this index (block), + multiple of 8 bytes. See more below. */ + /* + For the index root attribute, the above two numbers are always + equal, as the attribute is resident and it is resized as needed. + + For the index allocation attribute, the attribute is not resident + and the allocated_size is equal to the index_block_size specified + by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK + size not counting the INDEX_HEADER part (i.e. minus -24). + */ +/* 12*/ INDEX_HEADER_FLAGS ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary.*/ +/* sizeof() == 16 */ +} __attribute__((__packed__)) INDEX_HEADER; + +/** + * struct INDEX_ROOT - Attribute: Index root (0x90). + * + * NOTE: Always resident. + * + * This is followed by a sequence of index entries (INDEX_ENTRY structures) + * as described by the index header. + * + * When a directory is small enough to fit inside the index root then this + * is the only attribute describing the directory. When the directory is too + * large to fit in the index root, on the other hand, two additional attributes + * are present: an index allocation attribute, containing sub-nodes of the B+ + * directory tree (see below), and a bitmap attribute, describing which virtual + * cluster numbers (vcns) in the index allocation attribute are in use by an + * index block. + * + * NOTE: The root directory (FILE_root) contains an entry for itself. Other + * directories do not contain entries for themselves, though. + */ +typedef struct { +/* 0*/ ATTR_TYPES type; /* Type of the indexed attribute. Is + $FILE_NAME for directories, zero + for view indexes. No other values + allowed. */ +/* 4*/ COLLATION_RULES collation_rule; /* Collation rule used to sort the + index entries. If type is $FILE_NAME, + this must be COLLATION_FILE_NAME. */ +/* 8*/ u32 index_block_size; /* Size of index block in bytes (in + the index allocation attribute). */ +/* 12*/ s8 clusters_per_index_block; /* Size of index block in clusters (in + the index allocation attribute), when + an index block is >= than a cluster, + otherwise sectors per index block. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ +/* 16*/ INDEX_HEADER index; /* Index header describing the + following index entries. */ +/* sizeof()= 32 bytes */ +} __attribute__((__packed__)) INDEX_ROOT; + +/** + * struct INDEX_BLOCK - Attribute: Index allocation (0xa0). + * + * NOTE: Always non-resident (doesn't make sense to be resident anyway!). + * + * This is an array of index blocks. Each index block starts with an + * INDEX_BLOCK structure containing an index header, followed by a sequence of + * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER. + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ + u16 usa_ofs; /* See NTFS_RECORD definition. */ + u16 usa_count; /* See NTFS_RECORD definition. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number of the last + modification of this index block. */ +/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ +/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ +/* sizeof()= 40 (0x28) bytes */ +/* + * When creating the index block, we place the update sequence array at this + * offset, i.e. before we start with the index entries. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading use the data from the ntfs record header. + */ +} __attribute__((__packed__)) INDEX_BLOCK; + +typedef INDEX_BLOCK INDEX_ALLOCATION; + +/** + * struct REPARSE_INDEX_KEY - + * + * The system file FILE_Extend/$Reparse contains an index named $R listing + * all reparse points on the volume. The index entry keys are as defined + * below. Note, that there is no index data associated with the index entries. + * + * The index entries are sorted by the index key file_id. The collation rule is + * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the + * primary key / is not a key at all. (AIA) + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + MFT_REF file_id; /* Mft record of the file containing the + reparse point attribute. */ +} __attribute__((__packed__)) REPARSE_INDEX_KEY; + +/** + * enum QUOTA_FLAGS - Quota flags (32-bit). + */ +typedef enum { + /* The user quota flags. Names explain meaning. */ + QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001), + QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002), + QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004), + + QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007), + /* Bit mask for user quota flags. */ + + /* These flags are only present in the quota defaults index entry, + i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ + QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010), + QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020), + QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040), + QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080), + QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100), + QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200), + QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400), + QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800), +} QUOTA_FLAGS; + +/** + * struct QUOTA_CONTROL_ENTRY - + * + * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas + * are on a per volume and per user basis. + * + * The $Q index contains one entry for each existing user_id on the volume. The + * index key is the user_id of the user/group owning this quota control entry, + * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the + * owner_id, is found in the standard information attribute. The collation rule + * for $Q is COLLATION_NTOFS_ULONG. + * + * The $O index contains one entry for each user/group who has been assigned + * a quota on that volume. The index key holds the SID of the user_id the + * entry belongs to, i.e. the owner_id. The collation rule for $O is + * COLLATION_NTOFS_SID. + * + * The $O index entry data is the user_id of the user corresponding to the SID. + * This user_id is used as an index into $Q to find the quota control entry + * associated with the SID. + * + * The $Q index entry data is the quota control entry and is defined below. + */ +typedef struct { + u32 version; /* Currently equals 2. */ + QUOTA_FLAGS flags; /* Flags describing this quota entry. */ + u64 bytes_used; /* How many bytes of the quota are in use. */ + s64 change_time; /* Last time this quota entry was changed. */ + s64 threshold; /* Soft quota (-1 if not limited). */ + s64 limit; /* Hard quota (-1 if not limited). */ + s64 exceeded_time; /* How long the soft quota has been exceeded. */ +/* The below field is NOT present for the quota defaults entry. */ + SID sid; /* The SID of the user/object associated with + this quota entry. If this field is missing + then the INDEX_ENTRY is padded with zeros + to multiply of 8 which are not counted in + the data_length field. If the sid is present + then this structure is padded with zeros to + multiply of 8 and the padding is counted in + the INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; + +/** + * struct QUOTA_O_INDEX_DATA - + */ +typedef struct { + u32 owner_id; + u32 unknown; /* Always 32. Seems to be padding and it's not + counted in the INDEX_ENTRY's data_length. + This field shouldn't be really here. */ +} __attribute__((__packed__)) QUOTA_O_INDEX_DATA; + +/** + * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit). + */ +typedef enum { + QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000), + QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001), + QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100), +} PREDEFINED_OWNER_IDS; + +/** + * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit). + */ +typedef enum { + INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a + sub-node, i.e. a reference to an index + block in form of a virtual cluster + number (see below). */ + INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last + entry in an index block. The index + entry does not represent a file but it + can point to a sub-node. */ + INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ +} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; + +/** + * struct INDEX_ENTRY_HEADER - This the index entry header (see below). + * + * ========================================================== + * !!!!! SEE DESCRIPTION OF THE FIELDS AT INDEX_ENTRY !!!!! + * ========================================================== + */ +typedef struct { +/* 0*/ union { + MFT_REF indexed_file; + struct { + u16 data_offset; + u16 data_length; + u32 reservedV; + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; +/* 10*/ u16 key_length; +/* 12*/ INDEX_ENTRY_FLAGS flags; +/* 14*/ u16 reserved; +/* sizeof() = 16 bytes */ +} __attribute__((__packed__)) INDEX_ENTRY_HEADER; + +/** + * struct INDEX_ENTRY - This is an index entry. + * + * A sequence of such entries follows each INDEX_HEADER structure. Together + * they make up a complete index. The index follows either an index root + * attribute or an index allocation attribute. + * + * NOTE: Before NTFS 3.0 only filename attributes were indexed. + */ +typedef struct { +/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ + union { /* Only valid when INDEX_ENTRY_END is not set. */ + MFT_REF indexed_file; /* The mft reference of the file + described by this index + entry. Used for directory + indexes. */ + struct { /* Used for views/indexes to find the entry's data. */ + u16 data_offset; /* Data byte offset from this + INDEX_ENTRY. Follows the + index key. */ + u16 data_length; /* Data length in bytes. */ + u32 reservedV; /* Reserved (zero). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; /* Byte size of this index entry, multiple of + 8-bytes. Size includes INDEX_ENTRY_HEADER + and the optional subnode VCN. See below. */ +/* 10*/ u16 key_length; /* Byte size of the key value, which is in the + index entry. It follows field reserved. Not + multiple of 8-bytes. */ +/* 12*/ INDEX_ENTRY_FLAGS ie_flags; /* Bit field of INDEX_ENTRY_* flags. */ +/* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ +/* End of INDEX_ENTRY_HEADER */ +/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present + if INDEX_ENTRY_END bit in flags is not set. NOTE: On + NTFS versions before 3.0 the only valid key is the + FILE_NAME_ATTR. On NTFS 3.0+ the following + additional index keys are defined: */ + FILE_NAME_ATTR file_name;/* $I30 index in directories. */ + SII_INDEX_KEY sii; /* $SII index in $Secure. */ + SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ + GUID object_id; /* $O index in FILE_Extend/$ObjId: The + object_id of the mft record found in + the data part of the index. */ + REPARSE_INDEX_KEY reparse; /* $R index in + FILE_Extend/$Reparse. */ + SID sid; /* $O index in FILE_Extend/$Quota: + SID of the owner of the user_id. */ + u32 owner_id; /* $Q index in FILE_Extend/$Quota: + user_id of the owner of the quota + control entry in the data part of + the index. */ + } __attribute__((__packed__)) key; + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ +} __attribute__((__packed__)) INDEX_ENTRY; + +/** + * struct BITMAP_ATTR - Attribute: Bitmap (0xb0). + * + * Contains an array of bits (aka a bitfield). + * + * When used in conjunction with the index allocation attribute, each bit + * corresponds to one index block within the index allocation attribute. Thus + * the number of bits in the bitmap * index block size / cluster size is the + * number of clusters in the index allocation attribute. + */ +typedef struct { + u8 bitmap[0]; /* Array of bits. */ +} __attribute__((__packed__)) BITMAP_ATTR; + +/** + * enum PREDEFINED_REPARSE_TAGS - + * + * The reparse point tag defines the type of the reparse point. It also + * includes several flags, which further describe the reparse point. + * + * The reparse point tag is an unsigned 32-bit value divided in three parts: + * + * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of + * the reparse point. + * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. + * 3. The most significant three bits are flags describing the reparse point. + * They are defined as follows: + * bit 29: Name surrogate bit. If set, the filename is an alias for + * another object in the system. + * bit 30: High-latency bit. If set, accessing the first byte of data will + * be slow. (E.g. the data is stored on a tape drive.) + * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User + * defined tags have to use zero here. + */ +typedef enum { + IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), + IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), + IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), + + IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000), + IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), + IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), + + IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005), + IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006), + IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007), + IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008), + + IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0x88000003), + + IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004), + + IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000), + + IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff), +} PREDEFINED_REPARSE_TAGS; + +/** + * struct REPARSE_POINT - Attribute: Reparse point (0xc0). + * + * NOTE: Can be resident or non-resident. + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + u16 reparse_data_length; /* Byte size of reparse data. */ + u16 reserved; /* Align to 8-byte boundary. */ + u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ +} __attribute__((__packed__)) REPARSE_POINT; + +/** + * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). + * + * NOTE: Always resident. + */ +typedef struct { + u16 ea_length; /* Byte size of the packed extended + attributes. */ + u16 need_ea_count; /* The number of extended attributes which have + the NEED_EA bit set. */ + u32 ea_query_length; /* Byte size of the buffer required to query + the extended attributes when calling + ZwQueryEaFile() in Windows NT/2k. I.e. the + byte size of the unpacked extended + attributes. */ +} __attribute__((__packed__)) EA_INFORMATION; + +/** + * enum EA_FLAGS - Extended attribute flags (8-bit). + */ +typedef enum { + NEED_EA = 0x80, /* Indicate that the file to which the EA + belongs cannot be interpreted without + understanding the associated extended + attributes. */ +} __attribute__((__packed__)) EA_FLAGS; + +/** + * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0). + * + * Like the attribute list and the index buffer list, the EA attribute value is + * a sequence of EA_ATTR variable length records. + * + * FIXME: It appears weird that the EA name is not Unicode. Is it true? + * FIXME: It seems that name is always uppercased. Is it true? + */ +typedef struct { + u32 next_entry_offset; /* Offset to the next EA_ATTR. */ + EA_FLAGS flags; /* Flags describing the EA. */ + u8 name_length; /* Length of the name of the extended + attribute in bytes. */ + u16 value_length; /* Byte size of the EA's value. */ + u8 name[0]; /* Name of the EA. */ + u8 value[0]; /* The value of the EA. Immediately + follows the name. */ +} __attribute__((__packed__)) EA_ATTR; + +/** + * struct PROPERTY_SET - Attribute: Property set (0xf0). + * + * Intended to support Native Structure Storage (NSS) - a feature removed from + * NTFS 3.0 during beta testing. + */ +typedef struct { + /* Irrelevant as feature unused. */ +} __attribute__((__packed__)) PROPERTY_SET; + +/** + * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100). + * + * NOTE: Can be resident or non-resident. + * + * Operations on this attribute are logged to the journal ($LogFile) like + * normal metadata changes. + * + * Used by the Encrypting File System (EFS). All encrypted files have this + * attribute with the name $EFS. See below for the relevant structures. + */ +typedef struct { + /* Can be anything the creator chooses. */ +} __attribute__((__packed__)) LOGGED_UTILITY_STREAM; + +/* + * $EFS Data Structure: + * + * The following information is about the data structures that are contained + * inside a logged utility stream (0x100) with a name of "$EFS". + * + * The stream starts with an instance of EFS_ATTR_HEADER. + * + * Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of + * them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence + * of multiple data decryption/recovery fields. + * + * Each data decryption/recovery field starts with a EFS_DF_HEADER and the next + * one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to + * the offset of the beginning of the current EFS_DF_HEADER. + * + * The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a + * SID, an optional GUID, an optional container name, a non-optional user name, + * and the encrypted FEK. + * + * Note all the below are best guesses so may have mistakes/inaccuracies. + * Corrections/clarifications/additions are always welcome! + * + * Ntfs.sys takes an EFS value length of <= 0x54 or > 0x40000 to BSOD, i.e. it + * is invalid. + */ + +/** + * struct EFS_ATTR_HEADER - "$EFS" header. + * + * The header of the Logged utility stream (0x100) attribute named "$EFS". + */ +typedef struct { +/* 0*/ u32 length; /* Length of EFS attribute in bytes. */ + u32 state; /* Always 0? */ + u32 version; /* Efs version. Always 2? */ + u32 crypto_api_version; /* Always 0? */ +/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is + created with a call to UuidCreate() so is + unlikely to be an MD5 hash and is more + likely to be GUID of this encrytped file + or something like that. */ +/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ +/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ +/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data + decryption fields (DDF), see below. Zero if + no DDFs are present. */ + u32 offset_to_drf_array;/* Offset in bytes to the array of data + recovery fields (DRF), see below. Zero if + no DRFs are present. */ + u32 reserved; /* Reserved. */ +} __attribute__((__packed__)) EFS_ATTR_HEADER; + +/** + * struct EFS_DF_ARRAY_HEADER - + */ +typedef struct { + u32 df_count; /* Number of data decryption/recovery fields in + the array. */ +} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; + +/** + * struct EFS_DF_HEADER - + */ +typedef struct { +/* 0*/ u32 df_length; /* Length of this data decryption/recovery + field in bytes. */ + u32 cred_header_offset; /* Offset in bytes to the credential header. */ + u32 fek_size; /* Size in bytes of the encrypted file + encryption key (FEK). */ + u32 fek_offset; /* Offset in bytes to the FEK from the start of + the data decryption/recovery field. */ +/* 16*/ u32 unknown1; /* always 0? Might be just padding. */ +} __attribute__((__packed__)) EFS_DF_HEADER; + +/** + * struct EFS_DF_CREDENTIAL_HEADER - + */ +typedef struct { +/* 0*/ u32 cred_length; /* Length of this credential in bytes. */ + u32 sid_offset; /* Offset in bytes to the user's sid from start + of this structure. Zero if no sid is + present. */ +/* 8*/ u32 type; /* Type of this credential: + 1 = CryptoAPI container. + 2 = Unexpected type. + 3 = Certificate thumbprint. + other = Unknown type. */ + union { + /* CryptoAPI container. */ + struct { +/* 12*/ u32 container_name_offset; /* Offset in bytes to + the name of the container from start of this + structure (may not be zero). */ +/* 16*/ u32 provider_name_offset; /* Offset in bytes to + the name of the provider from start of this + structure (may not be zero). */ + u32 public_key_blob_offset; /* Offset in bytes to + the public key blob from start of this + structure. */ +/* 24*/ u32 public_key_blob_size; /* Size in bytes of + public key blob. */ + } __attribute__((__packed__)); + /* Certificate thumbprint. */ + struct { +/* 12*/ u32 cert_thumbprint_header_size; /* Size in + bytes of the header of the certificate + thumbprint. */ +/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in + bytes to the header of the certificate + thumbprint from start of this structure. */ + u32 unknown1; /* Always 0? Might be padding... */ + u32 unknown2; /* Always 0? Might be padding... */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; + +typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; + +/** + * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - + */ +typedef struct { +/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ + u32 thumbprint_size; /* Size of thumbprint in bytes. */ +/* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the + container from start of this + structure or 0 if no name present. */ + u32 provider_name_offset; /* Offset in bytes to the name of the + cryptographic provider from start of + this structure or 0 if no name + present. */ +/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name + from start of this structure or 0 if + no user name present. (This is also + known as lpDisplayInformation.) */ +} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; + +typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; + +typedef enum { + INTX_SYMBOLIC_LINK = + const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */ + INTX_CHARACTER_DEVICE = + const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */ + INTX_BLOCK_DEVICE = + const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */ +} INTX_FILE_TYPES; + +typedef struct { + INTX_FILE_TYPES magic; /* Intx file magic. */ + union { + /* For character and block devices. */ + struct { + u64 major; /* Major device number. */ + u64 minor; /* Minor device number. */ + void *device_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + /* For symbolic links. */ + ntfschar target[0]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) INTX_FILE; + +#endif /* defined _NTFS_LAYOUT_H */ diff --git a/lib/libntfs/orig/source/lcnalloc.c b/lib/libntfs/orig/source/lcnalloc.c new file mode 100644 index 0000000..e84d243 --- /dev/null +++ b/lib/libntfs/orig/source/lcnalloc.c @@ -0,0 +1,771 @@ +/** + * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "runlist.h" +#include "volume.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +/* + * Plenty possibilities for big optimizations all over in the cluster + * allocation, however at the moment the dominant bottleneck (~ 90%) is + * the update of the mapping pairs which converges to the cubic Faulhaber's + * formula as the function of the number of extents (fragments, runs). + */ +#define NTFS_LCNALLOC_BSIZE 4096 +#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE + +enum { + ZONE_MFT = 1, + ZONE_DATA1 = 2, + ZONE_DATA2 = 4 +} ; + +static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) +{ + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); + + if (tc >= end) + *pos = start; + else if (tc >= start) + *pos = tc; +} + +static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) +{ + ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); + + if (zone == ZONE_MFT) + ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, + &vol->mft_zone_pos, tc); + else if (zone == ZONE_DATA1) + ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, + &vol->data1_zone_pos, tc); + else /* zone == ZONE_DATA2 */ + ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, + &vol->data2_zone_pos, tc); +} + +/* + * Unmark full zones when a cluster has been freed in a full zone + * + * Next allocation will reuse the freed cluster + */ + +static void update_full_status(ntfs_volume *vol, LCN lcn) +{ + if (lcn >= vol->mft_zone_end) { + if (vol->full_zones & ZONE_DATA1) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn); + vol->full_zones &= ~ZONE_DATA1; + } + } else + if (lcn < vol->mft_zone_start) { + if (vol->full_zones & ZONE_DATA2) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn); + vol->full_zones &= ~ZONE_DATA2; + } + } else { + if (vol->full_zones & ZONE_MFT) { + ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn); + vol->full_zones &= ~ZONE_MFT; + } + } +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_log_trace("Entering\n"); + + i = 0; + while (i < size) { + switch (*buf) { + case 0 : + do { + buf++; + run += 8; + i++; + } while ((i < size) && !*buf); + break; + case 255 : + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 - run; + } + run = 0; + do { + buf++; + i++; + } while ((i < size) && (*buf == 255)); + break; + default : + for (j = 0; j < 8; j++) { + + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 + (j - run); + } + run = 0; + } else + run++; + } + i++; + buf++; + + } + } + + if (run > max_range) + start_pos = (s64)i * 8 - run; + + return start_pos; +} + +static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, + u8 *writeback) +{ + s64 written; + + ntfs_log_trace("Entering\n"); + + if (!*writeback) + return 0; + + *writeback = 0; + + written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); + if (written != size) { + if (!written) + errno = EIO; + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); + return -1; + } + + return 0; +} + +/** + * ntfs_cluster_alloc - allocate clusters on an ntfs volume + * @vol: mounted ntfs volume on which to allocate the clusters + * @start_vcn: vcn to use for the first allocated cluster + * @count: number of clusters to allocate + * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) + * @zone: zone from which to allocate the clusters + * + * Allocate @count clusters preferably starting at cluster @start_lcn or at the + * current allocator position if @start_lcn is -1, on the mounted ntfs volume + * @vol. @zone is either DATA_ZONE for allocation of normal clusters and + * MFT_ZONE for allocation of clusters for the master file table, i.e. the + * $MFT/$DATA attribute. + * + * On success return a runlist describing the allocated cluster(s). + * + * On error return NULL with errno set to the error code. + * + * Notes on the allocation algorithm + * ================================= + * + * There are two data zones. First is the area between the end of the mft zone + * and the end of the volume, and second is the area between the start of the + * volume and the start of the mft zone. On unmodified/standard NTFS 1.x + * volumes, the second data zone doesn't exist due to the mft zone being + * expanded to cover the start of the volume in order to reserve space for the + * mft bitmap attribute. + * + * The complexity stems from the need of implementing the mft vs data zoned + * approach and from the fact that we have access to the lcn bitmap via up to + * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over + * boundaries of two buffers. Further, the fact that the allocator allows for + * caller supplied hints as to the location of where allocation should begin + * and the fact that the allocator keeps track of where in the data zones the + * next natural allocation should occur, contribute to the complexity of the + * function. But it should all be worthwhile, because this allocator: + * 1) implements MFT zone reservation + * 2) causes reduction in fragmentation. + * The code is not optimized for speed. + */ +runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) +{ + LCN zone_start, zone_end; /* current search range */ + LCN last_read_pos, lcn; + LCN bmp_pos; /* current bit position inside the bitmap */ + LCN prev_lcn = 0, prev_run_len = 0; + s64 clusters, br; + runlist *rl = NULL, *trl; + u8 *buf, *byte, bit, writeback; + u8 pass = 1; /* 1: inside zone; 2: start of zone */ + u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ + u8 done_zones = 0; + u8 has_guess, used_zone_pos; + int err = 0, rlpos, rlsize, buf_size; + + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " + "zone = %s_ZONE.\n", (long long)count, (long long) + start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || + (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { + errno = EINVAL; + ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", + __FUNCTION__, (long long)start_vcn, + (long long)count, (long long)start_lcn); + goto out; + } + + /* Return empty runlist if @count == 0 */ + if (!count) { + rl = ntfs_malloc(0x1000); + if (rl) { + rl[0].vcn = start_vcn; + rl[0].lcn = LCN_RL_NOT_MAPPED; + rl[0].length = 0; + } + goto out; + } + + buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); + if (!buf) + goto out; + /* + * If no @start_lcn was requested, use the current zone + * position otherwise use the requested @start_lcn. + */ + has_guess = 1; + zone_start = start_lcn; + + if (zone_start < 0) { + if (zone == DATA_ZONE) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->mft_zone_pos; + has_guess = 0; + } + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = ZONE_DATA2; + } else if (zone_start < vol->mft_zone_end) { + zone_end = vol->mft_zone_end; + search_zone = ZONE_MFT; + } else { + zone_end = vol->nr_clusters; + search_zone = ZONE_DATA1; + } + + bmp_pos = zone_start; + + /* Loop until all clusters are allocated. */ + clusters = count; + rlpos = rlsize = 0; + while (1) { + /* check whether we have exhausted the current zone */ + if (search_zone & vol->full_zones) + goto zone_pass_done; + last_read_pos = bmp_pos >> 3; + br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, + NTFS_LCNALLOC_BSIZE, buf); + if (br <= 0) { + if (!br) + goto zone_pass_done; + err = errno; + ntfs_log_perror("Reading $BITMAP failed"); + goto err_ret; + } + /* + * We might have read less than NTFS_LCNALLOC_BSIZE bytes + * if we are close to the end of the attribute. + */ + buf_size = (int)br << 3; + lcn = bmp_pos & 7; + bmp_pos &= ~7; + writeback = 0; + + while (lcn < buf_size) { + byte = buf + (lcn >> 3); + bit = 1 << (lcn & 7); + if (has_guess) { + if (*byte & bit) { + has_guess = 0; + break; + } + } else { + lcn = max_empty_bit_range(buf, br); + if (lcn < 0) + break; + has_guess = 1; + continue; + } + + /* First free bit is at lcn + bmp_pos. */ + + /* Reallocate memory if necessary. */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; + trl = realloc(rl, rlsize); + if (!trl) { + err = ENOMEM; + ntfs_log_perror("realloc() failed"); + goto wb_err_ret; + } + rl = trl; + } + + /* Allocate the bitmap bit. */ + *byte |= bit; + writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", + (long long)vol->free_clusters); + else + vol->free_clusters--; + + /* + * Coalesce with previous run if adjacent LCNs. + * Otherwise, append a new run. + */ + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); + rl[rlpos - 1].length = ++prev_run_len; + } else { + if (rlpos) + rl[rlpos].vcn = rl[rlpos - 1].vcn + + prev_run_len; + else { + rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + (long long)start_vcn); + } + + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; + rl[rlpos].length = prev_run_len = 1; + rlpos++; + } + + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); + /* Done? */ + if (!--clusters) { + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, lcn + bmp_pos + 1 + + NTFS_LCNALLOC_SKIP); + goto done_ret; + } + + lcn++; + } + + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } + + if (!used_zone_pos) { + + used_zone_pos = 1; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_pos; + else if (search_zone == ZONE_DATA1) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else + bmp_pos += buf_size; + + if (bmp_pos < zone_end) + continue; + +zone_pass_done: + ntfs_log_trace("Finished current zone pass(%i).\n", pass); + if (pass == 1) { + pass = 2; + zone_end = zone_start; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_start; + else if (search_zone == ZONE_DATA1) + zone_start = vol->mft_zone_end; + else + zone_start = 0; + + /* Sanity check. */ + if (zone_end < zone_start) + zone_end = zone_start; + + bmp_pos = zone_start; + + continue; + } + /* pass == 2 */ +done_zones_check: + done_zones |= search_zone; + vol->full_zones |= search_zone; + if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) { + ntfs_log_trace("Switching zone.\n"); + pass = 1; + if (rlpos) { + LCN tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; + + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, tc); + } + + switch (search_zone) { + case ZONE_MFT: + ntfs_log_trace("Zone switch: mft -> data1\n"); +switch_to_data1_zone: search_zone = ZONE_DATA1; + zone_start = vol->data1_zone_pos; + zone_end = vol->nr_clusters; + if (zone_start == vol->mft_zone_end) + pass = 2; + break; + case ZONE_DATA1: + ntfs_log_trace("Zone switch: data1 -> data2\n"); + search_zone = ZONE_DATA2; + zone_start = vol->data2_zone_pos; + zone_end = vol->mft_zone_start; + if (!zone_start) + pass = 2; + break; + case ZONE_DATA2: + if (!(done_zones & ZONE_DATA1)) { + ntfs_log_trace("data2 -> data1\n"); + goto switch_to_data1_zone; + } + ntfs_log_trace("Zone switch: data2 -> mft\n"); + search_zone = ZONE_MFT; + zone_start = vol->mft_zone_pos; + zone_end = vol->mft_zone_end; + if (zone_start == vol->mft_zone_start) + pass = 2; + break; + } + + bmp_pos = zone_start; + + if (zone_start == zone_end) { + ntfs_log_trace("Empty zone, skipped.\n"); + goto done_zones_check; + } + + continue; + } + + ntfs_log_trace("All zones are finished, no space on device.\n"); + err = ENOSPC; + goto err_ret; + } +done_ret: + ntfs_log_debug("At done_ret.\n"); + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } +done_err_ret: + free(buf); + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; + +wb_err_ret: + ntfs_log_trace("At wb_err_ret.\n"); + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) + err = errno; +err_ret: + ntfs_log_trace("At err_ret.\n"); + if (rl) { + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + rl = NULL; + } + goto done_err_ret; +} + +/** + * ntfs_cluster_free_from_rl - free clusters from runlist + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist from which deallocate clusters + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + + for (; rl->length; rl++) { + + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)rl->lcn, (long long)rl->length); + + if (rl->lcn >= 0) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); + goto out; + } + nr_freed += rl->length ; + } + } + + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/* + * Basic cluster run free + * Returns 0 if successful + */ + +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); + + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/** + * ntfs_cluster_free - free clusters on an ntfs volume + * @vol: mounted ntfs volume on which to free the clusters + * @na: attribute whose runlist describes the clusters to free + * @start_vcn: vcn in @rl at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters + * + * Free @count clusters starting at the cluster @start_vcn in the runlist + * described by the attribute @na from the mounted ntfs volume @vol. + * + * If @count is -1, all clusters from @start_vcn to the end of the runlist + * are deallocated. + * + * On success return the number of deallocated clusters (not counting sparse + * clusters) and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) +{ + runlist *rl; + s64 delta, to_free, nr_freed = 0; + int ret = -1; + + if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || + (count < 0 && count != -1)) { + ntfs_log_trace("Invalid arguments!\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)count, (long long)start_vcn); + + rl = ntfs_attr_find_vcn(na, start_vcn); + if (!rl) { + if (errno == ENOENT) + ret = 0; + goto leave; + } + + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); + goto leave; + } + + /* Find the starting cluster inside the run that needs freeing. */ + delta = start_vcn - rl->vcn; + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length - delta; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + /* Do the actual freeing of the clusters in this run. */ + update_full_status(vol,rl->lcn + delta); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, + to_free)) + goto leave; + nr_freed = to_free; + } + + /* Go to the next run and adjust the number of clusters left to free. */ + ++rl; + if (count >= 0) + count -= to_free; + + /* + * Loop over the remaining runs, using @count as a capping value, and + * free them. + */ + for (; rl->length && count != 0; ++rl) { + // FIXME: Need to try ntfs_attr_map_runlist() for attribute + // list support! (AIA) + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + // FIXME: Eeek! We need rollback! (AIA) + errno = EIO; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; + } + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + to_free)) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; + } + nr_freed += to_free; + } + + if (count >= 0) + count -= to_free; + } + + if (count != -1 && count != 0) { + // FIXME: Eeek! BUG() + errno = EIO; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; + } + + ret = nr_freed; +out: + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); + return ret; +} diff --git a/lib/libntfs/orig/source/lcnalloc.h b/lib/libntfs/orig/source/lcnalloc.h new file mode 100644 index 0000000..cbf4c5c --- /dev/null +++ b/lib/libntfs/orig/source/lcnalloc.h @@ -0,0 +1,51 @@ +/* + * lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LCNALLOC_H +#define _NTFS_LCNALLOC_H + +#include "types.h" +#include "runlist.h" +#include "volume.h" + +/** + * enum NTFS_CLUSTER_ALLOCATION_ZONES - + */ +typedef enum { + FIRST_ZONE = 0, /* For sanity checking. */ + MFT_ZONE = 0, /* Allocate from $MFT zone. */ + DATA_ZONE = 1, /* Allocate from $DATA zone. */ + LAST_ZONE = 1, /* For sanity checking. */ +} NTFS_CLUSTER_ALLOCATION_ZONES; + +extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); + +extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); + +extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, + s64 count); + +#endif /* defined _NTFS_LCNALLOC_H */ + diff --git a/lib/libntfs/orig/source/logfile.c b/lib/libntfs/orig/source/logfile.c new file mode 100644 index 0000000..0f8bce9 --- /dev/null +++ b/lib/libntfs/orig/source/logfile.c @@ -0,0 +1,738 @@ +/** + * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "logfile.h" +#include "volume.h" +#include "mst.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_check_restart_page_header - check the page header for consistency + * @rp: restart page header to check + * @pos: position in logfile at which the restart page header resides + * + * Check the restart page header @rp for consistency and return TRUE if it is + * consistent and FALSE otherwise. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) +{ + u32 logfile_system_page_size, logfile_log_page_size; + u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; + BOOL have_usa = TRUE; + + ntfs_log_trace("Entering.\n"); + /* + * If the system or log page sizes are smaller than the ntfs block size + * or either is not a power of 2 we cannot handle this log file. + */ + logfile_system_page_size = le32_to_cpu(rp->system_page_size); + logfile_log_page_size = le32_to_cpu(rp->log_page_size); + if (logfile_system_page_size < NTFS_BLOCK_SIZE || + logfile_log_page_size < NTFS_BLOCK_SIZE || + logfile_system_page_size & + (logfile_system_page_size - 1) || + logfile_log_page_size & (logfile_log_page_size - 1)) { + ntfs_log_error("$LogFile uses unsupported page size.\n"); + return FALSE; + } + /* + * We must be either at !pos (1st restart page) or at pos = system page + * size (2nd restart page). + */ + if (pos && pos != logfile_system_page_size) { + ntfs_log_error("Found restart area in incorrect " + "position in $LogFile.\n"); + return FALSE; + } + /* We only know how to handle version 1.1. */ + if (sle16_to_cpu(rp->major_ver) != 1 || + sle16_to_cpu(rp->minor_ver) != 1) { + ntfs_log_error("$LogFile version %i.%i is not " + "supported. (This driver supports version " + "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), + (int)sle16_to_cpu(rp->minor_ver)); + return FALSE; + } + /* + * If chkdsk has been run the restart page may not be protected by an + * update sequence array. + */ + if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { + have_usa = FALSE; + goto skip_usa_checks; + } + /* Verify the size of the update sequence array. */ + usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); + if (usa_count != le16_to_cpu(rp->usa_count)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array count.\n"); + return FALSE; + } + /* Verify the position of the update sequence array. */ + usa_ofs = le16_to_cpu(rp->usa_ofs); + usa_end = usa_ofs + usa_count * sizeof(u16); + if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || + usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array offset.\n"); + return FALSE; + } +skip_usa_checks: + /* + * Verify the position of the restart area. It must be: + * - aligned to 8-byte boundary, + * - after the update sequence array, and + * - within the system page size. + */ + ra_ofs = le16_to_cpu(rp->restart_area_offset); + if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : + ra_ofs < sizeof(RESTART_PAGE_HEADER)) || + ra_ofs > logfile_system_page_size) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent restart area offset.\n"); + return FALSE; + } + /* + * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn + * set. + */ + if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { + ntfs_log_error("$LogFile restart page is not modified " + "by chkdsk but a chkdsk LSN is specified.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_restart_area - check the restart area for consistency + * @rp: restart page whose restart area to check + * + * Check the restart area of the restart page @rp for consistency and return + * TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header has already been + * consistency checked. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) +{ + u64 file_size; + RESTART_AREA *ra; + u16 ra_ofs, ra_len, ca_ofs; + u8 fs_bits; + + ntfs_log_trace("Entering.\n"); + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (RESTART_AREA*)((u8*)rp + ra_ofs); + /* + * Everything before ra->file_size must be before the first word + * protected by an update sequence number. This ensures that it is + * safe to access ra->client_array_offset. + */ + if (ra_ofs + offsetof(RESTART_AREA, file_size) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent file offset.\n"); + return FALSE; + } + /* + * Now that we can access ra->client_array_offset, make sure everything + * up to the log client array is before the first word protected by an + * update sequence number. This ensures we can access all of the + * restart area elements safely. Also, the client array offset must be + * aligned to an 8-byte boundary. + */ + ca_ofs = le16_to_cpu(ra->client_array_offset); + if (((ca_ofs + 7) & ~7) != ca_ofs || + ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - + sizeof(u16))) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent client array offset.\n"); + return FALSE; + } + /* + * The restart area must end within the system page size both when + * calculated manually and as specified by ra->restart_area_length. + * Also, the calculated length must not exceed the specified length. + */ + ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * + sizeof(LOG_CLIENT_RECORD); + if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || + (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > + le32_to_cpu(rp->system_page_size) || + ra_len > le16_to_cpu(ra->restart_area_length)) { + ntfs_log_error("$LogFile restart area is out of bounds " + "of the system page size specified by the " + "restart page header and/or the specified " + "restart area length is inconsistent.\n"); + return FALSE; + } + /* + * The ra->client_free_list and ra->client_in_use_list must be either + * LOGFILE_NO_CLIENT or less than ra->log_clients or they are + * overflowing the client array. + */ + if ((ra->client_free_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_free_list) >= + le16_to_cpu(ra->log_clients)) || + (ra->client_in_use_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_in_use_list) >= + le16_to_cpu(ra->log_clients))) { + ntfs_log_error("$LogFile restart area specifies " + "overflowing client free and/or in use lists.\n"); + return FALSE; + } + /* + * Check ra->seq_number_bits against ra->file_size for consistency. + * We cannot just use ffs() because the file size is not a power of 2. + */ + file_size = (u64)sle64_to_cpu(ra->file_size); + fs_bits = 0; + while (file_size) { + file_size >>= 1; + fs_bits++; + } + if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent sequence number bits.\n"); + return FALSE; + } + /* The log record header length must be a multiple of 8. */ + if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != + le16_to_cpu(ra->log_record_header_length)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log record header length.\n"); + return FALSE; + } + /* Ditto for the log page data offset. */ + if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != + le16_to_cpu(ra->log_page_data_offset)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log page data offset.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_log_client_array - check the log client array for consistency + * @rp: restart page whose log client array to check + * + * Check the log client array of the restart page @rp for consistency and + * return TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header and the restart area have + * already been consistency checked. + * + * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this + * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full + * restart page and the page must be multi sector transfer deprotected. + */ +static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + LOG_CLIENT_RECORD *ca, *cr; + u16 nr_clients, idx; + BOOL in_free_list, idx_is_first; + + ntfs_log_trace("Entering.\n"); + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + ca = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + /* + * Check the ra->client_free_list first and then check the + * ra->client_in_use_list. Check each of the log client records in + * each of the lists and check that the array does not overflow the + * ra->log_clients value. Also keep track of the number of records + * visited as there cannot be more than ra->log_clients records and + * that way we detect eventual loops in within a list. + */ + nr_clients = le16_to_cpu(ra->log_clients); + idx = le16_to_cpu(ra->client_free_list); + in_free_list = TRUE; +check_list: + for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, + idx = le16_to_cpu(cr->next_client)) { + if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) + goto err_out; + /* Set @cr to the current log client record. */ + cr = ca + idx; + /* The first log client record must not have a prev_client. */ + if (idx_is_first) { + if (cr->prev_client != LOGFILE_NO_CLIENT) + goto err_out; + idx_is_first = FALSE; + } + } + /* Switch to and check the in use list if we just did the free list. */ + if (in_free_list) { + in_free_list = FALSE; + idx = le16_to_cpu(ra->client_in_use_list); + goto check_list; + } + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + ntfs_log_error("$LogFile log client array is corrupt.\n"); + return FALSE; +} + +/** + * ntfs_check_and_load_restart_page - check the restart page for consistency + * @log_na: opened ntfs attribute for journal $LogFile + * @rp: restart page to check + * @pos: position in @log_na at which the restart page resides + * @wrp: [OUT] copy of the multi sector transfer deprotected restart page + * @lsn: [OUT] set to the current logfile lsn on success + * + * Check the restart page @rp for consistency and return 0 if it is consistent + * and errno otherwise. The restart page may have been modified by chkdsk in + * which case its magic is CHKD instead of RSTR. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + * + * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a + * copy of the complete multi sector transfer deprotected page. On failure, + * *@wrp is undefined. + * + * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current + * logfile lsn according to this restart page. On failure, *@lsn is undefined. + * + * The following error codes are defined: + * EINVAL - The restart page is inconsistent. + * ENOMEM - Not enough memory to load the restart page. + * EIO - Failed to reading from $LogFile. + */ +static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, + RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, + LSN *lsn) +{ + RESTART_AREA *ra; + RESTART_PAGE_HEADER *trp; + int err; + + ntfs_log_trace("Entering.\n"); + /* Check the restart page header for consistency. */ + if (!ntfs_check_restart_page_header(rp, pos)) { + /* Error output already done inside the function. */ + return EINVAL; + } + /* Check the restart area for consistency. */ + if (!ntfs_check_restart_area(rp)) { + /* Error output already done inside the function. */ + return EINVAL; + } + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * Allocate a buffer to store the whole restart page so we can multi + * sector transfer deprotect it. + */ + trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); + if (!trp) + return errno; + /* + * Read the whole of the restart page into the buffer. If it fits + * completely inside @rp, just copy it from there. Otherwise read it + * from disk. + */ + if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) + memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); + else if (ntfs_attr_pread(log_na, pos, + le32_to_cpu(rp->system_page_size), trp) != + le32_to_cpu(rp->system_page_size)) { + err = errno; + ntfs_log_error("Failed to read whole restart page into the " + "buffer.\n"); + if (err != ENOMEM) + err = EIO; + goto err_out; + } + /* + * Perform the multi sector transfer deprotection on the buffer if the + * restart page is protected. + */ + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) + && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, + le32_to_cpu(rp->system_page_size))) { + /* + * A multi sector tranfer error was detected. We only need to + * abort if the restart page contents exceed the multi sector + * transfer fixup of the first sector. + */ + if (le16_to_cpu(rp->restart_area_offset) + + le16_to_cpu(ra->restart_area_length) > + NTFS_BLOCK_SIZE - (int)sizeof(u16)) { + ntfs_log_error("Multi sector transfer error " + "detected in $LogFile restart page.\n"); + err = EINVAL; + goto err_out; + } + } + /* + * If the restart page is modified by chkdsk or there are no active + * logfile clients, the logfile is consistent. Otherwise, need to + * check the log client records for consistency, too. + */ + err = 0; + if (ntfs_is_rstr_record(rp->magic) && + ra->client_in_use_list != LOGFILE_NO_CLIENT) { + if (!ntfs_check_log_client_array(trp)) { + err = EINVAL; + goto err_out; + } + } + if (lsn) { + if (ntfs_is_rstr_record(rp->magic)) + *lsn = sle64_to_cpu(ra->current_lsn); + else /* if (ntfs_is_chkd_record(rp->magic)) */ + *lsn = sle64_to_cpu(rp->chkdsk_lsn); + } + ntfs_log_trace("Done.\n"); + if (wrp) + *wrp = trp; + else { +err_out: + free(trp); + } + return err; +} + +/** + * ntfs_check_logfile - check in the journal if the volume is consistent + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: [OUT] on success this is a copy of the current restart page + * + * Check the $LogFile journal for consistency and return TRUE if it is + * consistent and FALSE if not. On success, the current restart page is + * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. + * + * At present we only check the two restart pages and ignore the log record + * pages. + * + * Note that the MstProtected flag is not set on the $LogFile inode and hence + * when reading pages they are not deprotected. This is because we do not know + * if the $LogFile was created on a system with a different page size to ours + * yet and mst deprotection would fail if our page size is smaller. + */ +BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) +{ + s64 size, pos; + LSN rstr1_lsn, rstr2_lsn; + ntfs_volume *vol = log_na->ni->vol; + u8 *kaddr = NULL; + RESTART_PAGE_HEADER *rstr1_ph = NULL; + RESTART_PAGE_HEADER *rstr2_ph = NULL; + int log_page_size, log_page_mask, err; + BOOL logfile_is_empty = TRUE; + u8 log_page_bits; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(vol)) + goto is_empty; + size = log_na->data_size; + /* Make sure the file doesn't exceed the maximum allowed size. */ + if (size > (s64)MaxLogFileSize) + size = MaxLogFileSize; + log_page_size = DefaultLogPageSize; + log_page_mask = log_page_size - 1; + /* + * Use generic_ffs() instead of ffs() to enable the compiler to + * optimize log_page_size and log_page_bits into constants. + */ + log_page_bits = ffs(log_page_size) - 1; + size &= ~(log_page_size - 1); + + /* + * Ensure the log file is big enough to store at least the two restart + * pages and the minimum number of log record pages. + */ + if (size < log_page_size * 2 || (size - log_page_size * 2) >> + log_page_bits < MinLogRecordPages) { + ntfs_log_error("$LogFile is too small.\n"); + return FALSE; + } + /* Allocate memory for restart page. */ + kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); + if (!kaddr) + return FALSE; + /* + * Read through the file looking for a restart page. Since the restart + * page header is at the beginning of a page we only need to search at + * what could be the beginning of a page (for each page size) rather + * than scanning the whole file byte by byte. If all potential places + * contain empty and uninitialized records, the log file can be assumed + * to be empty. + */ + for (pos = 0; pos < size; pos <<= 1) { + /* + * Read first NTFS_BLOCK_SIZE bytes of potential restart page. + */ + if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != + NTFS_BLOCK_SIZE) { + ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " + "bytes of potential restart page.\n"); + goto err_out; + } + + /* + * A non-empty block means the logfile is not empty while an + * empty block after a non-empty block has been encountered + * means we are done. + */ + if (!ntfs_is_empty_recordp((le32*)kaddr)) + logfile_is_empty = FALSE; + else if (!logfile_is_empty) + break; + /* + * A log record page means there cannot be a restart page after + * this so no need to continue searching. + */ + if (ntfs_is_rcrd_recordp((le32*)kaddr)) + break; + /* If not a (modified by chkdsk) restart page, continue. */ + if (!ntfs_is_rstr_recordp((le32*)kaddr) && + !ntfs_is_chkd_recordp((le32*)kaddr)) { + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * Check the (modified by chkdsk) restart page for consistency + * and get a copy of the complete multi sector transfer + * deprotected restart page. + */ + err = ntfs_check_and_load_restart_page(log_na, + (RESTART_PAGE_HEADER*)kaddr, pos, + !rstr1_ph ? &rstr1_ph : &rstr2_ph, + !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); + if (!err) { + /* + * If we have now found the first (modified by chkdsk) + * restart page, continue looking for the second one. + */ + if (!pos) { + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * We have now found the second (modified by chkdsk) + * restart page, so we can stop looking. + */ + break; + } + /* + * Error output already done inside the function. Note, we do + * not abort if the restart page was invalid as we might still + * find a valid one further in the file. + */ + if (err != EINVAL) + goto err_out; + /* Continue looking. */ + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + } + if (kaddr) { + free(kaddr); + kaddr = NULL; + } + if (logfile_is_empty) { + NVolSetLogFileEmpty(vol); +is_empty: + ntfs_log_trace("Done. ($LogFile is empty.)\n"); + return TRUE; + } + if (!rstr1_ph) { + if (rstr2_ph) + ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); + ntfs_log_error("Did not find any restart pages in " + "$LogFile and it was not empty.\n"); + return FALSE; + } + /* If both restart pages were found, use the more recent one. */ + if (rstr2_ph) { + /* + * If the second restart area is more recent, switch to it. + * Otherwise just throw it away. + */ + if (rstr2_lsn > rstr1_lsn) { + ntfs_log_debug("Using second restart page as it is more " + "recent.\n"); + free(rstr1_ph); + rstr1_ph = rstr2_ph; + /* rstr1_lsn = rstr2_lsn; */ + } else { + ntfs_log_debug("Using first restart page as it is more " + "recent.\n"); + free(rstr2_ph); + } + rstr2_ph = NULL; + } + /* All consistency checks passed. */ + if (rp) + *rp = rstr1_ph; + else + free(rstr1_ph); + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + free(kaddr); + free(rstr1_ph); + free(rstr2_ph); + return FALSE; +} + +/** + * ntfs_is_logfile_clean - check in the journal if the volume is clean + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: copy of the current restart page + * + * Analyze the $LogFile journal and return TRUE if it indicates the volume was + * shutdown cleanly and FALSE if not. + * + * At present we only look at the two restart pages and ignore the log record + * pages. This is a little bit crude in that there will be a very small number + * of cases where we think that a volume is dirty when in fact it is clean. + * This should only affect volumes that have not been shutdown cleanly but did + * not have any pending, non-check-pointed i/o, i.e. they were completely idle + * at least for the five seconds preceding the unclean shutdown. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and in particular if the $LogFile + * is empty this function requires that NVolLogFileEmpty() is true otherwise an + * empty volume will be reported as dirty. + */ +BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(log_na->ni->vol)) { + ntfs_log_trace("$LogFile is empty\n"); + return TRUE; + } + if (!rp) { + ntfs_log_error("Restart page header is NULL\n"); + return FALSE; + } + if (!ntfs_is_rstr_record(rp->magic) && + !ntfs_is_chkd_record(rp->magic)) { + ntfs_log_error("Restart page buffer is invalid\n"); + return FALSE; + } + + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * If the $LogFile has active clients, i.e. it is open, and we do not + * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, + * we assume there was an unclean shutdown. + */ + if (ra->client_in_use_list != LOGFILE_NO_CLIENT && + !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->flags)); + return FALSE; + } + /* $LogFile indicates a clean shutdown. */ + ntfs_log_trace("$LogFile indicates a clean shutdown\n"); + return TRUE; +} + +/** + * ntfs_empty_logfile - empty the contents of the $LogFile journal + * @na: ntfs attribute of journal $LogFile to empty + * + * Empty the contents of the $LogFile journal @na and return 0 on success and + * -1 on error. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() + * has been used to ensure that the $LogFile is clean. + */ +int ntfs_empty_logfile(ntfs_attr *na) +{ + s64 pos, count; + char buf[NTFS_BUF_SIZE]; + + ntfs_log_trace("Entering.\n"); + + if (NVolLogFileEmpty(na->ni->vol)) + return 0; + + if (!NAttrNonResident(na)) { + errno = EIO; + ntfs_log_perror("Resident $LogFile $DATA attribute"); + return -1; + } + + memset(buf, -1, NTFS_BUF_SIZE); + + pos = 0; + while ((count = na->data_size - pos) > 0) { + + if (count > NTFS_BUF_SIZE) + count = NTFS_BUF_SIZE; + + count = ntfs_attr_pwrite(na, pos, count, buf); + if (count <= 0) { + ntfs_log_perror("Failed to reset $LogFile"); + if (count != -1) + errno = EIO; + return -1; + } + pos += count; + if(pos>=64*1024) break; //only clear first 64kb + } + + NVolSetLogFileEmpty(na->ni->vol); + + return 0; +} diff --git a/lib/libntfs/orig/source/logfile.h b/lib/libntfs/orig/source/logfile.h new file mode 100644 index 0000000..798d562 --- /dev/null +++ b/lib/libntfs/orig/source/logfile.h @@ -0,0 +1,394 @@ +/* + * logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LOGFILE_H +#define _NTFS_LOGFILE_H + +#include "types.h" +#include "endians.h" +#include "layout.h" + +/* + * Journal ($LogFile) organization: + * + * Two restart areas present in the first two pages (restart pages, one restart + * area in each page). When the volume is dismounted they should be identical, + * except for the update sequence array which usually has a different update + * sequence number. + * + * These are followed by log records organized in pages headed by a log record + * header going up to log file size. Not all pages contain log records when a + * volume is first formatted, but as the volume ages, all records will be used. + * When the log file fills up, the records at the beginning are purged (by + * modifying the oldest_lsn to a higher value presumably) and writing begins + * at the beginning of the file. Effectively, the log file is viewed as a + * circular entity. + * + * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept + * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We + * probably only want to support 1.1 as this seems to be the current version + * and we don't know how that differs from the older versions. The only + * exception is if the journal is clean as marked by the two restart pages + * then it doesn't matter whether we are on an earlier version. We can just + * reinitialize the logfile and start again with version 1.1. + */ + +/* Some $LogFile related constants. */ +#define MaxLogFileSize 0x100000000ULL +#define DefaultLogPageSize 4096 +#define MinLogRecordPages 48 + +/** + * struct RESTART_PAGE_HEADER - Log file restart page header. + * + * Begins the restart area. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ +/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ +/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ +/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + +/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by + chkdsk. Only used when the magic is changed + to "CHKD". Otherwise this is zero. */ +/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file + was created, has to be >= 512 and a power of + 2. Use this to calculate the required size + of the usa (usa_count) and add it to usa_ofs. + Then verify that the result is less than the + value of the restart_area_offset. */ +/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= + 512 and a power of 2. The default is 4096 + and is used when the system page size is + between 4096 and 8192. Otherwise this is + set to the system page size instead. */ +/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to + the RESTART_AREA. Value has to be aligned + to 8-byte boundary. When creating, set this + to be after the usa. */ +/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major + version is 1. */ +/* 28*/ sle16 major_ver; /* Log file major version. We only support + version 1.1. */ +/* sizeof() = 30 (0x1e) bytes */ +} __attribute__((__packed__)) RESTART_PAGE_HEADER; + +/* + * Constant for the log client indices meaning that there are no client records + * in this particular client array. Also inside the client records themselves, + * this means that there are no client records preceding or following this one. + */ +#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) +#define LOGFILE_NO_CLIENT_CPU 0xffff + +/* + * These are the so far known RESTART_AREA_* flags (16-bit) which contain + * information about the log file in which they are present. + */ +enum { + RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ +} __attribute__((__packed__)); + +typedef le16 RESTART_AREA_FLAGS; + +/** + * struct RESTART_AREA - Log file restart area record. + * + * The offset of this record is found by adding the offset of the + * RESTART_PAGE_HEADER to the restart_area_offset value found in it. + * See notes at restart_area_offset above. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log + when the restart area was last written. + This happens often but what is the interval? + Is it just fixed time or is it every time a + check point is written or something else? + On create set to 0. */ +/* 8*/ le16 log_clients; /* Number of log client records in the array of + log client records which follows this + restart area. Must be 1. */ +/* 10*/ le16 client_free_list; /* The index of the first free log client record + in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + free log client records in the array. + If != LOGFILE_NO_CLIENT, check that + log_clients > client_free_list. On Win2k + and presumably earlier, on a clean volume + this is != LOGFILE_NO_CLIENT, and it should + be 0, i.e. the first (and only) client + record is free and thus the logfile is + closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be LOGFILE_NO_CLIENT. On WinXP + and presumably later, the logfile is always + open, even on clean shutdown so this should + always be LOGFILE_NO_CLIENT. */ +/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client + record in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + in-use log client records in the array. If + != LOGFILE_NO_CLIENT check that log_clients + > client_in_use_list. On Win2k and + presumably earlier, on a clean volume this + is LOGFILE_NO_CLIENT, i.e. there are no + client records in use and thus the logfile + is closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be != LOGFILE_NO_CLIENT, and it + should be 0, i.e. the first (and only) + client record is in use. On WinXP and + presumably later, the logfile is always + open, even on clean shutdown so this should + always be 0. */ +/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k + and presumably earlier this is always 0. On + WinXP and presumably later, if the logfile + was shutdown cleanly, the second bit, + RESTART_VOLUME_IS_CLEAN, is set. This bit + is cleared when the volume is mounted by + WinXP and set when the volume is dismounted, + thus if the logfile is dirty, this bit is + clear. Thus we don't need to check the + Windows version to determine if the logfile + is clean. Instead if the logfile is closed, + we know it must be clean. If it is open and + this bit is set, we also know it must be + clean. If on the other hand the logfile is + open and this bit is clear, we can be almost + certain that the logfile is dirty. */ +/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence + number. This is calculated as 67 - the + number of bits required to store the logfile + size in bytes and this can be used in with + the specified file_size as a consistency + check. */ +/* 20*/ le16 restart_area_length;/* Length of the restart area including the + client array. Following checks required if + version matches. Otherwise, skip them. + restart_area_offset + restart_area_length + has to be <= system_page_size. Also, + restart_area_length has to be >= + client_array_offset + (log_clients * + sizeof(log client record)). */ +/* 22*/ le16 client_array_offset;/* Offset from the start of this record to + the first log client record if versions are + matched. When creating, set this to be + after this restart area structure, aligned + to 8-bytes boundary. If the versions do not + match, this is ignored and the offset is + assumed to be (sizeof(RESTART_AREA) + 7) & + ~7, i.e. rounded up to first 8-byte + boundary. Either way, client_array_offset + has to be aligned to an 8-byte boundary. + Also, restart_area_offset + + client_array_offset has to be <= 510. + Finally, client_array_offset + (log_clients + * sizeof(log client record)) has to be <= + system_page_size. On Win2k and presumably + earlier, this is 0x30, i.e. immediately + following this record. On WinXP and + presumably later, this is 0x40, i.e. there + are 16 extra bytes between this record and + the client array. This probably means that + the RESTART_AREA record is actually bigger + in WinXP and later. */ +/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the + restart_area_offset + the offset of the + file_size are > 510 then corruption has + occurred. This is the very first check when + starting with the restart_area as if it + fails it means that some of the above values + will be corrupted by the multi sector + transfer protection. The file_size has to + be rounded down to be a multiple of the + log_page_size in the RESTART_PAGE_HEADER and + then it has to be at least big enough to + store the two restart pages and 48 (0x30) + log record pages. */ +/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including + the log record header. On create set to + 0. */ +/* 36*/ le16 log_record_header_length;/* Byte size of the log record header. + If the version matches then check that the + value of log_record_header_length is a + multiple of 8, i.e. + (log_record_header_length + 7) & ~7 == + log_record_header_length. When creating set + it to sizeof(LOG_RECORD_HEADER), aligned to + 8 bytes. */ +/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record + page. Must be a multiple of 8. On create + set it to immediately after the update + sequence array of the log record page. */ +/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every + time the logfile is restarted which happens + at mount time when the logfile is opened. + When creating set to a random value. Win2k + sets it to the low 32 bits of the current + system time in NTFS format (see time.h). */ +/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ +/* sizeof() = 48 (0x30) bytes */ +} __attribute__((__packed__)) RESTART_AREA; + +/** + * struct LOG_CLIENT_RECORD - Log client record. + * + * The offset of this record is found by adding the offset of the + * RESTART_AREA to the client_array_offset value found in it. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create + set to 0. */ +/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart + the volume, i.e. the current position within + the log file. At present, if clean this + should = current_lsn in restart area but it + probably also = current_lsn when dirty most + of the time. At create set to 0. */ +/* 16*/ le16 prev_client; /* The offset to the previous log client record + in the array of log client records. + LOGFILE_NO_CLIENT means there is no previous + client record, i.e. this is the first one. + This is always LOGFILE_NO_CLIENT. */ +/* 18*/ le16 next_client; /* The offset to the next log client record in + the array of log client records. + LOGFILE_NO_CLIENT means there are no next + client records, i.e. this is the last one. + This is always LOGFILE_NO_CLIENT. */ +/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set + to zero every time the logfile is restarted + and it is incremented when the logfile is + closed at dismount time. Thus it is 0 when + dirty and 1 when clean. On WinXP and + presumably later, this is always 0. */ +/* 22*/ u8 reserved[6]; /* Reserved/alignment. */ +/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should + always be 8. */ +/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should + always be "NTFS" with the remaining bytes + set to 0. */ +/* sizeof() = 160 (0xa0) bytes */ +} __attribute__((__packed__)) LOG_CLIENT_RECORD; + +/** + * struct RECORD_PAGE_HEADER - Log page record page header. + * + * Each log page begins with this header and is followed by several LOG_RECORD + * structures, starting at offset 0x40 (the size of this structure and the + * following update sequence array and then aligned to 8 byte boundary, but is + * this specified anywhere?). + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ + u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ + u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + + union { + LSN last_lsn; + s64 file_offset; + } __attribute__((__packed__)) copy; + u32 flags; + u16 page_count; + u16 page_position; + union { + struct { + u16 next_record_offset; + u8 reserved[6]; + LSN last_end_lsn; + } __attribute__((__packed__)) packed; + } __attribute__((__packed__)) header; +} __attribute__((__packed__)) RECORD_PAGE_HEADER; + +/** + * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. + * + * (Or is it log record pages?) + */ +typedef enum { + LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, + /* This has nothing to do with the log record. It is only so + gcc knows to make the flags 16-bit. */ +} __attribute__((__packed__)) LOG_RECORD_FLAGS; + +/** + * struct LOG_CLIENT_ID - The log client id structure identifying a log client. + */ +typedef struct { + u16 seq_number; + u16 client_index; +} __attribute__((__packed__)) LOG_CLIENT_ID; + +/** + * struct LOG_RECORD - Log record header. + * + * Each log record seems to have a constant size of 0x70 bytes. + */ +typedef struct { + LSN this_lsn; + LSN client_previous_lsn; + LSN client_undo_next_lsn; + u32 client_data_length; + LOG_CLIENT_ID client_id; + u32 record_type; + u32 transaction_id; + u16 flags; + u16 reserved_or_alignment[3]; +/* Now are at ofs 0x30 into struct. */ + u16 redo_operation; + u16 undo_operation; + u16 redo_offset; + u16 redo_length; + u16 undo_offset; + u16 undo_length; + u16 target_attribute; + u16 lcns_to_follow; /* Number of lcn_list entries + following this entry. */ +/* Now at ofs 0x40. */ + u16 record_offset; + u16 attribute_offset; + u32 alignment_or_reserved; + VCN target_vcn; +/* Now at ofs 0x50. */ + struct { /* Only present if lcns_to_follow + is not 0. */ + LCN lcn; + } __attribute__((__packed__)) lcn_list[0]; +} __attribute__((__packed__)) LOG_RECORD; + +extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); +extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); +extern int ntfs_empty_logfile(ntfs_attr *na); + +#endif /* defined _NTFS_LOGFILE_H */ diff --git a/lib/libntfs/orig/source/logging.c b/lib/libntfs/orig/source/logging.c new file mode 100644 index 0000000..ccccbb8 --- /dev/null +++ b/lib/libntfs/orig/source/logging.c @@ -0,0 +1,637 @@ +/** + * logging.c - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include "logging.h" +#include "misc.h" + +#ifndef PATH_SEP +#define PATH_SEP '/' +#endif + +#ifdef DEBUG +static int tab; +#endif + +/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ +#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) +# define BROKEN_GCC_FORMAT_ATTRIBUTE +#else +# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) +#endif + +/** + * struct ntfs_logging - Control info for the logging system + * @levels: Bitfield of logging levels + * @flags: Flags which affect the output style + * @handler: Function to perform the actual logging + */ +struct ntfs_logging { + u32 levels; + u32 flags; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; +}; + +/** + * ntfs_log + * This struct controls all the logging within the library and tools. + */ +static struct ntfs_logging ntfs_log = { +#ifdef DEBUG + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | +#endif + NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | + NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | + NTFS_LOG_LEVEL_PROGRESS, + NTFS_LOG_FLAG_ONLYNAME, +#ifdef DEBUG + ntfs_log_handler_outerr +#else + ntfs_log_handler_null +#endif +}; + + +/** + * ntfs_log_get_levels - Get a list of the current logging levels + * + * Find out which logging levels are enabled. + * + * Returns: Log levels in a 32-bit field + */ +u32 ntfs_log_get_levels(void) +{ + return ntfs_log.levels; +} + +/** + * ntfs_log_set_levels - Enable extra logging levels + * @levels: 32-bit field of log levels to set + * + * Enable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_set_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels |= levels; + return old; +} + +/** + * ntfs_log_clear_levels - Disable some logging levels + * @levels: 32-bit field of log levels to clear + * + * Disable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_clear_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels &= (~levels); + return old; +} + + +/** + * ntfs_log_get_flags - Get a list of logging style flags + * + * Find out which logging flags are enabled. + * + * Returns: Logging flags in a 32-bit field + */ +u32 ntfs_log_get_flags(void) +{ + return ntfs_log.flags; +} + +/** + * ntfs_log_set_flags - Enable extra logging style flags + * @flags: 32-bit field of logging flags to set + * + * Enable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_set_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags |= flags; + return old; +} + +/** + * ntfs_log_clear_flags - Disable some logging styles + * @flags: 32-bit field of logging flags to clear + * + * Disable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_clear_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags &= (~flags); + return old; +} + + +/** + * ntfs_log_get_stream - Default output streams for logging levels + * @level: Log level + * + * By default, urgent messages are sent to "stderr". + * Other messages are sent to "stdout". + * + * Returns: "string" Prefix to be used + */ +static FILE * ntfs_log_get_stream(u32 level) +{ + FILE *stream; + + switch (level) { + case NTFS_LOG_LEVEL_INFO: + case NTFS_LOG_LEVEL_QUIET: + case NTFS_LOG_LEVEL_PROGRESS: + case NTFS_LOG_LEVEL_VERBOSE: + stream = stdout; + break; + + case NTFS_LOG_LEVEL_DEBUG: + case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: + case NTFS_LOG_LEVEL_WARNING: + case NTFS_LOG_LEVEL_ERROR: + case NTFS_LOG_LEVEL_CRITICAL: + case NTFS_LOG_LEVEL_PERROR: + default: + stream = stderr; + break; + } + + return stream; +} + +/** + * ntfs_log_get_prefix - Default prefixes for logging levels + * @level: Log level to be prefixed + * + * Prefixing the logging output can make it easier to parse. + * + * Returns: "string" Prefix to be used + */ +static const char * ntfs_log_get_prefix(u32 level) +{ + const char *prefix; + + switch (level) { + case NTFS_LOG_LEVEL_DEBUG: + prefix = "DEBUG: "; + break; + case NTFS_LOG_LEVEL_TRACE: + prefix = "TRACE: "; + break; + case NTFS_LOG_LEVEL_QUIET: + prefix = "QUIET: "; + break; + case NTFS_LOG_LEVEL_INFO: + prefix = "INFO: "; + break; + case NTFS_LOG_LEVEL_VERBOSE: + prefix = "VERBOSE: "; + break; + case NTFS_LOG_LEVEL_PROGRESS: + prefix = "PROGRESS: "; + break; + case NTFS_LOG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case NTFS_LOG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_PERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL: "; + break; + default: + prefix = ""; + break; + } + + return prefix; +} + + +/** + * ntfs_log_set_handler - Provide an alternate logging handler + * @handler: function to perform the logging + * + * This alternate handler will be called for all future logging requests. + * If no @handler is specified, logging will revert to the default handler. + */ +void ntfs_log_set_handler(ntfs_log_handler *handler) +{ + if (handler) { + ntfs_log.handler = handler; +#ifdef HAVE_SYSLOG_H + if (handler == ntfs_log_handler_syslog) + openlog("ntfs-3g", LOG_PID, LOG_USER); +#endif + } else + ntfs_log.handler = ntfs_log_handler_null; +} + +/** + * ntfs_log_redirect - Pass on the request to the real handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @...: Arguments to be formatted + * + * This is just a redirector function. The arguments are simply passed to the + * main logging handler (as defined in the global logging struct @ntfs_log). + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_redirect(const char *function, const char *file, + int line, u32 level, void *data, const char *format, ...) +{ + int olderr = errno; + int ret; + va_list args; + + if (!(ntfs_log.levels & level)) /* Don't log this message */ + return 0; + + va_start(args, format); + errno = olderr; + ret = ntfs_log.handler(function, file, line, level, data, format, args); + va_end(args); + + errno = olderr; + return ret; +} + + +/** + * ntfs_log_handler_syslog - syslog logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple syslog logging handler. Ignores colors. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ + + +#ifdef HAVE_SYSLOG_H + +#define LOG_LINE_LEN 512 + +int ntfs_log_handler_syslog(const char *function __attribute__((unused)), + const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), + const char *format, va_list args) +{ + char logbuf[LOG_LINE_LEN]; + int ret, olderr = errno; + +#ifndef DEBUG + if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) + return 1; +#endif + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); + if (ret < 0) { + vsyslog(LOG_NOTICE, format, args); + ret = 1; + goto out; + } + + if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); + } + + syslog(LOG_NOTICE, "%s", logbuf); +out: + errno = olderr; + return ret; +} +#endif + +/* + * Early logging before the logs are redirected + * + * (not quite satisfactory : this appears before the ntfs-g banner, + * and with a different pid) + */ + +void ntfs_log_early_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); +#ifdef HAVE_SYSLOG_H + openlog("ntfs-3g", LOG_PID, LOG_USER); + ntfs_log_handler_syslog(NULL, NULL, 0, + NTFS_LOG_LEVEL_ERROR, NULL, + format, args); +#else + vfprintf(stderr,format,args); +#endif + va_end(args); +} + +/** + * ntfs_log_handler_fprintf - Basic logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple logging handler. This is where the log line is finally displayed. + * It is more likely that you will want to set the handler to either + * ntfs_log_handler_outerr or ntfs_log_handler_stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, nothing will be displayed. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_fprintf(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ +#ifdef DEBUG + int i; +#endif + int ret = 0; + int olderr = errno; + FILE *stream; + + if (!data) /* Interpret data as a FILE stream. */ + return 0; /* If it's NULL, we can't do anything. */ + stream = (FILE*)data; + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; + } + + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif + if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && + (strchr(file, PATH_SEP))) /* Abbreviate the filename */ + file = strrchr(file, PATH_SEP) + 1; + + if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ + ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); + + if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ + ret += fprintf(stream, "%s ", file); + + if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ + ret += fprintf(stream, "(%d) ", line); + + if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) + ret += fprintf(stream, "%s(): ", function); + + ret += vfprintf(stream, format, args); + + if (level & NTFS_LOG_LEVEL_PERROR) + ret += fprintf(stream, ": %s\n", strerror(olderr)); + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif + fflush(stream); + errno = olderr; + return ret; +} + +/** + * ntfs_log_handler_null - Null logging handler (no output) + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * This handler produces no output. It provides a way to temporarily disable + * logging, without having to change the levels and flags. + * + * Returns: 0 Message wasn't logged + */ +int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), + const char *format __attribute__((unused)), va_list args __attribute__((unused))) +{ + return 0; +} + +/** + * ntfs_log_handler_stdout - All logs go to stdout + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stdout. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stdout(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stdout; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message. The output stream will be determined by the log + * level. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, the function ntfs_log_get_stream will be called + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_outerr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = ntfs_log_get_stream(level); + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_stderr - All logs go to stderr + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stderr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stderr; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + + +/** + * ntfs_log_parse_option - Act upon command line options + * @option: Option flag + * + * Delegate some of the work of parsing the command line. All the options begin + * with "--log-". Options cause log levels to be enabled in @ntfs_log (the + * global logging structure). + * + * Note: The "colour" option changes the logging handler. + * + * Returns: TRUE Option understood + * FALSE Invalid log option + */ +BOOL ntfs_log_parse_option(const char *option) +{ + if (strcmp(option, "--log-debug") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); + return TRUE; + } else if (strcmp(option, "--log-verbose") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + return TRUE; + } else if (strcmp(option, "--log-quiet") == 0) { + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + return TRUE; + } else if (strcmp(option, "--log-trace") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); + return TRUE; + } + + ntfs_log_debug("Unknown logging option '%s'\n", option); + return FALSE; +} + diff --git a/lib/libntfs/orig/source/logging.h b/lib/libntfs/orig/source/logging.h new file mode 100644 index 0000000..82f39fe --- /dev/null +++ b/lib/libntfs/orig/source/logging.h @@ -0,0 +1,121 @@ +/* + * logging.h - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2007-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#include "types.h" + +/* Function prototype for the logging handlers */ +typedef int (ntfs_log_handler)(const char *function, const char *file, int line, + u32 level, void *data, const char *format, va_list args); + +/* Set the logging handler from one of the functions, below. */ +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); + +/* Logging handlers */ +ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0))); + +/* Enable/disable certain log levels */ +u32 ntfs_log_set_levels(u32 levels); +u32 ntfs_log_clear_levels(u32 levels); +u32 ntfs_log_get_levels(void); + +/* Enable/disable certain log flags */ +u32 ntfs_log_set_flags(u32 flags); +u32 ntfs_log_clear_flags(u32 flags); +u32 ntfs_log_get_flags(void); + +/* Turn command-line options into logging flags */ +BOOL ntfs_log_parse_option(const char *option); + +int ntfs_log_redirect(const char *function, const char *file, int line, + u32 level, void *data, const char *format, ...) + __attribute__((format(printf, 6, 7))); + +/* Logging levels - Determine what gets logged */ +#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ +#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ +#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ +#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ +#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ +#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ +#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ +#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ +#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ +#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ + +/* Logging style flags - Manage the style of the output */ +#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ +#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ +#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ +#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ +#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ + +/* Macros to simplify logging. One for each level defined above. + * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. + */ +#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) +#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) +#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) +#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) +#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) +#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) + +/* By default debug and trace messages are compiled into the program, + * but not displayed. + */ +#ifdef DEBUG +#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) +#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS) +#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS) +#else +#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) +#endif /* DEBUG */ + +void ntfs_log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +#endif /* _LOGGING_H_ */ + diff --git a/lib/libntfs/orig/source/mem_allocate.h b/lib/libntfs/orig/source/mem_allocate.h new file mode 100644 index 0000000..600e5a9 --- /dev/null +++ b/lib/libntfs/orig/source/mem_allocate.h @@ -0,0 +1,43 @@ +/** + * mem_allocate.h - Memory allocation and destruction calls. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +static inline void* ntfs_alloc (size_t size) { + return malloc(size); +} + +static inline void* ntfs_align (size_t size) { + #ifdef __wii__ + return memalign(32, size); + #else + return malloc(size); + #endif +} + +static inline void ntfs_free (void* mem) { + free(mem); +} + +#endif /* _MEM_ALLOCATE_H */ diff --git a/lib/libntfs/orig/source/mft.c b/lib/libntfs/orig/source/mft.c new file mode 100644 index 0000000..e93c664 --- /dev/null +++ b/lib/libntfs/orig/source/mft.c @@ -0,0 +1,1909 @@ +/** + * mft.c - Mft record handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#include + +#include "compat.h" +#include "types.h" +#include "device.h" +#include "debug.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "volume.h" +#include "layout.h" +#include "lcnalloc.h" +#include "mft.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_mft_records_read - read records from the mft from disk + * @vol: volume to read from + * @mref: starting mft record number to read + * @count: number of mft records to read + * @b: output data buffer + * + * Read @count mft records starting at @mref from volume @vol into buffer + * @b. Return 0 on success or -1 on error, with errno set to the error + * code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * The read mft records are mst deprotected and are hence ready to use. The + * caller should check each record with is_baad_record() in case mst + * deprotection failed. + * + * NOTE: @b has to be at least of size @count * vol->mft_record_size. + */ +int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 br; + VCN m; + + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); + + if (!vol || !vol->mft_na || !b || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); + return -1; + } + m = MREF(mref); + /* Refuse to read non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to read non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (br != count) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); + return -1; + } + return 0; +} + +/** + * ntfs_mft_records_write - write mft records to disk + * @vol: volume to write to + * @mref: starting mft record number to write + * @count: number of mft records to write + * @b: data buffer containing the mft records to write + * + * Write @count mft records starting at @mref from data buffer @b to volume + * @vol. Return 0 on success or -1 on error, with errno set to the error code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * Before the mft records are written, they are mst protected. After the write, + * they are deprotected again, thus resulting in an increase in the update + * sequence number inside the data buffer @b. + * + * If any mft records are written which are also represented in the mft mirror + * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a + * temporary buffer before we do the actual write. Then if at least one mft + * record was successfully written, we write the appropriate mft records from + * the copied buffer to the mft mirror, too. + */ +int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 bw; + VCN m; + void *bmirr = NULL; + int cnt = 0, res = 0; + + if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { + errno = EINVAL; + return -1; + } + m = MREF(mref); + /* Refuse to write non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to write non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + if (m < vol->mftmirr_size) { + if (!vol->mftmirr_na) { + errno = EINVAL; + return -1; + } + cnt = vol->mftmirr_size - m; + if (cnt > count) + cnt = count; + bmirr = ntfs_malloc(cnt * vol->mft_record_size); + if (!bmirr) + return -1; + memcpy(bmirr, b, cnt * vol->mft_record_size); + } + bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (bw != count) { + if (bw != -1) + errno = EIO; + if (bw >= 0) + ntfs_log_debug("Error: partial write while writing $Mft " + "record(s)!\n"); + else + ntfs_log_perror("Error writing $Mft record(s)"); + res = errno; + } + if (bmirr && bw > 0) { + if (bw < cnt) + cnt = bw; + bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, + m << vol->mft_record_size_bits, cnt, + vol->mft_record_size, bmirr); + if (bw != cnt) { + if (bw != -1) + errno = EIO; + ntfs_log_debug("Error: failed to sync $MFTMirr! Run " + "chkdsk.\n"); + res = errno; + } + } + free(bmirr); + if (!res) + return res; + errno = res; + return -1; +} + +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + +/** + * ntfs_file_record_read - read a FILE record from the mft from disk + * @vol: volume to read from + * @mref: mft reference specifying mft record to read + * @mrec: address of pointer in which to return the mft record + * @attr: address of pointer in which to return the first attribute + * + * Read a FILE record from the mft of @vol from the storage medium. @mref + * specifies the mft record to read, including the sequence number, which can + * be 0 if no sequence number checking is to be performed. + * + * The function allocates a buffer large enough to hold the mft record and + * reads the record into the buffer (mst deprotecting it in the process). + * *@mrec is then set to point to the buffer. + * + * If @attr is not NULL, *@attr is set to point to the first attribute in the + * mft record, i.e. *@attr is a pointer into *@mrec. + * + * Return 0 on success, or -1 on error, with errno set to the error code. + * + * The read mft record is checked for having the magic FILE, + * and for having a matching sequence number (if MSEQNO(*@mref) != 0). + * If either of these fails, -1 is returned and errno is set to EIO. If you get + * this, but you still want to read the mft record (e.g. in order to correct + * it), use ntfs_mft_record_read() directly. + * + * Note: Caller has to free *@mrec when finished. + * + * Note: We do not check if the mft record is flagged in use. The caller can + * check if desired. + */ +int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr) +{ + MFT_RECORD *m; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + + m = *mrec; + if (!m) { + m = ntfs_malloc(vol->mft_record_size); + if (!m) + return -1; + } + if (ntfs_mft_record_read(vol, mref, m)) + goto err_out; + + if (ntfs_mft_record_check(vol, mref, m)) + goto err_out; + + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); + errno = EIO; + goto err_out; + } + *mrec = m; + if (attr) + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + return 0; +err_out: + if (m != *mrec) + free(m); + return -1; +} + +/** + * ntfs_mft_record_layout - layout an mft record into a memory buffer + * @vol: volume to which the mft record will belong + * @mref: mft reference specifying the mft record number + * @mrec: destination buffer of size >= @vol->mft_record_size bytes + * + * Layout an empty, unused mft record with the mft reference @mref into the + * buffer @m. The volume @vol is needed because the mft record structure was + * modified in NTFS 3.1 so we need to know which volume version this mft record + * will be used on. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec) +{ + ATTR_RECORD *a; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); + else { + /* Abort if mref is > 32 bits. */ + if (MREF(mref) & 0x0000ffff00000000ull) { + errno = ERANGE; + ntfs_log_perror("Mft reference exceeds 32 bits"); + return -1; + } + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + mrec->reserved = cpu_to_le16(0); + mrec->mft_record_number = cpu_to_le32(MREF(mref)); + } + mrec->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_BLOCK_SIZE) + mrec->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_BLOCK_SIZE + 1); + else { + mrec->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than MFT record size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the file system created was corrupt. " + "Thank you.\n", NTFS_DEV_LIST); + } + /* Set the update sequence number to 1. */ + *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); + mrec->lsn = cpu_to_le64(0ull); + mrec->sequence_number = cpu_to_le16(1); + mrec->link_count = cpu_to_le16(0); + /* Aligned to 8-byte boundary. */ + mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + + (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); + mrec->flags = cpu_to_le16(0); + /* + * Using attrs_offset plus eight bytes (for the termination attribute), + * aligned to 8-byte boundary. + */ + mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + + 7) & ~7); + mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); + mrec->base_mft_record = cpu_to_le64((MFT_REF)0); + mrec->next_attr_instance = cpu_to_le16(0); + a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + a->type = AT_END; + a->length = cpu_to_le32(0); + /* Finally, clear the unused part of the mft record. */ + memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); + return 0; +} + +/** + * ntfs_mft_record_format - format an mft record on an ntfs volume + * @vol: volume on which to format the mft record + * @mref: mft reference specifying mft record to format + * + * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay + * out an empty, unused mft record in memory and write it to the volume @vol. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) +{ + MFT_RECORD *m; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + m = ntfs_calloc(vol->mft_record_size); + if (!m) + goto out; + + if (ntfs_mft_record_layout(vol, mref, m)) + goto free_m; + + if (ntfs_mft_record_write(vol, mref, m)) + goto free_m; + + ret = 0; +free_m: + free(m); +out: + ntfs_log_leave("\n"); + return ret; +} + +static const char *es = " Leaving inconsistent metadata. Run chkdsk."; + +/** + * ntfs_ffz - Find the first unset (zero) bit in a word + * @word: + * + * Description... + * + * Returns: + */ +static inline unsigned int ntfs_ffz(unsigned int word) +{ + return ffs(~word) - 1; +} + +static int ntfs_is_mft(ntfs_inode *ni) +{ + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; +} + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define RESERVED_MFT_RECORDS 64 + +/** + * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap + * @vol: volume on which to search for a free mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Search for a free mft record in the mft bitmap attribute on the ntfs volume + * @vol. + * + * If @base_ni is NULL start the search at the default allocator position. + * + * If @base_ni is not NULL start the search at the mft record after the base + * mft record @base_ni. + * + * Return the free mft record on success and -1 on error with errno set to the + * error code. An error code of ENOSPC means that there are no free mft + * records in the currently initialized mft bitmap. + */ +static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 pass_end, ll, data_pos, pass_start, ofs, bit; + ntfs_attr *mftbmp_na; + u8 *buf, *byte; + unsigned int size; + u8 pass, b; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + /* + * Set the end of the pass making sure we do not overflow the mft + * bitmap. + */ + size = PAGE_SIZE; + pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; + ll = mftbmp_na->initialized_size << 3; + if (pass_end > ll) + pass_end = ll; + pass = 1; + if (!base_ni) + data_pos = vol->mft_data_pos; + else + data_pos = base_ni->mft_no + 1; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; + if (data_pos >= pass_end) { + data_pos = RESERVED_MFT_RECORDS; + pass = 2; + /* This happens on a freshly formatted volume. */ + if (data_pos >= pass_end) { + errno = ENOSPC; + goto leave; + } + } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } + pass_start = data_pos; + buf = ntfs_malloc(PAGE_SIZE); + if (!buf) + goto leave; + + ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " + "pass_end 0x%llx, data_pos 0x%llx.\n", pass, + (long long)pass_start, (long long)pass_end, + (long long)data_pos); +#ifdef DEBUG + byte = NULL; + b = 0; +#endif + /* Loop until a free mft record is found. */ + for (; pass <= 2; size = PAGE_SIZE) { + /* Cap size to pass_end. */ + ofs = data_pos >> 3; + ll = ((pass_end + 7) >> 3) - ofs; + if (size > ll) + size = ll; + ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); + if (ll < 0) { + ntfs_log_perror("Failed to read $MFT bitmap"); + free(buf); + goto leave; + } + ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); + /* If we read at least one byte, search @buf for a zero bit. */ + if (ll) { + size = ll << 3; + bit = data_pos & 7; + data_pos &= ~7ull; + ntfs_log_debug("Before inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + for (; bit < size && data_pos + bit < pass_end; + bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + + /* Note: ffz() result must be zero based. */ + b = ntfs_ffz((unsigned long)*byte); + if (b < 8 && b >= (bit & 7)) { + free(buf); + ret = data_pos + (bit & ~7ull) + b; + goto leave; + } + } + ntfs_log_debug("After inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + data_pos += size; + /* + * If the end of the pass has not been reached yet, + * continue searching the mft bitmap for a zero bit. + */ + if (data_pos < pass_end) + continue; + } + /* Do the next pass. */ + pass++; + if (pass == 2) { + /* + * Starting the second pass, in which we scan the first + * part of the zone which we omitted earlier. + */ + pass_end = pass_start; + data_pos = pass_start = RESERVED_MFT_RECORDS; + ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " + "0x%llx.\n", pass, (long long)pass_start, + (long long)pass_end); + if (data_pos >= pass_end) + break; + } + } + /* No free mft records in currently initialized mft bitmap. */ +out: + free(buf); + errno = ENOSPC; +leave: + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_mft_attr_extend(ntfs_attr *na) +{ + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); + + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } + + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation + */ +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) +{ + LCN lcn; + s64 ll = 0; /* silence compiler warning */ + ntfs_attr *mftbmp_na; + runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + mftbmp_na = vol->mftbmp_na; + /* + * Determine the last lcn of the mft bitmap. The allocated size of the + * mft bitmap cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >> + vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft bitmap attribute.\n"); + if (rl) + errno = EIO; + return STATUS_ERROR; + } + lcn = rl->lcn + rl->length; + + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); + if (!rl2) { + ntfs_log_error("Failed to allocate a cluster for " + "the mft bitmap.\n"); + return STATUS_ERROR; + } + rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft " + "bitmap.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate " + "cluster.%s\n", es); + free(rl2); + errno = err; + return STATUS_ERROR; + } + mftbmp_na->rl = rl; + ntfs_log_debug("Adding one run to mft bitmap.\n"); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* + * Update the attribute record as well. Note: @rl is the last + * (non-terminator) runlist element of mft bitmap. + */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mftbmp_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft bitmap attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft bitmap attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, mp_size + + le16_to_cpu(a->mapping_pairs_offset))) { + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* Generate the mapping pairs array directly into the attr record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, + NULL)) { + ntfs_log_error("Failed to build mapping pairs array for " + "mft bitmap attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft bitmap allocated_size by one cluster. + * Reflect this in the ntfs_attr structure and the attribute record. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft bitmap attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mftbmp_na->allocated_size += vol->cluster_size; + a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return STATUS_OK; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mftbmp_na->allocated_size += vol->cluster_size; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + return STATUS_ERROR; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); + errno = err; +undo_alloc: + err = errno; + + /* Remove the last run from the runlist. */ + lcn = rl->lcn; + rl->lcn = rl[1].lcn; + rl->length = 0; + + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) + ntfs_log_error("Failed to free cluster.%s\n", es); + else + vol->free_clusters++; + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping " + "pairs array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; +} +/** + * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the initialized portion of the mft bitmap attribute on the ntfs + * volume @vol by 8 bytes. + * + * Note: Only changes initialized_size and data_size, i.e. requires that + * allocated_size is big enough to fit the new initialized_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) +{ + s64 old_data_size, old_initialized_size, ll; + ntfs_attr *mftbmp_na; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int err; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.\n"); + err = errno; + goto put_err_out; + } + a = ctx->attr; + old_data_size = mftbmp_na->data_size; + old_initialized_size = mftbmp_na->initialized_size; + mftbmp_na->initialized_size += 8; + a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size > mftbmp_na->data_size) { + mftbmp_na->data_size = mftbmp_na->initialized_size; + a->data_size = cpu_to_sle64(mftbmp_na->data_size); + } + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* Initialize the mft bitmap attribute value with zeroes. */ + ll = 0; + ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); + if (ll == 8) { + ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); + vol->free_mft_records += (8 * 8); + ret = 0; + goto out; + } + ntfs_log_error("Failed to write to mft bitmap.\n"); + err = errno; + if (ll >= 0) + err = EIO; + /* Try to recover from the error. */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.%s\n", es); +put_err_out: + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + a = ctx->attr; + mftbmp_na->initialized_size = old_initialized_size; + a->initialized_size = cpu_to_sle64(old_initialized_size); + if (mftbmp_na->data_size != old_data_size) { + mftbmp_na->data_size = old_data_size; + a->data_size = cpu_to_sle64(old_data_size); + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); +err_out: + errno = err; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_data_extend_allocation - extend mft data attribute + * @vol: volume on which to extend the mft data attribute + * + * Extend the mft data attribute on the ntfs volume @vol by 16 mft records + * worth of clusters or if not enough space for this by one mft record worth + * of clusters. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) +{ + LCN lcn; + VCN old_last_vcn; + s64 min_nr, nr, ll = 0; /* silence compiler warning */ + ntfs_attr *mft_na; + runlist_element *rl, *rl2; + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + ntfs_log_enter("Extending mft data allocation.\n"); + + mft_na = vol->mft_na; + /* + * Determine the preferred allocation location, i.e. the last lcn of + * the mft data attribute. The allocated size of the mft data + * attribute cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mft_na, + (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft data attribute.\n"); + if (rl) + errno = EIO; + goto out; + } + + lcn = rl->lcn + rl->length; + ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); + /* Minimum allocation is one mft record worth of clusters. */ + min_nr = vol->mft_record_size >> vol->cluster_size_bits; + if (!min_nr) + min_nr = 1; + /* Want to allocate 16 mft records worth of clusters. */ + nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = min_nr; + + old_last_vcn = rl[1].vcn; + do { + rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); + if (rl2) + break; + if (errno != ENOSPC || nr == min_nr) { + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; + } + /* + * There is not enough space to do the allocation, but there + * might be enough space to do a minimal allocation so try that + * before failing. + */ + nr = min_nr; + ntfs_log_debug("Retrying mft data allocation with minimal cluster " + "count %lli.\n", (long long)nr); + } while (1); + + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); + + rl = ntfs_runlists_merge(mft_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft data " + "attribute.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate clusters " + "from the mft data attribute.%s\n", es); + free(rl2); + errno = err; + goto out; + } + mft_na->rl = rl; + + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* Update the attribute record as well. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mft_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft data attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft data attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, + mp_size + le16_to_cpu(a->mapping_pairs_offset))) { + ret = ntfs_mft_attr_extend(vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* + * Generate the mapping pairs array directly into the attribute record. + */ + if (ntfs_mapping_pairs_build(vol, + (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, + rl2, ll, NULL)) { + ntfs_log_error("Failed to build mapping pairs array of " + "mft data attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft data allocated_size by nr clusters. + * Reflect this in the ntfs_attr structure and the attribute record. + * @rl is the last (non-terminator) runlist element of mft data + * attribute. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, + mft_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft data attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mft_na->allocated_size += nr << vol->cluster_size_bits; + a->allocated_size = cpu_to_sle64(mft_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mft_na->allocated_size += nr << vol->cluster_size_bits; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + ret = STATUS_ERROR; + goto out; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(old_last_vcn - 1); + errno = err; +undo_alloc: + err = errno; + if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0) + ntfs_log_error("Failed to free clusters from mft data " + "attribute.%s\n", es); + if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn)) + ntfs_log_error("Failed to truncate mft data attribute " + "runlist.%s\n", es); + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping pairs " + "array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + goto out; +} + + +static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + /* + * The mft record is outside the initialized data. Extend the mft data + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume + * is first written to so it optimizes away nicely in the common case. + */ + ntfs_log_debug("Status of mft data before extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + while (size > mft_na->allocated_size) { + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) + goto out; + ntfs_log_debug("Status of mft data after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records along the way. + * Note: We only modify the ntfs_attr structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + while (size > mft_na->initialized_size) { + s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; + mft_na->initialized_size += vol->mft_record_size; + if (mft_na->initialized_size > mft_na->data_size) + mft_na->data_size = mft_na->initialized_size; + ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); + if (ntfs_mft_record_format(vol, ll2) < 0) { + ntfs_log_perror("Failed to format mft record"); + goto undo_data_init; + } + } + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Status of mft data after mft record initialization: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); + + /* Sync MFT to minimize data loss if there won't be clean unmount. */ + if (ntfs_inode_sync(mft_na->ni)) + goto undo_data_init; + + ret = 0; +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na, *mftbmp_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + le16 seq_no, usn; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + base_ni = mft_na->ni; + + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; + + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; + +found_free_rec: + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume + * @vol: volume on which to allocate the mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. + * + * If @base_ni is NULL make the mft record a base mft record and allocate it at + * the default allocator position. + * + * If @base_ni is not NULL make the allocated mft record an extent record, + * allocate it starting at the mft record after the base mft record and attach + * the allocated and opened ntfs inode to the base inode @base_ni. + * + * On success return the now opened ntfs (extent) inode of the mft record. + * + * On error return NULL with errno set to the error code. + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place specified by @base_ni or if + * @base_ni is NULL we start where we last stopped and we perform wrap around + * when we reach the end. Note, we do not try to allocate mft records below + * number 24 because numbers 0 to 15 are the defined system files anyway and 16 + * to 24 are special in that they are used for storing extension mft records + * for the $DATA attribute of $MFT. This is required to avoid the possibility + * of creating a run list with a circular dependence which once written to disk + * can never be read in again. Windows will only use records 16 to 24 for + * normal files if the volume is completely out of space. We never use them + * which means that when the volume is really out of space we cannot create any + * more files while Windows can still create up to 8 small files. We can start + * doing this at some later time, it does not matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 24 to number of + * allocated mft records, then we extend the $MFT/$DATA attribute in order to + * create free mft records. We extend the allocated size of $MFT/$DATA by 16 + * records at a time or one cluster, if cluster size is above 16kiB. If there + * is not sufficient space to do this, we try to extend by a single mft record + * or one cluster, if cluster size is above the mft record size, but we only do + * this if there is enough free space, which we know from the values returned + * by the failed cluster allocation function when we tried to do the first + * allocation. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record, incrementing mft data size and initialized size + * accordingly, open an ntfs_inode for it and return it to the caller, unless + * there are less than 24 mft records, in which case we allocate and initialize + * mft records until we reach record 24 which we consider as the first free mft + * record for use by normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superfluous bits are padded with zeroes. + * + * Thus, when we return successfully (return value non-zero), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - open an ntfs_inode for the allocated mft record, and we will + * - return the ntfs_inode. + * + * On error (return value zero), nothing will have changed. If we had changed + * anything before the error occurred, we will have reverted back to the + * starting state before returning to the caller. Thus, except for bugs, we + * should always leave the volume in a consistent state when returning from + * this function. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important that + * the extension mft records used to store the $DATA attribute of $MFT can be + * reached without having to read the information contained inside them, as + * this would make it impossible to find them in the first place after the + * volume is dismounted. $MFT/$BITMAP probably does not need to follow this + * rule because the bitmap is not essential for finding the mft records, but on + * the other hand, handling the bitmap in this special way would make life + * easier because otherwise there might be circular invocations of functions + * when reading the bitmap but if we are careful, we should be able to avoid + * all problems. + */ +ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + int err; + le16 seq_no, usn; + + if (base_ni) + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); + else + ntfs_log_enter("Entering (allocating a base mft record)\n"); + if (!vol || !vol->mft_na || !vol->mftbmp_na) { + errno = EINVAL; + goto out; + } + + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol); + goto out; + } + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; +retry: + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) { + ntfs_log_debug("found free record (#1) at %lld\n", + (long long)bit); + goto found_free_rec; + } + if (errno != ENOSPC) + goto out; + /* + * No free mft records left. If the mft bitmap already covers more + * than the currently used mft records, the next records are all free, + * so we can simply allocate the first unused mft record. + * Note: We also have to make sure that the mft bitmap at least covers + * the first 24 mft records as they are special and whilst they may not + * be in use, we do not allocate from them. + */ + ll = mft_na->initialized_size >> vol->mft_record_size_bits; + if (mftbmp_na->initialized_size << 3 > ll && + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { + bit = ll; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + ntfs_log_debug("found free record (#2) at %lld\n", + (long long)bit); + goto found_free_rec; + } + /* + * The mft bitmap needs to be expanded until it covers the first unused + * mft record that we can allocate. + * Note: The smallest mft record we allocate is mft record 24. + */ + ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { + + int ret = ntfs_mft_bitmap_extend_allocation(vol); + + if (ret == STATUS_ERROR) + goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } + + ntfs_log_debug("Status of mftbmp after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + } + /* + * We now have sufficient allocated space, extend the initialized_size + * as well as the data_size if necessary and fill the new space with + * zeroes. + */ + bit = mftbmp_na->initialized_size << 3; + if (ntfs_mft_bitmap_extend_initialized(vol)) + goto err_out; + ntfs_log_debug("Status of mftbmp after initialized extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); +found_free_rec: + /* @bit is the found free mft record, allocate it in the mft bitmap. */ + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); + goto err_out; + } + + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_record_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto retry; + } + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + if (base_ni) { + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + } + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_debug("allocated %sinode 0x%llx.\n", + base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_free - free an mft record on an ntfs volume + * @vol: volume on which to free the mft record + * @ni: open ntfs inode of the mft record to free + * + * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. + * Note that this function calls ntfs_inode_close() internally and hence you + * cannot use the pointer @ni any more after this function returns success. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) +{ + u64 mft_no; + int err; + u16 seq_no; + le16 old_seq_no; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!vol || !vol->mftbmp_na || !ni) { + errno = EINVAL; + return -1; + } + + /* Cache the mft reference for later. */ + mft_no = ni->mft_no; + + /* Mark the mft record as not in use. */ + ni->mrec->flags &= ~MFT_RECORD_IN_USE; + + /* Increment the sequence number, skipping zero, if it is not zero. */ + old_seq_no = ni->mrec->sequence_number; + seq_no = le16_to_cpu(old_seq_no); + if (seq_no == 0xffff) + seq_no = 1; + else if (seq_no) + seq_no++; + ni->mrec->sequence_number = cpu_to_le16(seq_no); + + /* Set the inode dirty and write it out. */ + ntfs_inode_mark_dirty(ni); + if (ntfs_inode_sync(ni)) { + err = errno; + goto sync_rollback; + } + + /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ + if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { + err = errno; + // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on + // error, this could be changed to goto sync_rollback; + goto bitmap_rollback; + } + + /* Throw away the now freed inode. */ +#if CACHE_NIDATA_SIZE + if (!ntfs_inode_real_close(ni)) { +#else + if (!ntfs_inode_close(ni)) { +#endif + vol->free_mft_records++; + return 0; + } + err = errno; + + /* Rollback what we did... */ +bitmap_rollback: + if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) + ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " + "Leaving inconsistent metadata!\n"); +sync_rollback: + ni->mrec->flags |= MFT_RECORD_IN_USE; + ni->mrec->sequence_number = old_seq_no; + ntfs_inode_mark_dirty(ni); + errno = err; + return -1; +} + +/** + * ntfs_mft_usn_dec - Decrement USN by one + * @mrec: pointer to an mft record + * + * On success return 0 and on error return -1 with errno set. + */ +int ntfs_mft_usn_dec(MFT_RECORD *mrec) +{ + u16 usn; + le16 *usnp; + + if (!mrec) { + errno = EINVAL; + return -1; + } + usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs)); + usn = le16_to_cpup(usnp); + if (usn-- <= 1) + usn = 0xfffe; + *usnp = cpu_to_le16(usn); + + return 0; +} + diff --git a/lib/libntfs/orig/source/mft.h b/lib/libntfs/orig/source/mft.h new file mode 100644 index 0000000..bb15f0f --- /dev/null +++ b/lib/libntfs/orig/source/mft.h @@ -0,0 +1,132 @@ +/* + * mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MFT_H +#define _NTFS_MFT_H + +#include "volume.h" +#include "inode.h" +#include "layout.h" +#include "logging.h" + +extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_read - read a record from the mft + * @vol: volume to read from + * @mref: mft record number to read + * @b: output data buffer + * + * Read the mft record specified by @mref from volume @vol into buffer @b. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * The read mft record is mst deprotected and is hence ready to use. The caller + * should check the record with is_baad_record() in case mst deprotection + * failed. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + +extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr); + +extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_write - write an mft record to disk + * @vol: volume to write to + * @mref: mft record number to write + * @b: data buffer containing the mft record to write + * + * Write the mft record specified by @mref from buffer @b to volume @vol. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * Before the mft record is written, it is mst protected. After the write, it + * is deprotected again, thus resulting in an increase in the update sequence + * number inside the buffer @b. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b + * @m: mft record to get the data size of + * + * Takes the mft record @m and returns the number of bytes used in the record + * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size + * for an mft record as it at least has to have the MFT_RECORD itself and a + * zero length attribute of type AT_END, thus making the minimum size 56 bytes. + * + * Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte + * alignment of the first attribute mask the difference in MFT_RECORD size + * between NTFS 1.x and 3.x. Also, you would expect every mft record to + * contain an update sequence array as well but that could in theory be + * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this + * as corruption in itself though). + */ +static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) +{ + if (!m || !ntfs_is_mft_record(m->magic)) + return 0; + /* Get the number of used bytes and return it. */ + return le32_to_cpu(m->bytes_in_use); +} + +extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec); + +extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); + +extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); + +extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); + +extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); + +#endif /* defined _NTFS_MFT_H */ + diff --git a/lib/libntfs/orig/source/misc.c b/lib/libntfs/orig/source/misc.c new file mode 100644 index 0000000..b2e17cb --- /dev/null +++ b/lib/libntfs/orig/source/misc.c @@ -0,0 +1,61 @@ +/** + * misc.c : miscellaneous : + * - dealing with errors in memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "types.h" +#include "misc.h" +#include "logging.h" + +/** + * ntfs_calloc + * + * Return a pointer to the allocated memory or NULL if the request fails. + */ +void *ntfs_calloc(size_t size) +{ + void *p; + + p = calloc(1, size); + if (!p) + ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); + return p; +} + +void *ntfs_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); + return p; +} diff --git a/lib/libntfs/orig/source/misc.h b/lib/libntfs/orig/source/misc.h new file mode 100644 index 0000000..a03e964 --- /dev/null +++ b/lib/libntfs/orig/source/misc.h @@ -0,0 +1,30 @@ +/* + * misc.h : miscellaneous exports + * - memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MISC_H_ +#define _NTFS_MISC_H_ + +void *ntfs_calloc(size_t size); +void *ntfs_malloc(size_t size); + +#endif /* _NTFS_MISC_H_ */ + diff --git a/lib/libntfs/orig/source/mst.c b/lib/libntfs/orig/source/mst.c new file mode 100644 index 0000000..470942d --- /dev/null +++ b/lib/libntfs/orig/source/mst.c @@ -0,0 +1,231 @@ +/** + * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "mst.h" +#include "logging.h" + +/** + * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * @size: size in bytes of @b + * + * Perform the necessary post read multi sector transfer fixups and detect the + * presence of incomplete multi sector transfers. - In that case, overwrite the + * magic of the ntfs record header being processed with "BAAD" (in memory only!) + * and abort processing. + * + * Return 0 on success and -1 on error, with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid arguments or invalid NTFS record in buffer @b. + * EIO Multi sector transfer error was detected. Magic of the NTFS + * record in @b will have been set to "BAAD". + */ +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d " + "usa_count: %d", __FUNCTION__, *(le32 *)b, + size, usa_ofs, usa_count); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + /* + * The update sequence number which has to be equal to each of the + * u16 values before they are fixed up. Note no need to care for + * endianness since we are comparing and moving data for on disk + * structures which means the data is consistent. - If it is + * consistency the wrong endianness it doesn't make any difference. + */ + usn = *usa_pos; + /* + * Position in protected data of first u16 that needs fixing up. + */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* + * Check for incomplete multi sector transfer(s). + */ + while (usa_count--) { + if (*data_pos != usn) { + /* + * Incomplete multi sector transfer detected! )-: + * Set the magic to "BAAD" and return failure. + * Note that magic_BAAD is already converted to le32. + */ + errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; + return -1; + } + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + /* Re-setup the variables. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/** + * ntfs_mst_pre_write_fixup - apply multi sector transfer protection + * @b: pointer to the data to protect + * @size: size in bytes of @b + * + * Perform the necessary pre write multi sector transfer fixup on the data + * pointer to by @b of @size. + * + * Return 0 if fixups applied successfully or -1 if no fixups were performed + * due to errors. In that case errno i set to the error code (EINVAL). + * + * NOTE: We consider the absence / invalidity of an update sequence array to + * mean error. This means that you have to create a valid update sequence + * array header in the ntfs record before calling this function, otherwise it + * will fail (the header needs to contain the position of the update sequence + * array together with the number of elements in the array). You also need to + * initialise the update sequence number before calling this function + * otherwise a random word will be used (whatever was in the record at that + * position at that time). + */ +int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Sanity check + only fixup if it makes sense. */ + if (!b || ntfs_is_baad_record(b->magic) || + ntfs_is_hole_record(b->magic)) { + errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); + return -1; + } + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)((u8*)b + usa_ofs); + /* + * Cyclically increment the update sequence number + * (skipping 0 and -1, i.e. 0xffff). + */ + usn = le16_to_cpup(usa_pos) + 1; + if (usn == 0xffff || !usn) + usn = 1; + usn = cpu_to_le16(usn); + *usa_pos = usn; + /* Position in data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment the position in the usa and save the + * original data from the data buffer into the usa. + */ + *(++usa_pos) = *data_pos; + /* Apply fixup to data. */ + *data_pos = usn; + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/** + * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * + * Perform the necessary post write multi sector transfer fixup, not checking + * for any errors, because we assume we have just used + * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never + * have gotten here. + */ +void ntfs_mst_post_write_fixup(NTFS_RECORD *b) +{ + u16 *usa_pos, *data_pos; + + u16 usa_ofs = le16_to_cpu(b->usa_ofs); + u16 usa_count = le16_to_cpu(b->usa_count) - 1; + + ntfs_log_trace("Entering\n"); + + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + + /* Position in protected data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } +} + diff --git a/lib/libntfs/orig/source/mst.h b/lib/libntfs/orig/source/mst.h new file mode 100644 index 0000000..ca81382 --- /dev/null +++ b/lib/libntfs/orig/source/mst.h @@ -0,0 +1,34 @@ +/* + * mst.h - Exports for multi sector transfer fixup functions. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MST_H +#define _NTFS_MST_H + +#include "types.h" +#include "layout.h" + +extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); +extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); + +#endif /* defined _NTFS_MST_H */ + diff --git a/lib/libntfs/orig/source/ntfs.c b/lib/libntfs/orig/source/ntfs.c new file mode 100644 index 0000000..9beb31f --- /dev/null +++ b/lib/libntfs/orig/source/ntfs.c @@ -0,0 +1,662 @@ +/** + * ntfs.c - Simple functionality for startup, mounting and unmounting of NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfs.h" +#include "ntfsinternal.h" +#include "ntfsfile.h" +#include "ntfsdir.h" +#include "gekko_io.h" +#include "cache.h" + +// NTFS device driver devoptab +static const devoptab_t devops_ntfs = { + NULL, /* Device name */ + sizeof (ntfs_file_state), + ntfs_open_r, + ntfs_close_r, + ntfs_write_r, + ntfs_read_r, + ntfs_seek_r, + ntfs_fstat_r, + ntfs_stat_r, + ntfs_link_r, + ntfs_unlink_r, + ntfs_chdir_r, + ntfs_rename_r, + ntfs_mkdir_r, + sizeof (ntfs_dir_state), + ntfs_diropen_r, + ntfs_dirreset_r, + ntfs_dirnext_r, + ntfs_dirclose_r, + ntfs_statvfs_r, + ntfs_ftruncate_r, + ntfs_fsync_r, + NULL /* Device data */ +}; + +void ntfsInit (void) +{ + static bool isInit = false; + + // Initialise ntfs-3g (if not already done so) + if (!isInit) { + isInit = true; + + // Set the log handler + #ifdef NTFS_ENABLE_LOG + ntfs_log_set_handler(ntfs_log_handler_stderr); + #else + ntfs_log_set_handler(ntfs_log_handler_null); + #endif + // Set our current local + ntfs_set_locale(); + + } + + return; +} + +int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) +{ + MASTER_BOOT_RECORD mbr; + PARTITION_RECORD *partition = NULL; + sec_t partition_starts[NTFS_MAX_PARTITIONS] = {0}; + int partition_count = 0; + sec_t part_lba = 0; + int i; + + union { + u8 buffer[MAX_SECTOR_SIZE]; + MASTER_BOOT_RECORD mbr; + EXTENDED_BOOT_RECORD ebr; + NTFS_BOOT_SECTOR boot; + } sector; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + if (!partitions) + return 0; + + // Initialise ntfs-3g + ntfsInit(); + + // Start the device and check that it is inserted + if (!interface->startup()) { + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + return 0; + } + + // Read the first sector on the device + if (!interface->readSectors(0, 1, §or.buffer)) { + errno = EIO; + return -1; + } + + // If this is the devices master boot record + if (sector.mbr.signature == MBR_SIGNATURE) { + memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); + ntfs_log_debug("Valid Master Boot Record found\n"); + + // Search the partition table for all NTFS partitions (max. 4 primary partitions) + for (i = 0; i < 4; i++) { + partition = &mbr.partitions[i]; + part_lba = le32_to_cpu(mbr.partitions[i].lba_start); + + ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, + partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + part_lba, partition->type); + + // Figure out what type of partition this is + switch (partition->type) { + + // Ignore empty partitions + case PARTITION_TYPE_EMPTY: + continue; + + // NTFS partition + case PARTITION_TYPE_NTFS: { + ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); + + // Read and validate the NTFS partition + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } else { + ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); + } + } + + break; + + } + + // DOS 3.3+ or Windows 95 extended partition + case PARTITION_TYPE_DOS33_EXTENDED: + case PARTITION_TYPE_WIN95_EXTENDED: { + ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); + + // Walk the extended partition chain, finding all NTFS partitions within it + sec_t ebr_lba = part_lba; + sec_t next_erb_lba = 0; + do { + + // Read and validate the extended boot record + if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { + if (sector.ebr.signature == EBR_SIGNATURE) { + ntfs_log_debug("Logical Partition @ %d: %s type 0x%x\n", ebr_lba + next_erb_lba, + sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + sector.ebr.partition.type); + + // Get the start sector of the current partition + // and the next extended boot record in the chain + part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); + next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start); + + // Check if this partition has a valid NTFS boot record + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba); + if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } + } + + } else { + next_erb_lba = 0; + } + } + + } while (next_erb_lba); + + break; + + } + + // Unknown or unsupported partition type + default: { + + // Check if this partition has a valid NTFS boot record anyway, + // it might be misrepresented due to a lazy partition editor + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if(partition->type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } + } + + break; + + } + + } + + } + + // Else it is assumed this device has no master boot record + } else { + ntfs_log_debug("No Master Boot Record was found!\n"); + + // As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions + for (i = 0; i < 64; i++) { + if (interface->readSectors(i, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i); + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = i; + partition_count++; + } + } + } + } + + } + + // Return the found partitions (if any) + if (partition_count > 0) { + *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); + if (*partitions) { + memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count); + return partition_count; + } + } + + return 0; +} + +int ntfsMountAll (ntfs_md **mounts, u32 flags) +{ + const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); + const INTERFACE_ID *disc = NULL; + ntfs_md mount_points[NTFS_MAX_MOUNTS]; + sec_t *partitions = NULL; + int mount_count = 0; + int partition_count = 0; + char name[128]; + int i, j, k; + + // Initialise ntfs-3g + ntfsInit(); + + // Find and mount all NTFS partitions on all known devices + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { + disc = &discs[i]; + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { + + // Find the next unused mount name + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); + errno = EADDRNOTAVAIL; + return -1; + } + } while (ntfsGetDevice(name, false)); + + // Mount the partition + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); + mount_points[mount_count].interface = disc->interface; + mount_points[mount_count].startSector = partitions[j]; + mount_count++; + } + } + + } + ntfs_free(partitions); + } + } + + // Return the mounts (if any) + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); + return mount_count; + } + } + + return 0; +} + +int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags) +{ + const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); + const INTERFACE_ID *disc = NULL; + ntfs_md mount_points[NTFS_MAX_MOUNTS]; + sec_t *partitions = NULL; + int mount_count = 0; + int partition_count = 0; + char name[128]; + int i, j, k; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + + // Initialise ntfs-3g + ntfsInit(); + + // Find the specified device then find and mount all NTFS partitions on it + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { + if (discs[i].interface == interface) { + disc = &discs[i]; + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { + + // Find the next unused mount name + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); + errno = EADDRNOTAVAIL; + return -1; + } + } while (ntfsGetDevice(name, false)); + + // Mount the partition + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); + mount_points[mount_count].interface = disc->interface; + mount_points[mount_count].startSector = partitions[j]; + mount_count++; + } + } + + } + ntfs_free(partitions); + } + break; + } + } + + // If we couldn't find the device then return with error status + if (!disc) { + errno = ENODEV; + return -1; + } + + // Return the mounts (if any) + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); + return mount_count; + } + } + + return 0; +} + +bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) +{ + ntfs_vd *vd = NULL; + gekko_fd *fd = NULL; + + // Sanity check + if (!name || !interface) { + errno = EINVAL; + return false; + } + + // Initialise ntfs-3g + ntfsInit(); + + // Check that the requested mount name is free + if (ntfsGetDevice(name, false)) { + errno = EADDRINUSE; + return false; + } + + // Check that we can at least read from this device + if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { + errno = EPERM; + return false; + } + + // Allocate the volume descriptor + vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); + if (!vd) { + errno = ENOMEM; + return false; + } + + // Setup the volume descriptor + vd->id = interface->ioType; + vd->flags = 0; + vd->uid = 0; + vd->gid = 0; + vd->fmask = 0; + vd->dmask = 0; + vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); + vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); + vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); + + // Allocate the device driver descriptor + fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); + if (!fd) { + ntfs_free(vd); + errno = ENOMEM; + return false; + } + + // Setup the device driver descriptor + fd->interface = interface; + fd->startSector = startSector; + fd->sectorSize = 0; + fd->sectorCount = 0; + fd->cachePageCount = cachePageCount; + fd->cachePageSize = cachePageSize; + + // Allocate the device driver + vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); + if (!vd->dev) { + ntfs_free(fd); + ntfs_free(vd); + return false; + } + + // Build the mount flags + if (flags & NTFS_READ_ONLY) + vd->flags |= MS_RDONLY; + else + { + if (!(interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_RDONLY; + if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_EXCLUSIVE; + } + if (flags & NTFS_RECOVER) + vd->flags |= MS_RECOVER; + if (flags & NTFS_IGNORE_HIBERFILE) + vd->flags |= MS_IGNORE_HIBERFILE; + + if (vd->flags & MS_RDONLY) + ntfs_log_debug("Mounting \"%s\" as read-only\n", name); + + // Mount the device + vd->vol = ntfs_device_mount(vd->dev, vd->flags); + if (!vd->vol) { + switch(ntfs_volume_error(errno)) { + case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break; + case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break; + case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break; + default: errno = EINVAL; break; + } + ntfs_device_free(vd->dev); + ntfs_free(vd); + return false; + } + + if (flags & NTFS_IGNORE_CASE) + ntfs_set_ignore_case(vd->vol); + + // Initialise the volume descriptor + if (ntfsInitVolume(vd)) { + ntfs_umount(vd->vol, true); + ntfs_free(vd); + return false; + } + + // Add the device to the devoptab table + if (ntfsAddDevice(name, vd)) { + ntfsDeinitVolume(vd); + ntfs_umount(vd->vol, true); + ntfs_free(vd); + return false; + } + + return true; +} + +void ntfsUnmount (const char *name, bool force) +{ + ntfs_vd *vd = NULL; + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) + return; + + // Remove the device from the devoptab table + ntfsRemoveDevice(name); + + // Deinitialise the volume descriptor + ntfsDeinitVolume(vd); + + // Unmount the volume + ntfs_umount(vd->vol, force); + + // Free the volume descriptor + ntfs_free(vd); + + return; +} + +const char *ntfsGetVolumeName (const char *name) +{ + ntfs_vd *vd = NULL; + + // Sanity check + if (!name) { + errno = EINVAL; + return NULL; + } + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) { + errno = ENODEV; + return NULL; + } + return vd->vol->vol_name; +} + +bool ntfsSetVolumeName (const char *name, const char *volumeName) +{ + ntfs_vd *vd = NULL; + ntfs_attr *na = NULL; + ntfschar *ulabel = NULL; + int ulabel_len; + + // Sanity check + if (!name) { + errno = EINVAL; + return false; + } + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) { + errno = ENODEV; + return false; + } + + // Lock + ntfsLock(vd); + + // Convert the new volume name to unicode + ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); + if (ulabel_len < 0) { + ntfsUnlock(vd); + errno = EINVAL; + return false; + } + + // Check if the volume name attribute exists + na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); + if (na) { + + // It does, resize it to match the length of the new volume name + if (ntfs_attr_truncate(na, ulabel_len)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + // Write the new volume name + if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + } else { + + // It doesn't, create it now + if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + } + + // Reset the volumes name cache (as it has now been changed) + vd->name[0] = '\0'; + + // Close the volume name attribute + if (na) + ntfs_attr_close(na); + + // Sync the volume node + if (ntfs_inode_sync(vd->vol->vol_ni)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + // Clean up + free(ulabel); + + // Unlock + ntfsUnlock(vd); + + return true; +} + +const devoptab_t *ntfsGetDevOpTab (void) +{ + return &devops_ntfs; +} diff --git a/lib/libntfs/orig/source/ntfsdir.c b/lib/libntfs/orig/source/ntfsdir.c new file mode 100644 index 0000000..ad54b51 --- /dev/null +++ b/lib/libntfs/orig/source/ntfsdir.c @@ -0,0 +1,636 @@ +/** + * ntfs_dir.c - devoptab directory routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsdir.h" +#include "device.h" +#include + +#define STATE(x) ((ntfs_dir_state*)(x)->dirStruct) + +void ntfsCloseDir (ntfs_dir_state *dir) +{ + // Sanity check + if (!dir || !dir->vd) + return; + + // Free the directory entries (if any) + while (dir->first) { + ntfs_dir_entry *next = dir->first->next; + ntfs_free(dir->first->name); + ntfs_free(dir->first); + dir->first = next; + } + + // Close the directory (if open) + if (dir->ni) + ntfsCloseEntry(dir->vd, dir->ni); + + // Reset the directory state + dir->ni = NULL; + dir->first = NULL; + dir->current = NULL; + + return; +} + +int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + // Short circuit cases were we don't actually have to do anything + if (!st || !path) + return 0; + + ntfs_log_trace("path %s, st %p\n", path, st); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFDIR; + return 0; + } + + // Lock + ntfsLock(vd); + + // Find the entry + ni = ntfsOpenEntry(vd, path); + if (!ni) { + r->_errno = errno; + ntfsUnlock(vd); + return -1; + } + + // Get the entry stats + int ret = ntfsStat(vd, ni, st); + if (ret) + r->_errno = errno; + + // Close the entry + ntfsCloseEntry(vd, ni); + + ntfsUnlock(vd); + + return 0; +} + +int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + ntfs_log_trace("existing %s, newLink %s\n", existing, newLink); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(existing); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Create a symbolic link between the two paths + ni = ntfsCreate(vd, existing, S_IFLNK, newLink); + if (!ni) { + ntfsUnlock(vd); + r->_errno = errno; + return -1; + } + + // Close the symbolic link + ntfsCloseEntry(vd, ni); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_unlink_r (struct _reent *r, const char *name) +{ + ntfs_log_trace("name %s\n", name); + + // Unlink the entry + int ret = ntfsUnlink(ntfsGetVolume(name), name); + if (ret) + r->_errno = errno; + + return ret; +} + +int ntfs_chdir_r (struct _reent *r, const char *name) +{ + ntfs_log_trace("name %s\n", name); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Find the directory + ni = ntfsOpenEntry(vd, name); + if (!ni) { + ntfsUnlock(vd); + r->_errno = ENOENT; + return -1; + } + + // Ensure that this directory is indeed a directory + if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); + r->_errno = ENOTDIR; + return -1; + } + + // Close the old current directory (if any) + if (vd->cwd_ni) + ntfsCloseEntry(vd, vd->cwd_ni); + + // Set the new current directory + vd->cwd_ni = ni; + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + ntfs_log_trace("oldName %s, newName %s\n", oldName, newName); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(oldName); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // You cannot rename between devices + if(vd != ntfsGetVolume(newName)) { + ntfsUnlock(vd); + r->_errno = EXDEV; + return -1; + } + + // Check that there is no existing entry with the new name + ni = ntfsOpenEntry(vd, newName); + if (ni) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); + r->_errno = EEXIST; + return -1; + } + + // Link the old entry with the new one + if (ntfsLink(vd, oldName, newName)) { + ntfsUnlock(vd); + return -1; + } + + // Unlink the old entry + if (ntfsUnlink(vd, oldName)) { + if (ntfsUnlink(vd, newName)) { + ntfsUnlock(vd); + return -1; + } + ntfsUnlock(vd); + return -1; + } + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_mkdir_r (struct _reent *r, const char *path, int mode) +{ + ntfs_log_trace("path %s, mode %i\n", path, mode); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Create the directory + ni = ntfsCreate(vd, path, S_IFDIR, NULL); + if (!ni) { + ntfsUnlock(vd); + r->_errno = errno; + return -1; + } + + // Close the directory + ntfsCloseEntry(vd, ni); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + ntfs_log_trace("path %s, buf %p\n", path, buf); + + ntfs_vd *vd = NULL; + s64 size; + int delta_bits; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Lock + ntfsLock(vd); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + if(ntfs_volume_get_free_space(vd->vol) < 0) + { + ntfsUnlock(vd); + return -1; + } + + // File system block size + buf->f_bsize = vd->vol->cluster_size; + + // Fundamental file system block size + buf->f_frsize = vd->vol->cluster_size; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = vd->vol->nr_clusters; + + // Free blocks available for all and for non-privileged processes + size = MAX(vd->vol->free_clusters, 0); + buf->f_bfree = buf->f_bavail = size; + + // Free inodes on the free space + delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits; + if (delta_bits >= 0) + size <<= delta_bits; + else + size >>= -delta_bits; + + // Number of inodes at this point in time + buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size; + + // Free inodes available for all and for non-privileged processes + size += vd->vol->free_mft_records; + buf->f_ffree = buf->f_favail = MAX(size, 0); + + // File system id + buf->f_fsid = vd->id; + + // Bit mask of f_flag values. + buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0); + + // Maximum length of filenames + buf->f_namemax = NTFS_MAX_NAME_LEN; + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +/** + * PRIVATE: Callback for directory walking + */ +int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type, + const s64 pos, const MFT_REF mref, const unsigned dt_type) +{ + ntfs_dir_state *dir = STATE(dirState); + ntfs_dir_entry *entry = NULL; + char *entry_name = NULL; + + // Sanity check + if (!dir || !dir->vd) { + errno = EINVAL; + return -1; + } + + // Ignore DOS file names + if (name_type == FILE_NAME_DOS) { + return 0; + } + + // Preliminary check that this entry can be enumerated (as described by the volume descriptor) + if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || dir->vd->showSystemFiles) { + + // Convert the entry name to our current local + if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) { + return -1; + } + + if(dir->first && dir->first->mref == FILE_root && + MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0) + { + return 0; + } + + // If this is not the parent or self directory reference + if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) { + + // Open the entry + ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name); + if (!ni) + return -1; + + // Double check that this entry can be emuerated (as described by the volume descriptor) + if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) || + ((ni->flags & FILE_ATTR_SYSTEM) && !dir->vd->showSystemFiles)) { + ntfs_inode_close(ni); + return 0; + } + + // Close the entry + ntfs_inode_close(ni); + + } + + // Allocate a new directory entry + entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry)); + if (!entry) + return -1; + + // Setup the entry + entry->name = entry_name; + entry->next = NULL; + entry->mref = MREF(mref); + + // Link the entry to the directory + if (!dir->first) { + dir->first = entry; + } else { + ntfs_dir_entry *last = dir->first; + while (last->next) last = last->next; + last->next = entry; + } + + } + + return 0; +} + +DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + ntfs_log_trace("dirState %p, path %s\n", dirState, path); + + ntfs_dir_state* dir = STATE(dirState); + s64 position = 0; + + // Get the volume descriptor for this path + dir->vd = ntfsGetVolume(path); + if (!dir->vd) { + r->_errno = ENODEV; + return NULL; + } + + // Lock + ntfsLock(dir->vd); + + // Find the directory + dir->ni = ntfsOpenEntry(dir->vd, path); + if (!dir->ni) { + ntfsUnlock(dir->vd); + r->_errno = ENOENT; + return NULL; + } + + // Ensure that this directory is indeed a directory + if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(dir->vd, dir->ni); + ntfsUnlock(dir->vd); + r->_errno = ENOTDIR; + return NULL; + } + + // Read the directory + dir->first = dir->current = NULL; + if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) { + ntfsCloseDir(dir); + ntfsUnlock(dir->vd); + r->_errno = errno; + return NULL; + } + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Insert the directory into the double-linked FILO list of open directories + if (dir->vd->firstOpenDir) { + dir->nextOpenDir = dir->vd->firstOpenDir; + dir->vd->firstOpenDir->prevOpenDir = dir; + } else { + dir->nextOpenDir = NULL; + } + dir->prevOpenDir = NULL; + dir->vd->cwd_ni = dir->ni; + dir->vd->firstOpenDir = dir; + dir->vd->openDirCount++; + + // Unlock + ntfsUnlock(dir->vd); + + return dirState; +} + +int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + ntfs_log_trace("dirState %p\n", dirState); + + ntfs_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} + +int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); + + ntfs_dir_state* dir = STATE(dirState); + ntfs_inode *ni = NULL; + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Check that there is a entry waiting to be fetched + if (!dir->current) { + ntfsUnlock(dir->vd); + r->_errno = ENOENT; + return -1; + } + + // Fetch the current entry + strcpy(filename, dir->current->name); + if(filestat != NULL) + { + if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) + { + memset(filestat, 0, sizeof(struct stat)); + filestat->st_mode = S_IFDIR; + } + else + { + ni = ntfsOpenEntry(dir->vd, dir->current->name); + if (ni) { + ntfsStat(dir->vd, ni, filestat); + ntfsCloseEntry(dir->vd, ni); + } + } + } + + // Move to the next entry in the directory + dir->current = dir->current->next; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} + +int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + ntfs_log_trace("dirState %p\n", dirState); + + ntfs_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Close the directory + ntfsCloseDir(dir); + + // Remove the directory from the double-linked FILO list of open directories + dir->vd->openDirCount--; + if (dir->nextOpenDir) + dir->nextOpenDir->prevOpenDir = dir->prevOpenDir; + if (dir->prevOpenDir) + dir->prevOpenDir->nextOpenDir = dir->nextOpenDir; + else + dir->vd->firstOpenDir = dir->nextOpenDir; + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} diff --git a/lib/libntfs/orig/source/ntfsdir.h b/lib/libntfs/orig/source/ntfsdir.h new file mode 100644 index 0000000..0550592 --- /dev/null +++ b/lib/libntfs/orig/source/ntfsdir.h @@ -0,0 +1,68 @@ +/** + * ntfs_dir.c - devoptab directory routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSDIR_H +#define _NTFSDIR_H + +#include "ntfsinternal.h" +#include + +/** + * ntfs_dir_entry - Directory entry + */ +typedef struct _ntfs_dir_entry { + char *name; + u64 mref; + struct _ntfs_dir_entry *next; +} ntfs_dir_entry; + +/** + * ntfs_dir_state - Directory state + */ +typedef struct _ntfs_dir_state { + ntfs_vd *vd; /* Volume this directory belongs to */ + ntfs_inode *ni; /* Directory descriptor */ + ntfs_dir_entry *first; /* The first entry in the directory */ + ntfs_dir_entry *current; /* The current entry in the directory */ + struct _ntfs_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */ + struct _ntfs_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */ +} ntfs_dir_state; + +/* Directory state routines */ +void ntfsCloseDir (ntfs_dir_state *file); + +/* Gekko devoptab directory routines for NTFS-based devices */ +extern int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st); +extern int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink); +extern int ntfs_unlink_r (struct _reent *r, const char *name); +extern int ntfs_chdir_r (struct _reent *r, const char *name); +extern int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName); +extern int ntfs_mkdir_r (struct _reent *r, const char *path, int mode); +extern int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* Gekko devoptab directory walking routines for NTFS-based devices */ +extern DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path); +extern int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState); + +#endif /* _NTFSDIR_H */ + diff --git a/lib/libntfs/orig/source/ntfsfile.c b/lib/libntfs/orig/source/ntfsfile.c new file mode 100644 index 0000000..f361812 --- /dev/null +++ b/lib/libntfs/orig/source/ntfsfile.c @@ -0,0 +1,526 @@ +/** + * ntfsfile.c - devoptab file routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsfile.h" + +#define STATE(x) ((ntfs_file_state*)x) + +void ntfsCloseFile (ntfs_file_state *file) +{ + // Sanity check + if (!file || !file->vd) + return; + + // Special case fix ups for compressed and/or encrypted files + if (file->compressed) + ntfs_attr_pclose(file->data_na); +#ifdef HAVE_SETXATTR + if (file->encrypted) + ntfs_efs_fixup_attribute(NULL, file->data_na); +#endif + // Close the file data attribute (if open) + if (file->data_na) + ntfs_attr_close(file->data_na); + + // Sync the file (and its attributes) to disc + if(file->write) + { + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); + ntfsSync(file->vd, file->ni); + } + + if (file->read) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + + // Close the file (if open) + if (file->ni) + ntfsCloseEntry(file->vd, file->ni); + + // Reset the file state + file->ni = NULL; + file->data_na = NULL; + file->flags = 0; + file->read = false; + file->write = false; + file->append = false; + file->pos = 0; + file->len = 0; + + return; +} + +int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", (void *) fileStruct, path, flags, mode); + + ntfs_file_state* file = STATE(fileStruct); + + // Get the volume descriptor for this path + file->vd = ntfsGetVolume(path); + if (!file->vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Determine which mode the file is opened for + file->flags = flags; + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + } else { + r->_errno = EACCES; + ntfsUnlock(file->vd); + return -1; + } + + // Try and find the file and (if found) ensure that it is not a directory + file->ni = ntfsOpenEntry(file->vd, path); + if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EISDIR; + return -1; + } + + // Are we creating this file? + if ((flags & O_CREAT) && !file->ni) { + + // Create the file + file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); + if (!file->ni) { + ntfsUnlock(file->vd); + return -1; + } + + } + + // Sanity check, the file should be open by now + if (!file->ni) { + ntfsUnlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + // Open the files data attribute + file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); + if(!file->data_na) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + return -1; + } + + // Determine if this files data is compressed and/or encrypted + file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); + file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); + + // We cannot read/write encrypted files + if (file->encrypted) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // Make sure we aren't trying to write to a read-only file + if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EROFS; + return -1; + } + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write) { + if (ntfs_attr_truncate(file->data_na, 0)) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } + + // Set the files current position and length + file->pos = 0; + file->len = file->data_na->data_size; + + ntfs_log_trace("file->len %llu\n", file->len); + + // Update file times + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + + // Insert the file into the double-linked FILO list of open files + if (file->vd->firstOpenFile) { + file->nextOpenFile = file->vd->firstOpenFile; + file->vd->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + file->vd->firstOpenFile = file; + file->vd->openFileCount++; + + // Unlock + ntfsUnlock(file->vd); + + return (int)fileStruct; +} + +int ntfs_close_r (struct _reent *r, int fd) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Close the file + ntfsCloseFile(file); + + // Remove the file from the double-linked FILO list of open files + file->vd->openFileCount--; + if (file->nextOpenFile) + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + if (file->prevOpenFile) + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + else + file->vd->firstOpenFile = file->nextOpenFile; + + // Unlock + ntfsUnlock(file->vd); + + return 0; +} + +ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) +{ + ntfs_log_trace("fd %p, ptr %p, len %u\n", (void *) fd, ptr, len); + + ntfs_file_state* file = STATE(fd); + ssize_t written = 0; + off_t old_pos = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // If we are in append mode, backup the current position and move to the end of the file + if (file->append) { + old_pos = file->pos; + file->pos = file->len; + } + + // Write to the files data atrribute + while (len) { + ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); + if (ret <= 0) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + len -= ret; + file->pos += ret; + written += ret; + } + + // If we are in append mode, restore the current position to were it was prior to this write + if (file->append) { + file->pos = old_pos; + } + + // Mark the file for archiving (if we actually wrote something) + if (written) + file->ni->flags |= FILE_ATTR_ARCHIVE; + + // Update the files data length + file->len = file->data_na->data_size; + + // Unlock + ntfsUnlock(file->vd); + + return written; +} + +ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + ntfs_log_trace("fd %p, ptr %p, len %u\n", (void *) fd, ptr, len); + + ntfs_file_state* file = STATE(fd); + ssize_t read = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to read from this file + if (!file->read) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // Don't read past the end of file + if (file->pos + len > file->len) { + r->_errno = EOVERFLOW; + len = file->len - file->pos; + ntfs_log_trace("EOVERFLOW"); + } + + ntfs_log_trace("file->pos:%d, len:%d, file->len:%d \n", (u32)file->pos, (u32)len, (u32)file->len); + + // Read from the files data attribute + while (len) { + ssize_t ret = ntfs_attr_pread(file->data_na, file->pos, len, ptr); + if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + ptr += ret; + len -= ret; + file->pos += ret; + read += ret; + } + //ntfs_log_trace("file->pos: %d \n", (u32)file->pos); + // Update file times (if we actually read something) + + // Unlock + ntfsUnlock(file->vd); + + return read; +} + +off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + ntfs_log_trace("fd %p, pos %llu, dir %i\n", (void *) fd, pos, dir); + + ntfs_file_state* file = STATE(fd); + off_t position = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Set the files current position + switch(dir) { + case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break; + case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break; + case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break; + } + + // Unlock + ntfsUnlock(file->vd); + + return position; +} +int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Get the file stats + ret = ntfsStat(file->vd, file->ni, st); + if (ret) + r->_errno = errno; + + return ret; +} + +int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len) +{ + ntfs_log_trace("fd %p, len %llu\n", (void *) fd, (u64) len); + + ntfs_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // For compressed files, only deleting and expanding contents are implemented + if (file->compressed && + len > 0 && + len < file->data_na->initialized_size) { + ntfsUnlock(file->vd); + r->_errno = EOPNOTSUPP; + return -1; + } + + // Resize the files data attribute, either by expanding or truncating + if (file->compressed && len > file->data_na->initialized_size) { + char zero = 0; + if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } else { + if (ntfs_attr_truncate(file->data_na, len)) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } + + // Mark the file for archiving (if we actually changed something) + if (file->len != file->data_na->data_size) + file->ni->flags |= FILE_ATTR_ARCHIVE; + + // Update file times (if we actually changed something) + if (file->len != file->data_na->data_size) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); + + // Update the files data length + file->len = file->data_na->data_size; + + // Sync the file (and its attributes) to disc + ntfsSync(file->vd, file->ni); + + // Unlock + ntfsUnlock(file->vd); + + return 0; +} + +int ntfs_fsync_r (struct _reent *r, int fd) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Sync the file (and its attributes) to disc + ret = ntfsSync(file->vd, file->ni); + if (ret) + r->_errno = errno; + + // Unlock + ntfsUnlock(file->vd); + + return ret; +} diff --git a/lib/libntfs/orig/source/ntfsfile.h b/lib/libntfs/orig/source/ntfsfile.h new file mode 100644 index 0000000..8e5ffcc --- /dev/null +++ b/lib/libntfs/orig/source/ntfsfile.h @@ -0,0 +1,65 @@ +/** + * ntfsfile.c - devoptab file routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSFILE_H +#define _NTFSFILE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ntfsinternal.h" +#include + +/** + * ntfs_file_state - File state + */ +typedef struct _ntfs_file_state { + ntfs_vd *vd; /* Volume this file belongs to */ + ntfs_inode *ni; /* File descriptor */ + ntfs_attr *data_na; /* File data descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ + bool compressed; /* True if file data is compressed */ + bool encrypted; /* True if file data is encryted */ + off_t pos; /* Current position within the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ + struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} ntfs_file_state; + +/* File state routines */ +void ntfsCloseFile (ntfs_file_state *file); + +/* Gekko devoptab file routines for NTFS-based devices */ +extern int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +extern int ntfs_close_r (struct _reent *r, int fd); +extern ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len); +extern ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len); +extern off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir); +extern int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st); +extern int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len); +extern int ntfs_fsync_r (struct _reent *r, int fd); + +#endif /* _NTFSFILE_H */ + diff --git a/lib/libntfs/orig/source/ntfsinternal.c b/lib/libntfs/orig/source/ntfsinternal.c new file mode 100644 index 0000000..c2de094 --- /dev/null +++ b/lib/libntfs/orig/source/ntfsinternal.c @@ -0,0 +1,868 @@ +/** + * ntfsinternal.h - Internal support routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsdir.h" +#include "ntfsfile.h" + +#if defined(__wii__) +#include +#include +#include + +const INTERFACE_ID ntfs_disc_interfaces[] = { + { "sd", &__io_wiisd }, + { "usb", &__io_usbstorage }, + { "carda", &__io_gcsda }, + { "cardb", &__io_gcsdb }, + { NULL, NULL } +}; + +#elif defined(__gamecube__) +#include + +const INTERFACE_ID ntfs_disc_interfaces[] = { + { "carda", &__io_gcsda }, + { "cardb", &__io_gcsdb }, + { NULL, NULL } +}; + +#endif + +int ntfsAddDevice (const char *name, void *deviceData) +{ + const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); + devoptab_t *dev = NULL; + char *devname = NULL; + int i; + + // Sanity check + if (!name || !deviceData || !devoptab_ntfs) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return false; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // Setup the devoptab + memcpy(dev, devoptab_ntfs, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = deviceData; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 0; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0] && i != 0) { + devoptab_list[i] = dev; + return 0; + } + } + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +void ntfsRemoveDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + ntfs_free((devoptab_t*)devoptab); + break; + } + } + } + + return; +} + +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return devoptab; + } + } + } + + // If we reach here then we couldn't find the device name, + // chances are that this path has no device name in it. + // Call GetDeviceOpTab to get our default device (chdir). + if (useDefaultDevice) + return GetDeviceOpTab(""); + + return NULL; +} + +const INTERFACE_ID *ntfsGetDiscInterfaces (void) +{ + // Get all know disc interfaces on the host system + return ntfs_disc_interfaces; +} + +ntfs_vd *ntfsGetVolume (const char *path) +{ + // Get the volume descriptor from the paths associated devoptab (if found) + const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); + const devoptab_t *devoptab = ntfsGetDevice(path, true); + if (devoptab && devoptab_ntfs && (devoptab->open_r == devoptab_ntfs->open_r)) + return (ntfs_vd*)devoptab->deviceData; + + return NULL; +} + +int ntfsInitVolume (ntfs_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Initialise the volume lock + LWP_MutexInit(&vd->lock, false); + + // Reset the volumes name cache + vd->name[0] = '\0'; + + // Reset the volumes current directory + vd->cwd_ni = NULL; + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + return 0; +} + +void ntfsDeinitVolume (ntfs_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ntfsLock(vd); + + // Close any directories which are still open (lazy programmers!) + ntfs_dir_state *nextDir = vd->firstOpenDir; + while (nextDir) { + ntfs_log_warning("Cleaning up orphaned directory @ %p\n", nextDir); + ntfsCloseDir(nextDir); + nextDir = nextDir->nextOpenDir; + } + + // Close any files which are still open (lazy programmers!) + ntfs_file_state *nextFile = vd->firstOpenFile; + while (nextFile) { + ntfs_log_warning("Cleaning up orphaned file @ %p\n", nextFile); + ntfsCloseFile(nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + // Close the volumes current directory (if any) + //if (vd->cwd_ni) { + //ntfsCloseEntry(vd, vd->cwd_ni); + //vd->cwd_ni = NULL; + //} + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // Unlock + ntfsUnlock(vd); + + // Deinitialise the volume lock + LWP_MutexDestroy(vd->lock); + + return; +} + +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path) +{ + return ntfsParseEntry(vd, path, 1); +} + +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel) +{ + ntfs_inode *ni = NULL; + char *target = NULL; + int attr_size; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // Get the actual path of the entry + path = ntfsRealPath(path); + if (!path) { + errno = EINVAL; + return NULL; + } else if (path[0] == '\0') { + path = "."; + } + + // Find the entry, taking into account our current directory (if any) + if (path[0] != PATH_SEP) + ni = ntfs_pathname_to_inode(vd->vol, vd->cwd_ni, path++); + else + ni = ntfs_pathname_to_inode(vd->vol, NULL, path); + + // If the entry was found and it has reparse data then parse its true path; + // this resolves the true location of symbolic links and directory junctions + if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) { + if (ntfs_possible_symlink(ni)) { + + // Sanity check, give up if we are parsing to deep + if (reparseLevel > NTFS_MAX_SYMLINK_DEPTH) { + ntfsCloseEntry(vd, ni); + errno = ELOOP; + return NULL; + } + + // Get the target path of this entry + target = ntfs_make_symlink(ni, path, &attr_size); + if (!target) { + ntfsCloseEntry(vd, ni); + return NULL; + } + + // Close the entry (we are no longer interested in it) + ntfsCloseEntry(vd, ni); + + // Parse the entries target + ni = ntfsParseEntry(vd, target, reparseLevel++); + + // Clean up + // use free because the value was not allocated with ntfs_alloc + free(target); + + } + } + + return ni; +} + +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ntfsLock(vd); + + // Sync the entry (if it is dirty) + if (NInoDirty(ni)) + ntfsSync(vd, ni); + + // Close the entry + ntfs_inode_close(ni); + + // Unlock + ntfsUnlock(vd); + + return; +} + + +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL, *utarget = NULL; + int uname_len, utarget_len; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // You cannot link between devices + if(target) { + if(vd != ntfsGetVolume(target)) { + errno = EXDEV; + return NULL; + } + } + + // Get the actual paths of the entry + path = ntfsRealPath(path); + target = ntfsRealPath(target); + if (!path) { + errno = EINVAL; + return NULL; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible, clean it up + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if(name) + { + name++; + name[0] = 0; + } + + // Open the entries parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + goto cleanup; + } + + // Create the entry + switch (type) { + + // Symbolic link + case S_IFLNK: + if (!target) { + errno = EINVAL; + goto cleanup; + } + utarget_len = ntfsLocalToUnicode(target, &utarget); + if (utarget_len < 0) { + errno = EINVAL; + goto cleanup; + } + ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); + break; + + // Directory or file + case S_IFDIR: + case S_IFREG: + ni = ntfs_create(dir_ni, 0, uname, uname_len, type); + break; + + } + + // If the entry was created + if (ni) { + + // Mark the entry for archiving + ni->flags |= FILE_ATTR_ARCHIVE; + + // Mark the entry as dirty + NInoSetDirty(ni); + + // Sync the entry to disc + ntfsSync(vd, ni); + + // Update parent directories times + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); + + } + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + // use free because the value was not allocated with ntfs_alloc + if(utarget) + free(utarget); + + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return ni; +} + +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL; + int uname_len; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // You cannot link between devices + if(vd != ntfsGetVolume(new_path)) { + errno = EXDEV; + return -1; + } + + // Get the actual paths of the entry + old_path = ntfsRealPath(old_path); + new_path = ntfsRealPath(new_path); + if (!old_path || !new_path) { + errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible, clean it up + dir = strdup(new_path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + *name = 0; + + // Find the entry + ni = ntfsOpenEntry(vd, old_path); + if (!ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Open the entries new parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Link the entry to its new parent + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -1; + goto cleanup; + } + + // Update entry times + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); + + // Sync the entry to disc + ntfsSync(vd, ni); + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + if(ni) + ntfsCloseEntry(vd, ni); + + // use free because the value was not allocated with ntfs_alloc + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return res; +} + +int ntfsUnlink (ntfs_vd *vd, const char *path) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL; + int uname_len; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Get the actual path of the entry + path = ntfsRealPath(path); + if (!path) { + errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if(name) + { + name++; + name[0] = 0; + } + + // Find the entry + ni = ntfsOpenEntry(vd, path); + if (!ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Open the entries parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Unlink the entry from its parent + if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) { + res = -1; + } + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore + dir_ni = ni = NULL; + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + if(ni) + ntfsCloseEntry(vd, ni); + + // use free because the value was not allocated with ntfs_alloc + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni) +{ + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Lock + ntfsLock(vd); + + // Sync the entry + res = ntfs_inode_sync(ni); + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // Unlock + ntfsUnlock(vd); + + return res; + +} + +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st) +{ + ntfs_attr *na = NULL; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Lock + ntfsLock(vd); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + // Is this entry a directory + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + st->st_mode = S_IFDIR | (0777 & ~vd->dmask); + st->st_nlink = 1; + + // Open the directories index allocation table attribute + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (na) { + st->st_size = na->data_size; + st->st_blocks = na->allocated_size >> 9; + ntfs_attr_close(na); + } + + // Else it must be a file + } else { + st->st_mode = S_IFREG | (0777 & ~vd->fmask); + st->st_size = ni->data_size; + st->st_blocks = (ni->allocated_size + 511) >> 9; + st->st_nlink = le16_to_cpu(ni->mrec->link_count); + } + + // Fill in the generic entry stats + st->st_dev = vd->id; + st->st_uid = vd->uid; + st->st_gid = vd->gid; + st->st_ino = ni->mft_no; + st->st_atime = ni->last_access_time; + st->st_ctime = ni->last_mft_change_time; + st->st_mtime = ni->last_data_change_time; + + // Update entry times + ntfsUpdateTimes(vd, ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(vd); + + return res; +} + +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask) +{ + // Run the access time update strategy against the device driver settings first + if (vd && vd->atime == ATIME_DISABLED) + mask &= ~NTFS_UPDATE_ATIME; + + // Update entry times + if (ni && mask) + ntfs_inode_update_times(ni, mask); + + return; +} + +const char *ntfsRealPath (const char *path) +{ + // Sanity check + if (!path) + return NULL; + + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; + } + if (strchr(path, ':') != NULL) { + return NULL; + } + + return path; +} + +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len) +{ + int len = 0; + int i; + + // Sanity check + if (!ins || !ins_len || !outs) + return 0; + + char * ucstombs_out = NULL; + + // Convert the unicode string to our current local + len = ntfs_ucstombs(ins, ins_len, &ucstombs_out, outs_len); + + if(ucstombs_out) + { + //use proper allocation + *outs = (char *) ntfs_alloc(strlen(ucstombs_out) + 1); + if(!*outs) + { + errno = ENOMEM; + return -1; + } + strcpy(*outs, ucstombs_out); + free(ucstombs_out); + ucstombs_out = NULL; + } + + if (len == -1 && errno == EILSEQ) + { + // The string could not be converted to the current local, + // do it manually by replacing non-ASCII characters with underscores + if (!*outs || outs_len >= ins_len) + { + if (!*outs) + { + *outs = (char *) ntfs_alloc(ins_len + 1); + if (!*outs) { + errno = ENOMEM; + return -1; + } + } + for (i = 0; i < ins_len; i++) { + ntfschar uc = le16_to_cpu(ins[i]); + if (uc > 0xff) + uc = (ntfschar)'_'; + *outs[i] = (char)uc; + } + *outs[ins_len] = (ntfschar)'\0'; + len = ins_len; + } + } + + return len; +} + +int ntfsLocalToUnicode (const char *ins, ntfschar **outs) +{ + // Sanity check + if (!ins || !outs) + return 0; + + // Convert the local string to unicode + return ntfs_mbstoucs(ins, outs); +} diff --git a/lib/libntfs/orig/source/ntfsinternal.h b/lib/libntfs/orig/source/ntfsinternal.h new file mode 100644 index 0000000..11dfb8f --- /dev/null +++ b/lib/libntfs/orig/source/ntfsinternal.h @@ -0,0 +1,178 @@ +/** + * ntfsinternal.h - Internal support routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSINTERNAL_H +#define _NTFSINTERNAL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "types.h" +#include "compat.h" +#include "logging.h" +#include "layout.h" +#include "device.h" +#include "volume.h" +#include "dir.h" +#include "inode.h" +#include "attrib.h" +#include "reparse.h" +#include "security.h" +#include "efs.h" +#include "unistr.h" + +#include +#include +#include + +#define NTFS_MOUNT_PREFIX "ntfs" /* Device name prefix to use when auto-mounting */ +#define NTFS_MAX_PARTITIONS 32 /* Maximum number of partitions that can be found */ +#define NTFS_MAX_MOUNTS 10 /* Maximum number of mounts available at one time */ +#define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links */ + +#define NTFS_OEM_ID cpu_to_le64(0x202020205346544eULL) /* "NTFS " */ + +#define MBR_SIGNATURE cpu_to_le16(0xAA55) +#define EBR_SIGNATURE cpu_to_le16(0xAA55) + +#define PARTITION_STATUS_NONBOOTABLE 0x00 /* Non-bootable */ +#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */ + +#define PARTITION_TYPE_EMPTY 0x00 /* Empty */ +#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */ +#define PARTITION_TYPE_NTFS 0x07 /* Windows NT NTFS */ +#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */ + +/* Forward declarations */ +struct _ntfs_file_state; +struct _ntfs_dir_state; + +/** + * PRIMARY_PARTITION - Block device partition record + */ +typedef struct _PARTITION_RECORD { + u8 status; /* Partition status; see above */ + u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ + u8 type; /* Partition type; see above */ + u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ + u32 lba_start; /* Local block address to first sector of partition */ + u32 block_count; /* Number of blocks in partition */ +} __attribute__((__packed__)) PARTITION_RECORD; + +/** + * MASTER_BOOT_RECORD - Block device master boot record + */ +typedef struct _MASTER_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partitions[4]; /* 4 primary partitions */ + u16 signature; /* MBR signature; 0xAA55 */ +} __attribute__((__packed__)) MASTER_BOOT_RECORD; + +/** + * EXTENDED_PARTITION - Block device extended boot record + */ +typedef struct _EXTENDED_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partition; /* Primary partition */ + PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */ + u8 reserved[32]; /* Normally empty */ + u16 signature; /* EBR signature; 0xAA55 */ +} __attribute__((__packed__)) EXTENDED_BOOT_RECORD; + +/** + * INTERFACE_ID - Disc interface identifier + */ +typedef struct _INTERFACE_ID { + const char *name; /* Interface name */ + const DISC_INTERFACE *interface; /* Disc interface */ +} INTERFACE_ID; + +/** + * ntfs_atime_t - File access time update strategies + */ +typedef enum { + ATIME_ENABLED, /* Update access times */ + ATIME_DISABLED /* Don't update access times */ +} ntfs_atime_t; + +/** + * ntfs_vd - NTFS volume descriptor + */ +typedef struct _ntfs_vd { + struct ntfs_device *dev; /* NTFS device handle */ + ntfs_volume *vol; /* NTFS volume handle */ + mutex_t lock; /* Volume lock mutex */ + s64 id; /* Filesystem id */ + u32 flags; /* Mount flags */ + char name[128]; /* Volume name (cached) */ + u16 uid; /* User id for entry creation */ + u16 gid; /* Group id for entry creation */ + u16 fmask; /* Unix style permission mask for file creation */ + u16 dmask; /* Unix style permission mask for directory creation */ + ntfs_atime_t atime; /* Entry access time update strategy */ + bool showHiddenFiles; /* If true, show hidden files when enumerating directories */ + bool showSystemFiles; /* If true, show system files when enumerating directories */ + ntfs_inode *cwd_ni; /* Current directory */ + struct _ntfs_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */ + struct _ntfs_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */ + u16 openDirCount; /* The total number of directories currently open in this volume */ + u16 openFileCount; /* The total number of files currently open in this volume */ +} ntfs_vd; + +/* Lock volume */ +static inline void ntfsLock (ntfs_vd *vd) +{ + LWP_MutexLock(vd->lock); +} + +/* Unlock volume */ +static inline void ntfsUnlock (ntfs_vd *vd) +{ + LWP_MutexUnlock(vd->lock); +} + +/* Gekko device related routines */ +int ntfsAddDevice (const char *name, void *deviceData); +void ntfsRemoveDevice (const char *path); +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice); +const devoptab_t *ntfsGetDevOpTab (void); +const INTERFACE_ID* ntfsGetDiscInterfaces (void); + +/* Miscellaneous helper/support routines */ +int ntfsInitVolume (ntfs_vd *vd); +void ntfsDeinitVolume (ntfs_vd *vd); +ntfs_vd *ntfsGetVolume (const char *path); +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path); +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel); +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni); +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target); +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path); +int ntfsUnlink (ntfs_vd *vd, const char *path); +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni); +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st); +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask); + +const char *ntfsRealPath (const char *path); +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len); +int ntfsLocalToUnicode (const char *ins, ntfschar **outs); + +#endif /* _NTFSINTERNAL_H */ diff --git a/lib/libntfs/orig/source/ntfstime.h b/lib/libntfs/orig/source/ntfstime.h new file mode 100644 index 0000000..426269d --- /dev/null +++ b/lib/libntfs/orig/source/ntfstime.h @@ -0,0 +1,121 @@ +/* + * ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_NTFSTIME_H +#define _NTFS_NTFSTIME_H + +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_GETTIMEOFDAY +#include +#endif + +#include "types.h" + +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + +#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) + +/** + * ntfs2timespec - Convert an NTFS time to Unix time + * @ntfs_time: An NTFS time in 100ns units since 1601 + * + * NTFS stores times as the number of 100ns intervals since January 1st 1601 at + * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. + * + * Return: A Unix time (number of seconds since 1970, and nanoseconds) + */ +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) +{ + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); +} + +/** + * timespec2ntfs - Convert Linux time to NTFS time + * @utc_time: Linux time to convert to NTFS + * + * Convert the Linux time @utc_time to its corresponding NTFS time. + * + * Linux stores time in a long at present and measures it as the number of + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value + * + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is + * measured as the number of 100 nano-second intervals since 1st January 1601, + * 00:00:00 UTC. + * + * Return: An NTFS time (100ns units since Jan 1601) + */ +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) +{ + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); +} + +#endif /* _NTFS_NTFSTIME_H */ diff --git a/lib/libntfs/orig/source/object_id.c b/lib/libntfs/orig/source/object_id.c new file mode 100644 index 0000000..8799ddb --- /dev/null +++ b/lib/libntfs/orig/source/object_id.c @@ -0,0 +1,641 @@ +/** + * object_id.c - Processing of object ids + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "object_id.h" +#include "logging.h" +#include "misc.h" + +/* + * Endianness considerations + * + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. + * + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. + * + * --------------------- begin from RFC 4122 ---------------------- + * Consider each field of the UUID to be an unsigned integer as shown + * in the table in section Section 4.1.2. Then, to compare a pair of + * UUIDs, arithmetically compare the corresponding fields from each + * UUID in order of significance and according to their data type. + * Two UUIDs are equal if and only if all the corresponding fields + * are equal. + * + * UUIDs, as defined in this document, can also be ordered + * lexicographically. For a pair of UUIDs, the first one follows the + * second if the most significant field in which the UUIDs differ is + * greater for the first UUID. The second precedes the first if the + * most significant field in which the UUIDs differ is greater for + * the second UUID. + * + * The fields are encoded as 16 octets, with the sizes and order of the + * fields defined above, and with each field encoded with the Most + * Significant Byte first (known as network byte order). Note that the + * field names, particularly for multiplexed fields, follow historical + * practice. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_low | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_mid | time_hi_and_version | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |clk_seq_hi_res | clk_seq_low | node (0-1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | node (2-5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ---------------------- end from RFC 4122 ----------------------- + */ + +typedef struct { + union { + /* alignment may be needed to evaluate collations */ + u32 alignment; + GUID guid; + } object_id; +} OBJECT_ID_INDEX_KEY; + +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; +} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA + +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; +} ; + +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for a new object id + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) +{ + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); + + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open the $Extend/$ObjId file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Merge object_id data stored in the index into + * a full object_id struct. + * + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error + */ + +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; + + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove an object id index entry if attribute present + * + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno + */ + +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + size = sizeof(GUID); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + size = sizeof(OBJECT_ID_ATTR); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the object id and index + * + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) +{ + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; + + res = 0; + + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Add a (dummy) object id to an inode if it does not exist + * + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) + */ + +static int add_object_id(ntfs_inode *ni, int flags) +{ + int res; + u8 dummy; + + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete an object_id index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_object_id_index(ntfs_inode *ni) +{ + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs object id into an extended attribute + * + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) + * + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) +{ + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; + + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); +} + +/* + * Set the object id from an extended attribute + * + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; + + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the object id + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; + + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } + + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/orig/source/object_id.h b/lib/libntfs/orig/source/object_id.h new file mode 100644 index 0000000..31af9fd --- /dev/null +++ b/lib/libntfs/orig/source/object_id.h @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); + +int ntfs_delete_object_id_index(ntfs_inode *ni); + +#endif /* OBJECT_ID_H */ diff --git a/lib/libntfs/orig/source/param.h b/lib/libntfs/orig/source/param.h new file mode 100644 index 0000000..7420c79 --- /dev/null +++ b/lib/libntfs/orig/source/param.h @@ -0,0 +1,101 @@ +/* + * param.h - Parameter values for ntfs-3g + * + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_PARAM_H +#define _NTFS_PARAM_H + +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ +#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ + +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +/* + * Parameters for compression + */ + + /* default option for compression */ +#define DEFAULT_COMPRESSION FALSE + /* (log2 of) number of clusters in a compression block for new files */ +#define STANDARD_COMPRESSION_UNIT 4 + /* maximum cluster size for allowing compression for new files */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +/* + * Parameters for runlists + */ + + /* only update the final extent of a runlist when appending data */ +#define PARTIAL_RUNLIST_UPDATING 0 + +/* + * Parameters for user and xattr mappings + */ + +#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */ + + +/* + * Permission checking modes for high level and low level + * + * The choices for high and low lowel are independent, they have + * no effect on the library + * + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security with internal fuse or external + * fuse older than 2.8 + * + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs + * + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs + * + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. + */ + +#define HPERMSCONFIG 1 +#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) +#define LPERMSCONFIG 5 +#else +#define LPERMSCONFIG 3 +#endif + +#endif /* defined _NTFS_PARAM_H */ diff --git a/lib/libntfs/orig/source/reparse.c b/lib/libntfs/orig/source/reparse.c new file mode 100644 index 0000000..05490bf --- /dev/null +++ b/lib/libntfs/orig/source/reparse.c @@ -0,0 +1,1227 @@ +/** + * reparse.c - Processing of reparse points + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" +#include "reparse.h" + +/* the definitions in layout.h are wrong, we use names defined in + http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx +*/ + +#define IO_REPARSE_TAG_DFS const_cpu_to_le32(0x8000000A) +#define IO_REPARSE_TAG_DFSR const_cpu_to_le32(0x80000012) +#define IO_REPARSE_TAG_HSM const_cpu_to_le32(0xC0000004) +#define IO_REPARSE_TAG_HSM2 const_cpu_to_le32(0x80000006) +#define IO_REPARSE_TAG_MOUNT_POINT const_cpu_to_le32(0xA0000003) +#define IO_REPARSE_TAG_SIS const_cpu_to_le32(0x80000007) +#define IO_REPARSE_TAG_SYMLINK const_cpu_to_le32(0xA000000C) + +struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + le32 flags; /* 1 for full target, otherwise 0 */ + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ + INDEX_ENTRY_HEADER header; + REPARSE_INDEX_KEY key; + le32 filling; +} ; + +static const ntfschar dir_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\') +} ; + +static const ntfschar vol_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\'), + const_cpu_to_le16('V'), + const_cpu_to_le16('o'), + const_cpu_to_le16('l'), + const_cpu_to_le16('u'), + const_cpu_to_le16('m'), + const_cpu_to_le16('e'), + const_cpu_to_le16('{'), +} ; + +static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('R') }; + +static const char mappingdir[] = ".NTFS-3G/"; + +/* + * Fix a file name with doubtful case in some directory index + * and return the name with the casing used in directory. + * + * Should only be used to translate paths stored with case insensitivity + * (such as directory junctions) when no case conflict is expected. + * If there some ambiguity, the name which collates first is returned. + * + * The name is converted to upper case and searched the usual way. + * The collation rules for file names are such that we should get the + * first candidate if any. + */ + +static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, + int uname_len) +{ + ntfs_volume *vol = dir_ni->vol; + ntfs_index_context *icx; + u64 mref; + le64 lemref; + int lkup; + int olderrno; + int i; + u32 cpuchar; + INDEX_ENTRY *entry; + FILE_NAME_ATTR *found; + struct { + FILE_NAME_ATTR attr; + ntfschar file_name[NTFS_MAX_NAME_LEN + 1]; + } find; + + mref = (u64)-1; /* default return (not found) */ + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + if (uname_len > NTFS_MAX_NAME_LEN) + uname_len = NTFS_MAX_NAME_LEN; + find.attr.file_name_length = uname_len; + for (i=0; iupcase_len) + && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar)) + find.attr.file_name[i] = vol->upcase[cpuchar]; + else + find.attr.file_name[i] = uname[i]; + } + olderrno = errno; + lkup = ntfs_index_lookup((char*)&find, uname_len, icx); + if (errno == ENOENT) + errno = olderrno; + /* + * We generally only get the first matching candidate, + * so we still have to check whether this is a real match + */ + if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END)) + /* get next entry if reaching end of block */ + entry = ntfs_index_next(icx->entry, icx); + else + entry = icx->entry; + if (entry) { + found = &entry->key.file_name; + if (lkup + && ntfs_names_are_equal(find.attr.file_name, + find.attr.file_name_length, + found->file_name, found->file_name_length, + IGNORE_CASE, + vol->upcase, vol->upcase_len)) + lkup = 0; + if (!lkup) { + /* + * name found : + * fix original name and return inode + */ + lemref = entry->indexed_file; + mref = le64_to_cpu(lemref); + if (NVolCaseSensitive(vol) || !vol->locase) { + for (i=0; ifile_name_length; i++) + uname[i] = found->file_name[i]; + } else { + for (i=0; ifile_name_length; i++) + uname[i] = vol->locase[found->file_name[i]]; + } + } + } + ntfs_index_ctx_put(icx); + } + return (mref); +} + +/* + * Search for a directory junction or a symbolic link + * along the target path, with target defined as a full absolute path + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_absolute(ntfs_volume *vol, ntfschar *path, + int count, BOOL isdir) +{ + ntfs_inode *ni; + u64 inum; + char *target; + int start; + int len; + + target = (char*)NULL; /* default return */ + ni = ntfs_inode_open(vol, (MFT_REF)FILE_root); + if (ni) { + start = 0; + do { + len = 0; + while (((start + len) < count) + && (path[start + len] != const_cpu_to_le16('\\'))) + len++; + inum = ntfs_fix_file_name(ni, &path[start], len); + ntfs_inode_close(ni); + ni = (ntfs_inode*)NULL; + if (inum != (u64)-1) { + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + start += len; + if (start < count) + path[start++] = const_cpu_to_le16('/'); + } + } while (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && (start < count)); + if (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)) + if (ntfs_ucstombs(path, count, &target, 0) < 0) { + if (target) { + free(target); + target = (char*)NULL; + } + } + if (ni) + ntfs_inode_close(ni); + } + return (target); +} + +/* + * Search for a symbolic link along the target path, + * with the target defined as a relative path + * + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) +{ + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + int max = 32; /* safety */ + + pos = 0; + ok = TRUE; + curni = ntfs_dir_parent_inode(ni); + while (curni && ok && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + } else { + pos += lth; + if ((ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; + } + } + } + } + } + } + + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; + } + return (target); +} + +/* + * Check whether a drive letter has been defined in .NTFS-3G + * + * Returns 1 if found, + * 0 if not found, + * -1 if there was an error (described by errno) + */ + +static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) +{ + char defines[NTFS_MAX_NAME_LEN + 5]; + char *drive; + int ret; + int sz; + int olderrno; + ntfs_inode *ni; + + ret = -1; + drive = (char*)NULL; + sz = ntfs_ucstombs(&letter, 1, &drive, 0); + if (sz > 0) { + strcpy(defines,mappingdir); + if ((*drive >= 'a') && (*drive <= 'z')) + *drive += 'A' - 'a'; + strcat(defines,drive); + strcat(defines,":"); + olderrno = errno; + ni = ntfs_pathname_to_inode(vol, NULL, defines); + if (ni && !ntfs_inode_close(ni)) + ret = 1; + else + if (errno == ENOENT) { + ret = 0; + /* avoid errno pollution */ + errno = olderrno; + } + } + if (drive) + free(drive); + return (ret); +} + +/* + * Do some sanity checks on reparse data + * + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + * + */ + +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) +{ + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; + + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); +} + +/* + * Check and translate the target of a junction point or + * a full absolute symbolic link. + * + * A full target definition begins with "\??\" or "\\?\" + * + * The fully defined target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a valid directory junction we want \??\x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 7) + && !memcmp(junction,dir_junction_head,8) + && junction[4] + && (junction[5] == const_cpu_to_le16(':')) + && (junction[6] == const_cpu_to_le16('\\'))) + kind = DIR_JUNCTION; + else + /* + * For a valid volume junction we want \\?\Volume{ + * and a final \ (where \ is an individual char) + */ + if ((count >= 12) + && !memcmp(junction,vol_junction_head,22) + && (junction[count-1] == const_cpu_to_le16('\\'))) + kind = VOL_JUNCTION; + else + kind = NO_JUNCTION; + /* + * Directory junction with an explicit path and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume + */ + if ((kind == DIR_JUNCTION) + && (count >= 7) + && junction[7] + && !ntfs_drive_letter(vol, junction[4])) { + target = search_absolute(vol,&junction[7],count - 7, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * Volume junctions or directory junctions with + * target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if (((kind == DIR_JUNCTION) && !fulltarget) + || (kind == VOL_JUNCTION)) { + sz = ntfs_ucstombs(&junction[4], + (kind == VOL_JUNCTION ? count - 5 : count - 4), + &target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of an absolute symbolic link. + * + * An absolute target definition begins with "\" or "x:\" + * + * The absolute target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a full valid path we want x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 3) + && junction[0] + && (junction[1] == const_cpu_to_le16(':')) + && (junction[2] == const_cpu_to_le16('\\'))) + kind = FULL_PATH; + else + /* + * For an absolute path we want an initial \ + */ + if ((count >= 0) + && (junction[0] == const_cpu_to_le16('\\'))) + kind = ABS_PATH; + else + kind = REJECTED_PATH; + /* + * Full path, with a drive letter and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume. + * Do the same for an abs path with no drive letter. + */ + if (((kind == FULL_PATH) + && (count >= 3) + && junction[3] + && !ntfs_drive_letter(vol, junction[0])) + || (kind == ABS_PATH)) { + if (kind == ABS_PATH) + target = search_absolute(vol, &junction[1], + count - 1, isdir); + else + target = search_absolute(vol, &junction[3], + count - 3, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * full path with target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if ((kind == FULL_PATH) && !fulltarget) { + sz = ntfs_ucstombs(&junction[0], + count,&target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of a relative symbolic link. + * + * A relative target definition does not begin with "\" + * + * The original definition of relative target is kept, it is just + * translated to a case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) +{ + char *target; + + target = search_relative(ni,junction,count); + return (target); +} + +/* + * Get the target for a junction point or symbolic link + * Should only be called for files or directories with reparse data + * + * returns the target converted to a relative path, or NULL + * if some error occurred, as described by errno + * errno is EOPNOTSUPP if the reparse point is not a valid + * symbolic link or directory junction + */ + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size) +{ + s64 attr_size = 0; + char *target; + unsigned int offs; + unsigned int lth; + ntfs_volume *vol; + REPARSE_POINT *reparse_attr; + struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + struct SYMLINK_REPARSE_DATA *symlink_data; + enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; + ntfschar *p; + BOOL bad; + BOOL isdir; + + target = (char*)NULL; + bad = TRUE; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + vol = ni->vol; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + p = (ntfschar*)&symlink_data->path_buffer[offs]; + /* + * Predetermine the kind of target, + * the called function has to make a full check + */ + if (*p++ == const_cpu_to_le16('\\')) { + if ((*p == const_cpu_to_le16('?')) + || (*p == const_cpu_to_le16('\\'))) + kind = FULL_TARGET; + else + kind = ABS_TARGET; + } else + if (*p == const_cpu_to_le16(':')) + kind = ABS_TARGET; + else + kind = REL_TARGET; + p--; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; + } + break; + } + free(reparse_attr); + } + *pattr_size = attr_size; + if (bad) + errno = EOPNOTSUPP; + return (target); +} + +/* + * Check whether a reparse point looks like a junction point + * or a symbolic link. + * Should only be called for files or directories with reparse data + * + * The validity of the target is not checked. + */ + +BOOL ntfs_possible_symlink(ntfs_inode *ni) +{ + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + BOOL possible; + + possible = FALSE; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + case IO_REPARSE_TAG_SYMLINK : + possible = TRUE; + default : ; + } + free(reparse_attr); + } + return (possible); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for new reparse data + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, + le32 reparse_tag) +{ + struct REPARSE_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(REPARSE_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16(0); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct REPARSE_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(REPARSE_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = const_cpu_to_le32(0); + ntfs_index_ctx_reinit(xr); + return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove a reparse data index entry if attribute present + * + * Returns the size of existing reparse data + * (the existing reparse tag is returned) + * -1 if failure, explained by errno + */ + +static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, + le32 *preparse_tag) +{ + REPARSE_INDEX_KEY key; + u64 file_id_cpu; + le64 file_id; + s64 size; + le16 seqn; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing reparse_tag */ + size = ntfs_attr_pread(na, 0, 4, preparse_tag); + if (size == 4) { + seqn = na->ni->mrec->sequence_number; + file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +/* + * Open the $Extend/$Reparse file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_reparse_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xr; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); + if (!xr) { + ntfs_inode_close(ni); + } + } else + xr = (ntfs_index_context*)NULL; + return (xr); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the reparse data and index + * + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, + const char *value, size_t size) +{ + int res; + int written; + int oldsize; + ntfs_attr *na; + le32 reparse_tag; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* remove the existing reparse data */ + oldsize = remove_reparse_index(na,xr,&reparse_tag); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + if (!res + && set_reparse_index(ni,xr, + ((const REPARSE_POINT*)value)->reparse_tag) + && (oldsize > 0)) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index reparse data." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete a reparse index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_reparse_index(ntfs_inode *ni) +{ + ntfs_index_context *xr; + ntfs_inode *xrni; + ntfs_attr *na; + le32 reparse_tag; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr,&reparse_tag) < 0) + res = -1; + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs reparse data into an extended attribute + * + * Returns the reparse data size + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) +{ + REPARSE_POINT *reparse_attr; + s64 attr_size; + + attr_size = 0; /* default to no data and no error */ + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,reparse_attr, + attr_size); + else + errno = EINVAL; + } + free(reparse_attr); + } + } else + errno = ENODATA; + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Set the reparse data from an extended attribute + * + * Warning : the new data is not checked + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + int res; + u8 dummy; + ntfs_inode *xrni; + ntfs_index_context *xr; + + res = 0; + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + xr = open_reparse_index(ni->vol); + if (xr) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, + AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, + AT_REPARSE_POINT, + AT_UNNAMED,0,&dummy, + (s64)0); + if (!res) { + ni->flags |= + FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } else { + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; + } + } + if (!res) { + /* update value and index */ + res = update_reparse_data(ni,xr,value,size); + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the reparse data + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xrni; + ntfs_index_context *xr; + le32 reparse_tag; + + res = 0; + if (ni) { + /* + * open and delete the reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + AT_UNNAMED,0); + if (na) { + /* first remove index (reparse data needed) */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr, + &reparse_tag) < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (!res) { + ni->flags &= + ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } else { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_reparse_index(ni, xr, + reparse_tag); + ntfs_log_error( + "Failed to remove reparse data." + " Possible corruption.\n"); + } + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/orig/source/reparse.h b/lib/libntfs/orig/source/reparse.h new file mode 100644 index 0000000..35f4aa4 --- /dev/null +++ b/lib/libntfs/orig/source/reparse.h @@ -0,0 +1,39 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef REPARSE_H +#define REPARSE_H + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); +BOOL ntfs_possible_symlink(ntfs_inode *ni); + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); + +int ntfs_delete_reparse_index(ntfs_inode *ni); + +#endif /* REPARSE_H */ diff --git a/lib/libntfs/orig/source/runlist.c b/lib/libntfs/orig/source/runlist.c new file mode 100644 index 0000000..383a80b --- /dev/null +++ b/lib/libntfs/orig/source/runlist.c @@ -0,0 +1,2171 @@ +/** + * runlist.c - Run list handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "layout.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_rl_mm - runlist memmove + * @base: + * @dst: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mm(runlist_element *base, int dst, int src, int size) +{ + if ((dst != src) && (size > 0)) + memmove(base + dst, base + src, size * sizeof(*base)); +} + +/** + * ntfs_rl_mc - runlist memory copy + * @dstbase: + * @dst: + * @srcbase: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mc(runlist_element *dstbase, int dst, + runlist_element *srcbase, int src, int size) +{ + if (size > 0) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); +} + +/** + * ntfs_rl_realloc - Reallocate memory for runlists + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for + * + * As the runlists grow, more memory will be required. To prevent large + * numbers of small reallocations of memory, this function returns a 4kiB block + * of memory. + * + * N.B. If the new allocation doesn't require a different number of 4kiB + * blocks in memory, the function will return the original pointer. + * + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return NULL with errno set to the error code. + */ +static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, + int new_size) +{ + old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + if (old_size == new_size) + return rl; + return realloc(rl, new_size); +} + +/* + * Extend a runlist by some entry count + * The runlist may have to be reallocated + * + * Returns the reallocated runlist + * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails + */ + +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) +{ + runlist_element *newrl; + int last; + int irl; + + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else + na->rl = newrl; + rl = &newrl[irl]; + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } + return (rl); +} + +/** + * ntfs_rl_are_mergeable - test if two runlists can be joined together + * @dst: original runlist + * @src: new runlist to test for mergeability with @dst + * + * Test if two runlists can be joined together. For this, their VCNs and LCNs + * must be adjacent. + * + * Return: TRUE Success, the runlists can be merged. + * FALSE Failure, the runlists cannot be merged. + */ +static BOOL ntfs_rl_are_mergeable(runlist_element *dst, runlist_element *src) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " + "pointer!\n"); + return FALSE; + } + + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return TRUE; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) + return FALSE; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return TRUE; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return TRUE; + /* Cannot merge. */ + return FALSE; +} + +/** + * __ntfs_rl_merge - merge two runlists without testing if they can be merged + * @dst: original, destination runlist + * @src: new runlist to merge with @dst + * + * Merge the two runlists, writing into the destination runlist @dst. The + * caller must make sure the runlists can be merged or this will corrupt the + * destination runlist. + */ +static void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) +{ + dst->length += src->length; +} + +/** + * ntfs_rl_append - append a runlist after a given element + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: runlist to be inserted into @dst + * @ssize: number of elements in @src (excluding end marker) + * @loc: append the new runlist @src after this element in @dst + * + * Append the runlist @src after element @loc in @dst. Merge the right end of + * the new runlist, if necessary. Adjust the size of the hole before the + * appended runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL right = FALSE; /* Right end of @src needs merging */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, check if the right hand end needs merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the right hand end, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + + /* marker - First run after the @src runs that have been inserted */ + marker = loc + ssize + 1; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the preceding hole. */ + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + + /* We may have changed the length of the file, so fix the end marker */ + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; + + return dst; +} + +/** + * ntfs_rl_insert - insert a runlist into another + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: insert the new runlist @src before this element in @dst + * + * Insert the runlist @src before element @loc in the runlist @dst. Merge the + * left end of the new runlist, if necessary. Adjust the size of the hole + * after the inserted runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL disc = FALSE; /* Discontinuity between @dst and @src */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "notmapped" run. + */ + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; + + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; + + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); + } + + /* Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlist. + */ + + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); + + /* Adjust the VCN of the first run after the insertion ... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* ... and the length. */ + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; + + /* Writing beyond the end of the file and there's a discontinuity. */ + if (disc) { + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; + } + dst[loc].lcn = LCN_RL_NOT_MAPPED; + } + return dst; +} + +/** + * ntfs_rl_replace - overwrite a runlist element with another runlist + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst to overwrite with @src + * + * Replace the runlist element @dst at @loc with @src. Merge the left and + * right ends of the inserted runlist, if necessary. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, + runlist_element *src, int ssize, + int loc) +{ + signed delta; + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL right = FALSE; /* Right end of @src needs merging */ + int tail; /* Start of tail of @dst */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + if (loc > 0) + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + /* Allocate some space. We'll need less if the left, right, or both + * ends get merged. The -1 accounts for the run being replaced. + */ + delta = ssize - 1 - left - right; + if (delta > 0) { + dst = ntfs_rl_realloc(dst, dsize, dsize + delta); + if (!dst) + return NULL; + } + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the left and right ends, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * tail - Offset of the tail of @dst + * Nominally: @tail = @loc + 1 (location, skipping the replaced run) + * If "right", then one of @dst's runs is already merged into @src. + */ + tail = loc + right + 1; + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: @marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + */ + marker = loc + ssize - left; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, tail, dsize - tail); + ntfs_rl_mc(dst, loc, src, left, ssize - left); + + /* We may have changed the length of the file, so fix the end marker */ + if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + + return dst; +} + +/** + * ntfs_rl_split - insert a runlist into the centre of a hole + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst at which to split and insert @src + * + * Split the runlist @dst at @loc into two and insert @new in between the two + * fragments. No merging of runlists is necessary. Adjust the size of the + * holes either side. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (!dst) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; + + return dst; +} + + +/** + * ntfs_runlists_merge_i - see ntfs_runlists_merge + */ +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) +{ + int di, si; /* Current index into @[ds]rl. */ + int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ + int dins; /* Index into @drl at which to insert @srl. */ + int dend, send; /* Last index into @[ds]rl. */ + int dfinal, sfinal; /* The last index into @[ds]rl with + lcn >= LCN_HOLE. */ + int marker = 0; + VCN marker_vcn = 0; + + ntfs_log_debug("dst:\n"); + ntfs_debug_runlist_dump(drl); + ntfs_log_debug("src:\n"); + ntfs_debug_runlist_dump(srl); + + /* Check for silly calling... */ + if (!srl) + return drl; + + /* Check for the case where the first mapping is being done now. */ + if (!drl) { + drl = srl; + /* Complete the source runlist if necessary. */ + if (drl[0].vcn) { + /* Scan to the end of the source runlist. */ + for (dend = 0; drl[dend].length; dend++) + ; + dend++; + drl = ntfs_rl_realloc(drl, dend, dend + 1); + if (!drl) + return drl; + /* Insert start element at the front of the runlist. */ + ntfs_rl_mm(drl, 1, 0, dend); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; + } + goto finished; + } + + si = di = 0; + + /* Skip any unmapped start element(s) in the source runlist. */ + while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) + si++; + + /* Can't have an entirely unmapped source runlist. */ + if (!srl[si].length) { + errno = EINVAL; + ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); + return NULL; + } + + /* Record the starting points. */ + sstart = si; + + /* + * Skip forward in @drl until we reach the position where @srl needs to + * be inserted. If we reach the end of @drl, @srl just needs to be + * appended to @drl. + */ + for (; drl[di].length; di++) { + if (drl[di].vcn + drl[di].length > srl[sstart].vcn) + break; + } + dins = di; + + /* Sanity check for illegal overlaps. */ + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + errno = ERANGE; + ntfs_log_perror("Run lists overlap. Cannot merge"); + return NULL; + } + + /* Scan to the end of both runlists in order to know their sizes. */ + for (send = si; srl[send].length; send++) + ; + for (dend = di; drl[dend].length; dend++) + ; + + if (srl[send].lcn == (LCN)LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; + + /* Scan to the last element with lcn >= LCN_HOLE. */ + for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) + ; + for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) + ; + + { + BOOL start; + BOOL finish; + int ds = dend + 1; /* Number of elements in drl & srl */ + int ss = sfinal - sstart + 1; + + start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ + (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ + finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ + ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ + (srl[send - 1].vcn + srl[send - 1].length))); + + /* Or we'll lose an end marker */ + if (finish && !drl[dins].length) + ss++; + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) + finish = FALSE; + + ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); + ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); + ntfs_log_debug("start = %i, finish = %i\n", start, finish); + ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); + + if (start) { + if (finish) + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); + } else { + if (finish) + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); + } + if (!drl) { + ntfs_log_perror("Merge failed"); + return drl; + } + free(srl); + if (marker) { + ntfs_log_debug("Triggering marker code.\n"); + for (ds = dend; drl[ds].length; ds++) + ; + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; + + if (drl[ds].vcn == marker_vcn) { + ntfs_log_debug("Old marker = %lli, replacing with " + "LCN_ENOENT.\n", + (long long)drl[ds].lcn); + drl[ds].lcn = (LCN)LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped runlist element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == (LCN)LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { + /* Add an unmapped runlist element. */ + if (!slots) { + /* FIXME/TODO: We need to have the + * extra memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 2); + if (!drl) + goto critical_error; + slots = 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + /* FIXME/TODO: We need to have the extra + * memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 1); + if (!drl) + goto critical_error; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = (LCN)LCN_ENOENT; + drl[ds].length = (s64)0; + } + } + } + +finished: + /* The merge was completed successfully. */ + ntfs_log_debug("Merged runlist:\n"); + ntfs_debug_runlist_dump(drl); + return drl; + +critical_error: + /* Critical error! We cannot afford to fail here. */ + ntfs_log_perror("libntfs: Critical error"); + ntfs_log_debug("Forcing segmentation fault!\n"); + marker_vcn = ((runlist*)NULL)->lcn; + return drl; +} + +/** + * ntfs_runlists_merge - merge two runlists into one + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. + */ +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) +{ + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; +} + +/** + * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist + * @vol: ntfs volume on which the attribute resides + * @attr: attribute record whose mapping pairs array to decompress + * @old_rl: optional runlist in which to insert @attr's runlist + * + * Decompress the attribute @attr's mapping pairs array into a runlist. On + * success, return the decompressed runlist. + * + * If @old_rl is not NULL, decompressed runlist is inserted into the + * appropriate place in @old_rl and the resultant, combined runlist is + * returned. The original @old_rl is deallocated. + * + * On error, return NULL with errno set to the error code. @old_rl is left + * unmodified in that case. + * + * The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EIO Corrupt runlist. + * EINVAL Invalid parameters were passed in. + * ERANGE The two runlists overlap. + * + * FIXME: For now we take the conceptionally simplest approach of creating the + * new runlist disregarding the already existing one and then splicing the + * two into one, if that is possible (we check for overlap and discard the new + * runlist if overlap present before returning NULL, with errno = ERANGE). + */ +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + VCN vcn; /* Current vcn. */ + LCN lcn; /* Current lcn. */ + s64 deltaxcn; /* Change in [vl]cn. */ + runlist_element *rl; /* The output runlist. */ + const u8 *buf; /* Current position in mapping pairs array. */ + const u8 *attr_end; /* End of attribute. */ + int err, rlsize; /* Size of runlist buffer. */ + u16 rlpos; /* Current runlist position in units of + runlist_elements. */ + u8 b; /* Current byte offset in buf. */ + + ntfs_log_trace("Entering for attr 0x%x.\n", + (unsigned)le32_to_cpu(attr->type)); + /* Make sure attr exists and is non-resident. */ + if (!attr || !attr->non_resident || + sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { + errno = EINVAL; + return NULL; + } + /* Start at vcn = lowest_vcn and lcn 0. */ + vcn = sle64_to_cpu(attr->lowest_vcn); + lcn = 0; + /* Get start of the mapping pairs array. */ + buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); + attr_end = (const u8*)attr + le32_to_cpu(attr->length); + if (buf < (const u8*)attr || buf > attr_end) { + ntfs_log_debug("Corrupt attribute.\n"); + errno = EIO; + return NULL; + } + /* Current position in runlist array. */ + rlpos = 0; + /* Allocate first 4kiB block and set current runlist size to 4kiB. */ + rlsize = 0x1000; + rl = ntfs_malloc(rlsize); + if (!rl) + return NULL; + /* Insert unmapped starting element if necessary. */ + if (vcn) { + rl->vcn = (VCN)0; + rl->lcn = (LCN)LCN_RL_NOT_MAPPED; + rl->length = vcn; + rlpos++; + } + while (buf < attr_end && *buf) { + /* + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. + */ + if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { + runlist_element *rl2; + + rlsize += 0x1000; + rl2 = realloc(rl, rlsize); + if (!rl2) { + int eo = errno; + free(rl); + errno = eo; + return NULL; + } + rl = rl2; + } + /* Enter the current vcn into the current runlist element. */ + rl[rlpos].vcn = vcn; + /* + * Get the change in vcn, i.e. the run length in clusters. + * Doing it this way ensures that we signextend negative values. + * A negative run length doesn't make any sense, but hey, I + * didn't make up the NTFS specs and Windows NT4 treats the run + * length as a signed value so that's how it is... + */ + b = *buf & 0xf; + if (b) { + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + } else { /* The length entry is compulsory. */ + ntfs_log_debug("Missing length entry in mapping pairs " + "array.\n"); + deltaxcn = (s64)-1; + } + /* + * Assume a negative length to indicate data corruption and + * hence clean-up and return NULL. + */ + if (deltaxcn < 0) { + ntfs_log_debug("Invalid length in mapping pairs array.\n"); + goto err_out; + } + /* + * Enter the current run length into the current runlist + * element. + */ + rl[rlpos].length = deltaxcn; + /* Increment the current vcn by the current run length. */ + vcn += deltaxcn; + /* + * There might be no lcn change at all, as is the case for + * sparse clusters on NTFS 3.0+, in which case we set the lcn + * to LCN_HOLE. + */ + if (!(*buf & 0xf0)) + rl[rlpos].lcn = (LCN)LCN_HOLE; + else { + /* Get the lcn change which really can be negative. */ + u8 b2 = *buf & 0xf; + b = b2 + ((*buf >> 4) & 0xf); + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b > b2; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + /* Change the current lcn to it's new value. */ + lcn += deltaxcn; +#ifdef DEBUG + /* + * On NTFS 1.2-, apparently can have lcn == -1 to + * indicate a hole. But we haven't verified ourselves + * whether it is really the lcn or the deltaxcn that is + * -1. So if either is found give us a message so we + * can investigate it further! + */ + if (vol->major_ver < 3) { + if (deltaxcn == (LCN)-1) + ntfs_log_debug("lcn delta == -1\n"); + if (lcn == (LCN)-1) + ntfs_log_debug("lcn == -1\n"); + } +#endif + /* Check lcn is not below -1. */ + if (lcn < (LCN)-1) { + ntfs_log_debug("Invalid LCN < -1 in mapping pairs " + "array.\n"); + goto err_out; + } + /* Enter the current lcn into the runlist element. */ + rl[rlpos].lcn = lcn; + } + /* Get to the next runlist element. */ + rlpos++; + /* Increment the buffer position to the next mapping pair. */ + buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; + } + if (buf >= attr_end) + goto io_error; + /* + * If there is a highest_vcn specified, it must be equal to the final + * vcn in the runlist - 1, or something has gone badly wrong. + */ + deltaxcn = sle64_to_cpu(attr->highest_vcn); + if (deltaxcn && vcn - 1 != deltaxcn) { +mpa_err: + ntfs_log_debug("Corrupt mapping pairs array in non-resident " + "attribute.\n"); + goto err_out; + } + /* Setup not mapped runlist element if this is the base extent. */ + if (!attr->lowest_vcn) { + VCN max_cluster; + + max_cluster = ((sle64_to_cpu(attr->allocated_size) + + vol->cluster_size - 1) >> + vol->cluster_size_bits) - 1; + /* + * A highest_vcn of zero means this is a single extent + * attribute so simply terminate the runlist with LCN_ENOENT). + */ + if (deltaxcn) { + /* + * If there is a difference between the highest_vcn and + * the highest cluster, the runlist is either corrupt + * or, more likely, there are more extents following + * this one. + */ + if (deltaxcn < max_cluster) { + ntfs_log_debug("More extents to follow; deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - deltaxcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + rlpos++; + } else if (deltaxcn > max_cluster) { + ntfs_log_debug("Corrupt attribute. deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + goto mpa_err; + } + } + rl[rlpos].lcn = (LCN)LCN_ENOENT; + } else /* Not the base extent. There may be more extents to follow. */ + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + + /* Setup terminating runlist element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; + /* If no existing runlist was specified, we are done. */ + if (!old_rl) { + ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); + ntfs_debug_runlist_dump(rl); + return rl; + } + /* Now combine the new and old runlists checking for overlaps. */ + old_rl = ntfs_runlists_merge(old_rl, rl); + if (old_rl) + return old_rl; + err = errno; + free(rl); + ntfs_log_debug("Failed to merge runlists.\n"); + errno = err; + return NULL; +io_error: + ntfs_log_debug("Corrupt attribute.\n"); +err_out: + free(rl); + errno = EIO; + return NULL; +} + +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; +} + +/** + * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist + * @rl: runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @rl to map vcns to their + * corresponding lcns. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ================================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been + * inserted into the runlist yet. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + */ +LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) +{ + int i; + + if (vcn < (VCN)0) + return (LCN)LCN_EINVAL; + /* + * If rl is NULL, assume that we have found an unmapped runlist. The + * caller can then attempt to map it and fail appropriately if + * necessary. + */ + if (!rl) + return (LCN)LCN_RL_NOT_MAPPED; + + /* Catch out of lower bounds vcn. */ + if (vcn < rl[0].vcn) + return (LCN)LCN_ENOENT; + + for (i = 0; rl[i].length; i++) { + if (vcn < rl[i+1].vcn) { + if (rl[i].lcn >= (LCN)0) + return rl[i].lcn + (vcn - rl[i].vcn); + return rl[i].lcn; + } + } + /* + * The terminator element is setup to the correct value, i.e. one of + * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. + */ + if (rl[i].lcn < (LCN)0) + return rl[i].lcn; + /* Just in case... We could replace this with BUG() some day. */ + return (LCN)LCN_ENOENT; +} + +/** + * ntfs_rl_pread - gather read from disk + * @vol: ntfs volume to read from + * @rl: runlist specifying where to read the data from + * @pos: byte position within runlist @rl at which to begin the read + * @count: number of bytes to read + * @b: data buffer into which to read from disk + * + * This function will read @count bytes from the volume @vol to the data buffer + * @b gathering the data as specified by the runlist @rl. The read begins at + * offset @pos into the runlist @rl. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + * + * NOTE: If we encounter EOF while reading we return EIO because we assume that + * the run list must point to valid locations within the ntfs volume. + */ +s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b) +{ + s64 bytes_read, to_read, ofs, total; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to read runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + return -1; + } + if (!count) + return count; + /* Seek in @rl to the run containing @pos. */ + for (ofs = 0; rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos); rl++) + ofs += (rl->length << vol->cluster_size_bits); + /* Offset in the run at which to begin reading. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + /* It is a hole. Just fill buffer @b with zeroes. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update counters and proceed with next run. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it from the volume. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + bytes_read = ntfs_pread(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (bytes_read > 0) { + total += bytes_read; + count -= bytes_read; + b = (u8*)b + bytes_read; + continue; + } + /* If the syscall was interrupted, try again. */ + if (bytes_read == (s64)-1 && errno == EINTR) + goto retry; + if (bytes_read == (s64)-1) + err = errno; + goto rl_err_out; + } + /* Finally, return the number of bytes read. */ + return total; +rl_err_out: + if (total) + return total; + errno = err; + return -1; +} + +/** + * ntfs_rl_pwrite - scatter write to disk + * @vol: ntfs volume to write to + * @rl: runlist entry specifying where to write the data to + * @ofs: offset in file for runlist element indicated in @rl + * @pos: byte position from runlist beginning at which to begin the write + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the volume @vol + * scattering the data as specified by the runlist @rl. The write begins at + * offset @pos into the runlist @rl. If a run is sparse then the related buffer + * data is ignored which means that the caller must ensure they are consistent. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case + * of invalid arguments. + */ +s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b) +{ + s64 written, to_write, total = 0; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to write runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + goto errno_set; + } + if (!count) + goto out; + /* Seek in @rl to the run containing @pos. */ + while (rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos)) { + ofs += (rl->length << vol->cluster_size_bits); + rl++; + } + /* Offset in the run at which to begin writing. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + + to_write = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + + total += to_write; + count -= to_write; + b = (u8*)b + to_write; + continue; + } + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + if (!NVolReadOnly(vol)) + written = ntfs_pwrite(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, + to_write, b); + else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + b = (u8*)b + written; + continue; + } + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (written == (s64)-1) + err = errno; + goto rl_err_out; + } +out: + return total; +rl_err_out: + if (total) + goto out; + errno = err; +errno_set: + total = -1; + goto out; +} + +/** + * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number + * @n: number for which to get the number of bytes for + * + * Return the number of bytes required to store @n unambiguously as + * a signed number. + * + * This is used in the context of the mapping pairs array to determine how + * many bytes will be needed in the array to store a given logical cluster + * number (lcn) or a specific run length. + * + * Return the number of bytes written. This function cannot fail. + */ +int ntfs_get_nr_significant_bytes(const s64 n) +{ + u64 l; + int i; + + l = (n < 0 ? ~n : n); + i = 1; + if (l >= 128) { + l >>= 7; + do { + i++; + l >>= 8; + } while (l); + } + return i; +} + +/** + * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array + * @vol: ntfs volume (needed for the ntfs version) + * @rl: runlist for which to determine the size of the mapping pairs + * @start_vcn: vcn at which to start the mapping pairs array + * + * Walk the runlist @rl and calculate the size in bytes of the mapping pairs + * array corresponding to the runlist @rl, starting at vcn @start_vcn. This + * for example allows us to allocate a buffer of the right size when building + * the mapping pairs array. + * + * If @rl is NULL, just return 1 (for the single terminator byte). + * + * Return the calculated size in bytes on success. On error, return -1 with + * errno set to the error code. The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + */ +int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size) +{ + LCN prev_lcn; + int rls; + + if (start_vcn < 0) { + ntfs_log_trace("start_vcn %lld (should be >= 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + if (!rl) { + if (start_vcn) { + ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + rls = 1; + goto out; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { + errno = EINVAL; + goto errno_set; + } + prev_lcn = 0; + /* Always need the terminating zero byte. */ + rls = 1; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(prev_lcn); + } + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && (rls <= max_size); rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(rl->lcn - + prev_lcn); + prev_lcn = rl->lcn; + } + } +out: + return rls; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + rls = -1; + goto out; +} + +/** + * ntfs_write_significant_bytes - write the significant bytes of a number + * @dst: destination buffer to write to + * @dst_max: pointer to last byte of destination buffer for bounds checking + * @n: number whose significant bytes to write + * + * Store in @dst, the minimum bytes of the number @n which are required to + * identify @n unambiguously as a signed number, taking care not to exceed + * @dest_max, the maximum position within @dst to which we are allowed to + * write. + * + * This is used when building the mapping pairs array of a runlist to compress + * a given logical cluster number (lcn) or a specific run length to the minimum + * size possible. + * + * Return the number of bytes written on success. On error, i.e. the + * destination buffer @dst is too small, return -1 with errno set ENOSPC. + */ +int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) +{ + s64 l = n; + int i; + + i = 0; + if (dst > dst_max) + goto err_out; + *dst++ = l; + i++; + while ((l > 0x7f) || (l < -0x80)) { + if (dst > dst_max) + goto err_out; + l >>= 8; + *dst++ = l; + i++; + } + return i; +err_out: + errno = ENOSPC; + return -1; +} + +/** + * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist + * @vol: ntfs volume (needed for the ntfs version) + * @dst: destination buffer to which to write the mapping pairs array + * @dst_len: size of destination buffer @dst in bytes + * @rl: runlist for which to build the mapping pairs array + * @start_vcn: vcn at which to start the mapping pairs array + * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error + * + * Create the mapping pairs array from the runlist @rl, starting at vcn + * @start_vcn and save the array in @dst. @dst_len is the size of @dst in + * bytes and it should be at least equal to the value obtained by calling + * ntfs_get_size_for_mapping_pairs(). + * + * If @rl is NULL, just write a single terminator byte to @dst. + * + * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to + * the first vcn outside the destination buffer. Note that on error @dst has + * been filled with all the mapping pairs that will fit, thus it can be treated + * as partial success, in that a new attribute extent needs to be created or the + * next extent has to be used and the mapping pairs build has to be continued + * with @start_vcn set to *@stop_vcn. + * + * Return 0 on success. On error, return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + * ENOSPC - The destination buffer is too small. + */ +int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl) +{ + LCN prev_lcn; + u8 *dst_max, *dst_next; + s8 len_len, lcn_len; + int ret = 0; + + if (start_vcn < 0) + goto val_err; + if (!rl) { + if (start_vcn) + goto val_err; + if (stop_rl) + *stop_rl = rl; + if (dst_len < 1) + goto nospc_err; + goto ok; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) + goto val_err; + /* + * @dst_max is used for bounds checking in + * ntfs_write_significant_bytes(). + */ + dst_max = dst + dst_len - 1; + prev_lcn = 0; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length - delta); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, prev_lcn); + if (lcn_len < 0) + goto size_err; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length; rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, rl->lcn - prev_lcn); + if (lcn_len < 0) + goto size_err; + prev_lcn = rl->lcn; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst += 1 + len_len + lcn_len; + } + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; +ok: + /* Add terminator byte. */ + *dst = 0; +out: + return ret; +size_err: + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; + /* Add terminator byte. */ + *dst = 0; +nospc_err: + errno = ENOSPC; + goto errno_set; +val_err: + errno = EINVAL; + goto errno_set; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + ret = -1; + goto out; +} + +/** + * ntfs_rl_truncate - truncate a runlist starting at a specified vcn + * @arl: address of runlist to truncate + * @start_vcn: first vcn which should be cut off + * + * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory + * buffer holding the runlist. + * + * Return 0 on success and -1 on error with errno set to the error code. + * + * NOTE: @arl is the address of the runlist. We need the address so we can + * modify the pointer to the runlist with the new, reallocated memory buffer. + */ +int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) +{ + runlist *rl; + BOOL is_end = FALSE; + + if (!arl || !*arl) { + errno = EINVAL; + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); + return -1; + } + + rl = *arl; + + if (start_vcn < rl->vcn) { + errno = EINVAL; + ntfs_log_perror("Start_vcn lies outside front of runlist"); + return -1; + } + + /* Find the starting vcn in the run list. */ + while (rl->length) { + if (start_vcn < rl[1].vcn) + break; + rl++; + } + + if (!rl->length) { + errno = EIO; + ntfs_log_trace("Truncating already truncated runlist?\n"); + return -1; + } + + /* Truncate the run. */ + rl->length = start_vcn - rl->vcn; + + /* + * If a run was partially truncated, make the following runlist + * element a terminator instead of the truncated runlist + * element itself. + */ + if (rl->length) { + ++rl; + if (!rl->length) + is_end = TRUE; + rl->vcn = start_vcn; + rl->length = 0; + } + rl->lcn = (LCN)LCN_ENOENT; + /** + * Reallocate memory if necessary. + * FIXME: Below code is broken, because runlist allocations must be + * a multiply of 4096. The code caused crashes and corruptions. + */ +/* + if (!is_end) { + size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); + rl = realloc(*arl, new_size); + if (rl) + *arl = rl; + } +*/ + return 0; +} + +/** + * ntfs_rl_sparse - check whether runlist have sparse regions or not. + * @rl: runlist to check + * + * Return 1 if have, 0 if not, -1 on error with errno set to the error code. + */ +int ntfs_rl_sparse(runlist *rl) +{ + runlist *rlc; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + return 1; + } + return 0; +} + +/** + * ntfs_rl_get_compressed_size - calculate length of non sparse regions + * @vol: ntfs volume (need for cluster size) + * @rl: runlist to calculate for + * + * Return compressed size or -1 on error with errno set to the error code. + */ +s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) +{ + runlist *rlc; + s64 ret = 0; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) { + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + } else + ret += rlc->length; + } + return ret << vol->cluster_size_bits; +} + + +#ifdef NTFS_TEST +/** + * test_rl_helper + */ +#define MKRL(R,V,L,S) \ + (R)->vcn = V; \ + (R)->lcn = L; \ + (R)->length = S; +/* +} +*/ +/** + * test_rl_dump_runlist - Runlist test: Display the contents of a runlist + * @rl: + * + * Description... + * + * Returns: + */ +static void test_rl_dump_runlist(const runlist_element *rl) +{ + int abbr = 0; /* abbreviate long lists */ + int len = 0; + int i; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; + + if (!rl) { + printf(" Run list not present.\n"); + return; + } + + if (abbr) + for (len = 0; rl[len].length; len++) ; + + printf(" VCN LCN len\n"); + for (i = 0; ; i++, rl++) { + LCN lcn = rl->lcn; + + if ((abbr) && (len > 20)) { + if (i == 4) + printf(" ...\n"); + if ((i > 3) && (i < (len - 3))) + continue; + } + + if (lcn < (LCN)0) { + int ind = -lcn - 1; + + if (ind > -LCN_ENOENT - 1) + ind = 3; + printf("%8lld %8s %8lld\n", + rl->vcn, lcn_str[ind], rl->length); + } else + printf("%8lld %8lld %8lld\n", + rl->vcn, rl->lcn, rl->length); + if (!rl->length) + break; + } + if ((abbr) && (len > 20)) + printf(" (%d entries)\n", len+1); + printf("\n"); +} + +/** + * test_rl_runlists_merge - Runlist test: Merge two runlists + * @drl: + * @srl: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) +{ + runlist_element *res = NULL; + + printf("dst:\n"); + test_rl_dump_runlist(drl); + printf("src:\n"); + test_rl_dump_runlist(srl); + + res = ntfs_runlists_merge(drl, srl); + + printf("res:\n"); + test_rl_dump_runlist(res); + + return res; +} + +/** + * test_rl_read_buffer - Runlist test: Read a file containing a runlist + * @file: + * @buf: + * @bufsize: + * + * Description... + * + * Returns: + */ +static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) +{ + FILE *fptr; + + fptr = fopen(file, "r"); + if (!fptr) { + printf("open %s\n", file); + return 0; + } + + if (fread(buf, bufsize, 1, fptr) == 99) { + printf("read %s\n", file); + return 0; + } + + fclose(fptr); + return 1; +} + +/** + * test_rl_pure_src - Runlist test: Complicate the simple tests a little + * @contig: + * @multi: + * @vcn: + * @len: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) +{ + runlist_element *result; + int fudge; + + if (contig) + fudge = 0; + else + fudge = 999; + + result = ntfs_malloc(4096); + if (!result) + return NULL; + + if (multi) { + MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) + MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) + MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) + MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) + MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) + } else { + MKRL(result+0, vcn, fudge + vcn + 1000, len) + MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) + } + return result; +} + +/** + * test_rl_pure_test - Runlist test: Perform tests using simple runlists + * @test: + * @contig: + * @multi: + * @vcn: + * @len: + * @file: + * @size: + * + * Description... + * + * Returns: + */ +static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) +{ + runlist_element *src; + runlist_element *dst; + runlist_element *res; + + src = test_rl_pure_src(contig, multi, vcn, len); + dst = ntfs_malloc(4096); + if (!src || !dst) { + printf("Test %2d ---------- FAILED! (no free memory?)\n", test); + return; + } + + memcpy(dst, file, size); + + printf("Test %2d ----------\n", test); + res = test_rl_runlists_merge(dst, src); + + free(res); +} + +/** + * test_rl_pure - Runlist test: Create tests using simple runlists + * @contig: + * @multi: + * + * Description... + * + * Returns: + */ +static void test_rl_pure(char *contig, char *multi) +{ + /* VCN, LCN, len */ + static runlist_element file1[] = { + { 0, -1, 100 }, /* HOLE */ + { 100, 1100, 100 }, /* DATA */ + { 200, -1, 100 }, /* HOLE */ + { 300, 1300, 100 }, /* DATA */ + { 400, -1, 100 }, /* HOLE */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file2[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -1, 100 }, /* HOLE */ + { 200, -3, 0 } /* NOENT */ + }; + static runlist_element file3[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -3, 0 } /* NOENT */ + }; + static runlist_element file4[] = { + { 0, -3, 0 } /* NOENT */ + }; + static runlist_element file5[] = { + { 0, -2, 100 }, /* NOTMAP */ + { 100, 1100, 100 }, /* DATA */ + { 200, -2, 100 }, /* NOTMAP */ + { 300, 1300, 100 }, /* DATA */ + { 400, -2, 100 }, /* NOTMAP */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file6[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -2, 100 }, /* NOTMAP */ + { 200, -3, 0 } /* NOENT */ + }; + BOOL c, m; + + if (strcmp(contig, "contig") == 0) + c = TRUE; + else if (strcmp(contig, "noncontig") == 0) + c = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + if (strcmp(multi, "multi") == 0) + m = TRUE; + else if (strcmp(multi, "single") == 0) + m = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + + test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); + test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); + test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); + test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); + test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); + test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); + test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); + test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); + test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); + test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); + test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); + test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); + test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); + test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); + test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); + test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); + test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); + test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); + test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); + test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); + test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); + test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); + test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); + test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); + test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); + test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); + test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); + test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); + test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); + test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); + test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); + test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); + test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); + test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); +} + +/** + * test_rl_zero - Runlist test: Merge a zero-length runlist + * + * Description... + * + * Returns: + */ +static void test_rl_zero(void) +{ + runlist_element *jim = NULL; + runlist_element *bob = NULL; + + bob = calloc(3, sizeof(runlist_element)); + if (!bob) + return; + + MKRL(bob+0, 10, 99, 5) + MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) + + jim = test_rl_runlists_merge(jim, bob); + if (!jim) + return; + + free(jim); +} + +/** + * test_rl_frag_combine - Runlist test: Perform tests using fragmented files + * @vol: + * @attr1: + * @attr2: + * @attr3: + * + * Description... + * + * Returns: + */ +static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) +{ + runlist_element *run1; + runlist_element *run2; + runlist_element *run3; + + run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); + if (!run1) + return; + + run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); + if (!run2) + return; + + run1 = test_rl_runlists_merge(run1, run2); + + run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); + if (!run3) + return; + + run1 = test_rl_runlists_merge(run1, run3); + + free(run1); +} + +/** + * test_rl_frag - Runlist test: Create tests using very fragmented files + * @test: + * + * Description... + * + * Returns: + */ +static void test_rl_frag(char *test) +{ + ntfs_volume vol; + ATTR_RECORD *attr1 = ntfs_malloc(1024); + ATTR_RECORD *attr2 = ntfs_malloc(1024); + ATTR_RECORD *attr3 = ntfs_malloc(1024); + + if (!attr1 || !attr2 || !attr3) + goto out; + + vol.sb = NULL; + vol.sector_size_bits = 9; + vol.cluster_size = 2048; + vol.cluster_size_bits = 11; + vol.major_ver = 3; + + if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) + goto out; + + if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); + else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); + else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); + else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); + else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); + else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); + else + printf("Frag: No such test '%s'\n", test); + +out: + free(attr1); + free(attr2); + free(attr3); +} + +/** + * test_rl_main - Runlist test: Program start (main) + * @argc: + * @argv: + * + * Description... + * + * Returns: + */ +int test_rl_main(int argc, char *argv[]) +{ + if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); + else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); + else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); + else + printf("rl [zero|frag|pure] {args}\n"); + + return 0; +} + +#endif + diff --git a/lib/libntfs/orig/source/runlist.h b/lib/libntfs/orig/source/runlist.h new file mode 100644 index 0000000..4b73af9 --- /dev/null +++ b/lib/libntfs/orig/source/runlist.h @@ -0,0 +1,90 @@ +/* + * runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_RUNLIST_H +#define _NTFS_RUNLIST_H + +#include "types.h" + +/* Forward declarations */ +typedef struct _runlist_element runlist_element; +typedef runlist_element runlist; + +#include "attrib.h" +#include "volume.h" + +/** + * struct _runlist_element - in memory vcn to lcn mapping array element. + * @vcn: starting vcn of the current array element + * @lcn: starting lcn of the current array element + * @length: length in clusters of the current array element + * + * The last vcn (in fact the last vcn + 1) is reached when length == 0. + * + * When lcn == -1 this means that the count vcns starting at vcn are not + * physically allocated (i.e. this is a hole / data is sparse). + */ +struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ + VCN vcn; /* vcn = Starting virtual cluster number. */ + LCN lcn; /* lcn = Starting logical cluster number. */ + s64 length; /* Run length in clusters. */ +}; + +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); + +extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); + +extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b); +extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b); + +extern runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl); + +extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl); + +extern int ntfs_get_nr_significant_bytes(const s64 n); + +extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size); + +extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, + const s64 n); + +extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl); + +extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); + +extern int ntfs_rl_sparse(runlist *rl); +extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); + +#ifdef NTFS_TEST +int test_rl_main(int argc, char *argv[]); +#endif + +#endif /* defined _NTFS_RUNLIST_H */ + diff --git a/lib/libntfs/orig/source/security.c b/lib/libntfs/orig/source/security.c new file mode 100644 index 0000000..b0bbe6b --- /dev/null +++ b/lib/libntfs/orig/source/security.c @@ -0,0 +1,5099 @@ +/** + * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2006 Yura Pakhuchiy + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SETXATTR +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include +#include +#include + +#include "param.h" +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "bitmap.h" +#include "security.h" +#include "acls.h" +#include "cache.h" +#include "misc.h" + +/* + * JPA NTFS constants or structs + * should be moved to layout.h + */ + +#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */ +#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */ +#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */ +#define FIRST_SECURITY_ID 0x100 /* Lowest security id */ + + /* Mask for attributes which can be forced */ +#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ + | FILE_ATTR_HIDDEN \ + | FILE_ATTR_SYSTEM \ + | FILE_ATTR_ARCHIVE \ + | FILE_ATTR_TEMPORARY \ + | FILE_ATTR_OFFLINE \ + | FILE_ATTR_NOT_CONTENT_INDEXED ) + +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; +} ; + +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; + +/* + * A few useful constants + */ + +static ntfschar sii_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('I'), + const_cpu_to_le16('I'), + const_cpu_to_le16(0) }; +static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('H'), + const_cpu_to_le16(0) }; + +/* + * null SID (S-1-0-0) + */ + +extern const SID *nullsid; + +/* + * The zero GUID. + */ + +static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), + const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; +static const GUID *const zero_guid = &__zero_guid; + +/** + * ntfs_guid_is_zero - check if a GUID is zero + * @guid: [IN] guid to check + * + * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID + * and FALSE otherwise. + */ +BOOL ntfs_guid_is_zero(const GUID *guid) +{ + return (memcmp(guid, zero_guid, sizeof(*zero_guid))); +} + +/** + * ntfs_guid_to_mbs - convert a GUID to a multi byte string + * @guid: [IN] guid to convert + * @guid_str: [OUT] string in which to return the GUID (optional) + * + * Convert the GUID pointed to by @guid to a multi byte string of the form + * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) + * needs to be able to store at least 37 bytes. + * + * If @guid_str is not NULL it will contain the converted GUID on return. If + * it is NULL a string will be allocated and this will be returned. The caller + * is responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) +{ + char *_guid_str; + int res; + + if (!guid) { + errno = EINVAL; + return NULL; + } + _guid_str = guid_str; + if (!_guid_str) { + _guid_str = (char*)ntfs_malloc(37); + if (!_guid_str) + return _guid_str; + } + res = snprintf(_guid_str, 37, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned int)le32_to_cpu(guid->data1), + le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), + guid->data4[0], guid->data4[1], + guid->data4[2], guid->data4[3], guid->data4[4], + guid->data4[5], guid->data4[6], guid->data4[7]); + if (res == 36) + return _guid_str; + if (!guid_str) + free(_guid_str); + errno = EINVAL; + return NULL; +} + +/** + * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID + * @sid: [IN] SID for which to determine the maximum string size + * + * Determine the maximum multi byte string size in bytes which is needed to + * store the standard textual representation of the SID pointed to by @sid. + * See ntfs_sid_to_mbs(), below. + * + * On success return the maximum number of bytes needed to store the multi byte + * string and on failure return -1 with errno set to the error code. + */ +int ntfs_sid_to_mbs_size(const SID *sid) +{ + int size, i; + + if (!ntfs_sid_is_valid(sid)) { + errno = EINVAL; + return -1; + } + /* Start with "S-". */ + size = 2; + /* + * Add the SID_REVISION. Hopefully the compiler will optimize this + * away as SID_REVISION is a constant. + */ + for (i = SID_REVISION; i > 0; i /= 10) + size++; + /* Add the "-". */ + size++; + /* + * Add the identifier authority. If it needs to be in decimal, the + * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be + * in hexadecimal, then maximum is 0x665544332211 = 14 characters. + */ + if (!sid->identifier_authority.high_part) + size += 10; + else + size += 14; + /* + * Finally, add the sub authorities. For each we have a "-" followed + * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. + */ + size += (1 + 10) * sid->sub_authority_count; + /* We need the zero byte at the end, too. */ + size++; + return size * sizeof(char); +} + +/** + * ntfs_sid_to_mbs - convert a SID to a multi byte string + * @sid: [IN] SID to convert + * @sid_str: [OUT] string in which to return the SID (optional) + * @sid_str_size: [IN] size in bytes of @sid_str + * + * Convert the SID pointed to by @sid to its standard textual representation. + * @sid_str (if not NULL) needs to be able to store at least + * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of + * @sid_str if @sid_str is not NULL. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * If @sid_str is not NULL it will contain the converted SUID on return. If it + * is NULL a string will be allocated and this will be returned. The caller is + * responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) +{ + u64 u; + le32 leauth; + char *s; + int i, j, cnt; + + /* + * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will + * check @sid, too. 8 is the minimum SID string size. + */ + if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { + errno = EINVAL; + return NULL; + } + /* Allocate string if not provided. */ + if (!sid_str) { + cnt = ntfs_sid_to_mbs_size(sid); + if (cnt < 0) + return NULL; + s = (char*)ntfs_malloc(cnt); + if (!s) + return s; + sid_str = s; + /* So we know we allocated it. */ + sid_str_size = 0; + } else { + s = sid_str; + cnt = sid_str_size; + } + /* Start with "S-R-". */ + i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Add the identifier authority. */ + for (u = i = 0, j = 40; i < 6; i++, j -= 8) + u += (u64)sid->identifier_authority.value[i] << j; + if (!sid->identifier_authority.high_part) + i = snprintf(s, cnt, "%lu", (unsigned long)u); + else + i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Finally, add the sub authorities. */ + for (j = 0; j < sid->sub_authority_count; j++) { + leauth = sid->sub_authority[j]; + i = snprintf(s, cnt, "-%u", (unsigned int) + le32_to_cpu(leauth)); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + } + return sid_str; +err_out: + if (i >= cnt) + i = EMSGSIZE; + else + i = errno; + if (!sid_str_size) + free(sid_str); + errno = i; + return NULL; +} + +/** + * ntfs_generate_guid - generatates a random current guid. + * @guid: [OUT] pointer to a GUID struct to hold the generated guid. + * + * perhaps not a very good random number generator though... + */ +void ntfs_generate_guid(GUID *guid) +{ + unsigned int i; + u8 *p = (u8 *)guid; + + for (i = 0; i < sizeof(GUID); i++) { + p[i] = (u8)(random() & 0xFF); + if (i == 7) + p[7] = (p[7] & 0x0F) | 0x40; + if (i == 8) + p[8] = (p[8] & 0x3F) | 0x80; + } +} + +/** + * ntfs_security_hash - calculate the hash of a security descriptor + * @sd: self-relative security descriptor whose hash to calculate + * @length: size in bytes of the security descritor @sd + * + * Calculate the hash of the self-relative security descriptor @sd of length + * @length bytes. + * + * This hash is used in the $Secure system file as the primary key for the $SDH + * index and is also stored in the header of each security descriptor in the + * $SDS data stream as well as in the index data of both the $SII and $SDH + * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER + * structure. + * + * Return the calculated security hash in little endian. + */ +le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) +{ + const le32 *pos = (const le32*)sd; + const le32 *end = pos + (len >> 2); + u32 hash = 0; + + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } + return cpu_to_le32(hash); +} + +/* + * Get the first entry of current index block + * cut and pasted form ntfs_ie_get_first() in index.c + */ + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset)); +} + +/* + * Stuff a 256KB block into $SDS before writing descriptors + * into the block. + * + * This prevents $SDS from being automatically declared as sparse + * when the second copy of the first security descriptor is written + * 256KB further ahead. + * + * Having $SDS declared as a sparse file is not wrong by itself + * and chkdsk leaves it as a sparse file. It does however complain + * and add a sparse flag (0x0200) into field file_attributes of + * STANDARD_INFORMATION of $Secure. This probably means that a + * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse + * files (FILE_ATTR_SPARSE_FILE). + * + * Windows normally does not convert to sparse attribute or sparse + * file. Stuffing is just a way to get to the same result. + */ + +static int entersecurity_stuff(ntfs_volume *vol, off_t offs) +{ + int res; + int written; + unsigned long total; + char *stuff; + + res = 0; + total = 0; + stuff = (char*)ntfs_malloc(STUFFSZ); + if (stuff) { + memset(stuff, 0, STUFFSZ); + do { + written = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, stuff, STUFFSZ, offs); + if (written == STUFFSZ) { + total += STUFFSZ; + offs += STUFFSZ; + } else { + errno = ENOSPC; + res = -1; + } + } while (!res && (total < ALIGN_SDS_BLOCK)); + free(stuff); + } else { + errno = ENOMEM; + res = -1; + } + return (res); +} + +/* + * Enter a new security descriptor into $Secure (data only) + * it has to be written twice with an offset of 256KB + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_data(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash, le32 keyid, off_t offs, int gap) +{ + int res; + int written1; + int written2; + char *fullattr; + int fullsz; + SECURITY_DESCRIPTOR_HEADER *phsds; + + res = -1; + fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER); + fullattr = (char*)ntfs_malloc(fullsz); + if (fullattr) { + /* + * Clear the gap from previous descriptor + * this could be useful for appending the second + * copy to the end of file. When creating a new + * 256K block, the gap is cleared while writing + * the first copy + */ + if (gap) + memset(fullattr,0,gap); + memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)], + attr,attrsz); + phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap]; + phsds->hash = hash; + phsds->security_id = keyid; + phsds->offset = cpu_to_le64(offs); + phsds->length = cpu_to_le32(fullsz - gap); + written1 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap); + written2 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap + ALIGN_SDS_BLOCK); + if ((written1 == fullsz) + && (written2 == written1)) + res = 0; + else + errno = ENOSPC; + free(fullattr); + } else + errno = ENOMEM; + return (res); +} + +/* + * Enter a new security descriptor in $Secure (indexes only) + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz, + le32 hash, le32 keyid, off_t offs) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int res; + ntfs_index_context *xsii; + ntfs_index_context *xsdh; + struct SII newsii; + struct SDH newsdh; + + res = -1; + /* enter a new $SII record */ + + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + newsii.offs = const_cpu_to_le16(20); + newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20); + newsii.fill1 = const_cpu_to_le32(0); + newsii.indexsz = const_cpu_to_le16(sizeof(struct SII)); + newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY)); + newsii.flags = const_cpu_to_le16(0); + newsii.fill2 = const_cpu_to_le16(0); + newsii.keysecurid = keyid; + newsii.hash = hash; + newsii.securid = keyid; + realign.all = cpu_to_le64(offs); + newsii.dataoffsh = realign.parts.dataoffsh; + newsii.dataoffsl = realign.parts.dataoffsl; + newsii.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) { + + /* enter a new $SDH record */ + + xsdh = vol->secure_xsdh; + ntfs_index_ctx_reinit(xsdh); + newsdh.offs = const_cpu_to_le16(24); + newsdh.size = const_cpu_to_le16( + sizeof(SECURITY_DESCRIPTOR_HEADER)); + newsdh.fill1 = const_cpu_to_le32(0); + newsdh.indexsz = const_cpu_to_le16( + sizeof(struct SDH)); + newsdh.indexksz = const_cpu_to_le16( + sizeof(SDH_INDEX_KEY)); + newsdh.flags = const_cpu_to_le16(0); + newsdh.fill2 = const_cpu_to_le16(0); + newsdh.keyhash = hash; + newsdh.keysecurid = keyid; + newsdh.hash = hash; + newsdh.securid = keyid; + newsdh.dataoffsh = realign.parts.dataoffsh; + newsdh.dataoffsl = realign.parts.dataoffsl; + newsdh.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + /* special filler value, Windows generally */ + /* fills with 0x00490049, sometimes with zero */ + newsdh.fill3 = const_cpu_to_le32(0x00490049); + if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh)) + res = 0; + } + return (res); +} + +/* + * Enter a new security descriptor in $Secure (data and indexes) + * Returns id of entry, or zero if there is a problem. + * (should not be called for NTFS version < 3.0) + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 entersecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + le32 securid; + le32 keyid; + u32 newkey; + off_t offs; + int gap; + int size; + BOOL found; + struct SII *psii; + INDEX_ENTRY *entry; + INDEX_ENTRY *next; + ntfs_index_context *xsii; + int retries; + ntfs_attr *na; + int olderrno; + + /* find the first available securid beyond the last key */ + /* in $Secure:$SII. This also determines the first */ + /* available location in $Secure:$SDS, as this stream */ + /* is always appended to and the id's are allocated */ + /* in sequence */ + + securid = const_cpu_to_le32(0); + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + offs = size = 0; + keyid = const_cpu_to_le32(-1); + olderrno = errno; + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Inconsistency in index $SII"); + psii = (struct SII*)NULL; + } else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsii->entry; + psii = (struct SII*)xsii->entry; + } + if (psii) { + /* + * Get last entry in block, but must get first one + * one first, as we should already be beyond the + * last one. For some reason the search for the last + * entry sometimes does not return the last block... + * we assume this can only happen in root block + */ + if (xsii->is_in_root) + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ir->index); + else + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ib->index); + /* + * All index blocks should be at least half full + * so there always is a last entry but one, + * except when creating the first entry in index root. + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. + */ + keyid = const_cpu_to_le32(0); + retries = 0; + while (entry) { + next = ntfs_index_next(entry,xsii); + if (next) { + psii = (struct SII*)next; + /* save last key and */ + /* available position */ + keyid = psii->keysecurid; + realign.parts.dataoffsh + = psii->dataoffsh; + realign.parts.dataoffsl + = psii->dataoffsl; + offs = le64_to_cpu(realign.all); + size = le32_to_cpu(psii->datasize); + } + entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + } + retries++; + } + } + } + if (!keyid) { + /* + * could not find any entry, before creating the first + * entry, make a double check by making sure size of $SII + * is less than needed for one entry + */ + securid = const_cpu_to_le32(0); + na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4); + if (na) { + if ((size_t)na->data_size < sizeof(struct SII)) { + ntfs_log_error("Creating the first security_id\n"); + securid = const_cpu_to_le32(FIRST_SECURITY_ID); + } + ntfs_attr_close(na); + } + if (!securid) { + ntfs_log_error("Error creating a security_id\n"); + errno = EIO; + } + } else { + newkey = le32_to_cpu(keyid) + 1; + securid = cpu_to_le32(newkey); + } + /* + * The security attr has to be written twice 256KB + * apart. This implies that offsets like + * 0x40000*odd_integer must be left available for + * the second copy. So align to next block when + * the last byte overflows on a wrong block. + */ + + if (securid) { + gap = (-size) & (ALIGN_SDS_ENTRY - 1); + offs += gap + size; + if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + & ALIGN_SDS_BLOCK) { + offs = ((offs + attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + | (ALIGN_SDS_BLOCK - 1)) + 1; + } + if (!(offs & (ALIGN_SDS_BLOCK - 1))) + entersecurity_stuff(vol, offs); + /* + * now write the security attr to storage : + * first data, then SII, then SDH + * If failure occurs while writing SDS, data will never + * be accessed through indexes, and will be overwritten + * by the next allocated descriptor + * If failure occurs while writing SII, the id has not + * recorded and will be reallocated later + * If failure occurs while writing SDH, the space allocated + * in SDS or SII will not be reused, an inconsistency + * will persist with no significant consequence + */ + if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap) + || entersecurity_indexes(vol, attrsz, hash, securid, offs)) + securid = const_cpu_to_le32(0); + } + /* inode now is dirty, synchronize it all */ + ntfs_index_entry_mark_dirty(vol->secure_xsii); + ntfs_index_ctx_reinit(vol->secure_xsii); + ntfs_index_entry_mark_dirty(vol->secure_xsdh); + ntfs_index_ctx_reinit(vol->secure_xsdh); + NInoSetDirty(vol->secure_ni); + if (ntfs_inode_sync(vol->secure_ni)) + ntfs_log_perror("Could not sync $Secure\n"); + return (securid); +} + +/* + * Find a matching security descriptor in $Secure, + * if none, allocate a new id and write the descriptor to storage + * Returns id of entry, or zero if there is a problem. + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 setsecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz) +{ + struct SDH *psdh; /* this is an image of index (le) */ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + BOOL found; + BOOL collision; + size_t size; + size_t rdsize; + s64 offs; + int res; + ntfs_index_context *xsdh; + char *oldattr; + SDH_INDEX_KEY key; + INDEX_ENTRY *entry; + le32 securid; + le32 hash; + int olderrno; + + hash = ntfs_security_hash(attr,attrsz); + oldattr = (char*)NULL; + securid = const_cpu_to_le32(0); + res = 0; + xsdh = vol->secure_xsdh; + if (vol->secure_ni && xsdh && !vol->secure_reentry++) { + ntfs_index_ctx_reinit(xsdh); + /* + * find the nearest key as (hash,0) + * (do not search for partial key : in case of collision, + * it could return a key which is not the first one which + * collides) + */ + key.hash = hash; + key.security_id = const_cpu_to_le32(0); + olderrno = errno; + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + if (!found && (errno != ENOENT)) + ntfs_log_perror("Inconsistency in index $SDH"); + else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsdh->entry; + found = FALSE; + /* + * lookup() may return a node with no data, + * if so get next + */ + if (entry->ie_flags & INDEX_ENTRY_END) + entry = ntfs_index_next(entry,xsdh); + do { + collision = FALSE; + psdh = (struct SDH*)entry; + if (psdh) + size = (size_t) le32_to_cpu(psdh->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + else size = 0; + /* if hash is not the same, the key is not present */ + if (psdh && (size > 0) + && (psdh->keyhash == hash)) { + /* if hash is the same */ + /* check the whole record */ + realign.parts.dataoffsh = psdh->dataoffsh; + realign.parts.dataoffsl = psdh->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + oldattr = (char*)ntfs_malloc(size); + if (oldattr) { + rdsize = ntfs_attr_data_read( + vol->secure_ni, + STREAM_SDS, 4, + oldattr, size, offs); + found = (rdsize == size) + && !memcmp(oldattr,attr,size); + free(oldattr); + /* if the records do not compare */ + /* (hash collision), try next one */ + if (!found) { + entry = ntfs_index_next( + entry,xsdh); + collision = TRUE; + } + } else + res = ENOMEM; + } + } while (collision && entry); + if (found) + securid = psdh->keysecurid; + else { + if (res) { + errno = res; + securid = const_cpu_to_le32(0); + } else { + /* + * no matching key : + * have to build a new one + */ + securid = entersecurityattr(vol, + attr, attrsz, hash); + } + } + } + } + if (--vol->secure_reentry) + ntfs_log_perror("Reentry error, check no multithreading\n"); + return (securid); +} + + +/* + * Update the security descriptor of a file + * Either as an attribute (complying with pre v3.x NTFS version) + * or, when possible, as an entry in $Secure (for NTFS v3.x) + * + * returns 0 if success + */ + +static int update_secur_descr(ntfs_volume *vol, + char *newattr, ntfs_inode *ni) +{ + int newattrsz; + int written; + int res; + ntfs_attr *na; + + newattrsz = ntfs_attr_size(newattr); + +#if !FORCE_FORMAT_v1x + if ((vol->major_ver < 3) || !vol->secure_ni) { +#endif + + /* update for NTFS format v1.x */ + + /* update the old security attribute */ + na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64) newattrsz); + /* overwrite value */ + if (!res) { + written = (int)ntfs_attr_pwrite(na, (s64) 0, + (s64) newattrsz, newattr); + if (written != newattrsz) { + ntfs_log_error("Failed to update " + "a v1.x security descriptor\n"); + errno = EIO; + res = -1; + } + } + + ntfs_attr_close(na); + /* if old security attribute was found, also */ + /* truncate standard information attribute to v1.x */ + /* this is needed when security data is wanted */ + /* as v1.x though volume is formatted for v3.x */ + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + clear_nino_flag(ni, v3_Extensions); + /* + * Truncating the record does not sweep extensions + * from copy in memory. Clear security_id to be safe + */ + ni->security_id = const_cpu_to_le32(0); + res = ntfs_attr_truncate(na, (s64)48); + ntfs_attr_close(na); + clear_nino_flag(ni, v3_Extensions); + } + } else { + /* + * insert the new security attribute if there + * were none + */ + res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, (u8*)newattr, + (s64) newattrsz); + } +#if !FORCE_FORMAT_v1x + } else { + + /* update for NTFS format v3.x */ + + le32 securid; + + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + (s64)newattrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + if (!test_nino_flag(ni, v3_Extensions)) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, + AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + } + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to update " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + } +#endif + + /* mark node as dirty */ + NInoSetDirty(ni); + return (res); +} + +/* + * Upgrade the security descriptor of a file + * This is intended to allow graceful upgrades for files which + * were created in previous versions, with a security attributes + * and no security id. + * + * It will allocate a security id and replace the individual + * security attribute by a reference to the global one + * + * Special files are not upgraded (currently / and files in + * directories /$*) + * + * Though most code is similar to update_secur_desc() it has + * been kept apart to facilitate the further processing of + * special cases or even to remove it if found dangerous. + * + * returns 0 if success, + * 1 if not upgradable. This is not an error. + * -1 if there is a problem + */ + +static int upgrade_secur_desc(ntfs_volume *vol, + const char *attr, ntfs_inode *ni) +{ + int attrsz; + int res; + le32 securid; + ntfs_attr *na; + + /* + * upgrade requires NTFS format v3.x + * also refuse upgrading for special files + * whose number is less than FILE_first_user + */ + + if ((vol->major_ver >= 3) + && (ni->mft_no >= FILE_first_user)) { + attrsz = ntfs_attr_size(attr); + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)attr, + (s64)attrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to upgrade " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + /* mark node as dirty */ + NInoSetDirty(ni); + } else + res = 1; + + return (res); +} + +/* + * Optional simplified checking of group membership + * + * This only takes into account the groups defined in + * /etc/group at initialization time. + * It does not take into account the groups dynamically set by + * setgroups() nor the changes in /etc/group since initialization + * + * This optional method could be useful if standard checking + * leads to a performance concern. + * + * Should not be called for user root, however the group may be root + * + */ + +static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + BOOL ingroup; + int grcnt; + gid_t *groups; + struct MAPPING *user; + + ingroup = FALSE; + if (uid) { + user = scx->mapping[MAPUSERS]; + while (user && ((uid_t)user->xid != uid)) + user = user->next; + if (user) { + groups = user->groups; + grcnt = user->grcnt; + while ((--grcnt >= 0) && (groups[grcnt] != gid)) { } + ingroup = (grcnt >= 0); + } + } + return (ingroup); +} + + +/* + * Check whether current thread owner is member of file group + * + * Should not be called for user root, however the group may be root + * + * As indicated by Miklos Szeredi : + * + * The group list is available in + * + * /proc/$PID/task/$TID/status + * + * and fuse supplies TID in get_fuse_context()->pid. The only problem is + * finding out PID, for which I have no good solution, except to iterate + * through all processes. This is rather slow, but may be speeded up + * with caching and heuristics (for single threaded programs PID = TID). + * + * The following implementation gets the group list from + * /proc/$TID/task/$TID/status which apparently exists and + * contains the same data. + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + static char key[] = "\nGroups:"; + char buf[BUFSZ+1]; + char filename[64]; + enum { INKEY, INSEP, INNUM, INEND } state; + int fd; + char c; + int matched; + BOOL ismember; + int got; + char *p; + gid_t grp; + pid_t tid; + + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/task/%u/status",tid,tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, buf, BUFSZ); + buf[got] = 0; + state = INKEY; + matched = 0; + p = buf; + grp = 0; + /* + * A simple automaton to process lines like + * Groups: 14 500 513 + */ + do { + c = *p++; + if (!c) { + /* refill buffer */ + got = read(fd, buf, BUFSZ); + buf[got] = 0; + p = buf; + c = *p++; /* 0 at end of file */ + } + switch (state) { + case INKEY : + if (key[matched] == c) { + if (!key[++matched]) + state = INSEP; + } else + if (key[0] == c) + matched = 1; + else + matched = 0; + break; + case INSEP : + if ((c >= '0') && (c <= '9')) { + grp = c - '0'; + state = INNUM; + } else + if ((c != ' ') && (c != '\t')) + state = INEND; + break; + case INNUM : + if ((c >= '0') && (c <= '9')) + grp = grp*10 + c - '0'; + else { + ismember = (grp == gid); + if ((c != ' ') && (c != '\t')) + state = INEND; + else + state = INSEP; + } + default : + break; + } + } while (!ismember && c && (state != INEND)); + close(fd); + if (!c) + ntfs_log_error("No group record found in %s\n",filename); + } else + ntfs_log_error("Could not open %s\n",filename); + } + return (ismember); +} + +/* + * Cacheing is done two-way : + * - from uid, gid and perm to securid (CACHED_SECURID) + * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) + * + * CACHED_SECURID data is kept in a most-recent-first list + * which should not be too long to be efficient. Its optimal + * size is depends on usage and is hard to determine. + * + * CACHED_PERMISSIONS data is kept in a two-level indexed array. It + * is optimal at the expense of storage. Use of a most-recent-first + * list would save memory and provide similar performances for + * standard usage, but not for file servers with too many file + * owners + * + * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS + * for legacy directories which were not allocated a security_id + * it is organized in a most-recent-first list. + * + * In main caches, data is never invalidated, as the meaning of + * a security_id only changes when user mapping is changed, which + * current implies remounting. However returned entries may be + * overwritten at next update, so data has to be copied elsewhere + * before another cache update is made. + * In legacy cache, data has to be invalidated when protection is + * changed. + * + * Though the same data may be found in both list, they + * must be kept separately : the interpretation of ACL + * in both direction are approximations which could be non + * reciprocal for some configuration of the user mapping data + * + * During the process of recompiling ntfs-3g from a tgz archive, + * security processing added 7.6% to the cpu time used by ntfs-3g + * and 30% if the cache is disabled. + */ + +static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *cache; + unsigned int index1; + unsigned int i; + + cache = (struct PERMISSIONS_CACHE*)NULL; + /* create the first permissions blocks */ + index1 = securindex >> CACHE_PERMISSIONS_BITS; + cache = (struct PERMISSIONS_CACHE*) + ntfs_malloc(sizeof(struct PERMISSIONS_CACHE) + + index1*sizeof(struct CACHED_PERMISSIONS*)); + if (cache) { + cache->head.last = index1; + cache->head.p_reads = 0; + cache->head.p_hits = 0; + cache->head.p_writes = 0; + *scx->pseccache = cache; + for (i=0; i<=index1; i++) + cache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + } + return (cache); +} + +/* + * Free memory used by caches + * The only purpose is to facilitate the detection of memory leaks + */ + +static void free_caches(struct SECURITY_CONTEXT *scx) +{ + unsigned int index1; + struct PERMISSIONS_CACHE *pseccache; + + pseccache = *scx->pseccache; + if (pseccache) { + for (index1=0; index1<=pseccache->head.last; index1++) + if (pseccache->cachetable[index1]) { +#if POSIXACLS + struct CACHED_PERMISSIONS *cacheentry; + unsigned int index2; + + for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) { + cacheentry = &pseccache->cachetable[index1][index2]; + if (cacheentry->valid + && cacheentry->pxdesc) + free(cacheentry->pxdesc); + } +#endif + free(pseccache->cachetable[index1]); + } + free(pseccache); + } +} + +static int compare(const struct CACHED_SECURID *cached, + const struct CACHED_SECURID *item) +{ +#if POSIXACLS + size_t csize; + size_t isize; + + /* only compare data and sizes */ + csize = (cached->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)cached->variable)->acccnt + + ((struct POSIX_SECURITY*)cached->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + isize = (item->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)item->variable)->acccnt + + ((struct POSIX_SECURITY*)item->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode) + || (csize != isize) + || (csize + && isize + && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl, + &((struct POSIX_SECURITY*)item->variable)->acl, csize))); +#else + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode)); +#endif +} + +static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, + const struct CACHED_PERMISSIONS_LEGACY *item) +{ + return (cached->mft_no != item->mft_no); +} + +/* + * Resize permission cache table + * do not call unless resizing is needed + * + * If allocation fails, the cache size is not updated + * Lack of memory is not considered as an error, the cache is left + * consistent and errno is not set. + */ + +static void resize_cache(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *oldcache; + struct PERMISSIONS_CACHE *newcache; + int newcnt; + int oldcnt; + unsigned int index1; + unsigned int i; + + oldcache = *scx->pseccache; + index1 = securindex >> CACHE_PERMISSIONS_BITS; + newcnt = index1 + 1; + if (newcnt <= ((CACHE_PERMISSIONS_SIZE + + (1 << CACHE_PERMISSIONS_BITS) + - 1) >> CACHE_PERMISSIONS_BITS)) { + /* expand cache beyond current end, do not use realloc() */ + /* to avoid losing data when there is no more memory */ + oldcnt = oldcache->head.last + 1; + newcache = (struct PERMISSIONS_CACHE*) + ntfs_malloc( + sizeof(struct PERMISSIONS_CACHE) + + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + if (newcache) { + memcpy(newcache,oldcache, + sizeof(struct PERMISSIONS_CACHE) + + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + free(oldcache); + /* mark new entries as not valid */ + for (i=newcache->head.last+1; i<=index1; i++) + newcache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + newcache->head.last = index1; + *scx->pseccache = newcache; + } + } +} + +/* + * Enter uid, gid and mode into cache, if possible + * + * returns the updated or created cache entry, + * or NULL if not possible (typically if there is no + * security id associated) + */ + +#if POSIXACLS +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + struct POSIX_SECURITY *pxdesc) +#else +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode) +#endif +{ + struct CACHED_PERMISSIONS *cacheentry; + struct CACHED_PERMISSIONS *cacheblock; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; +#if POSIXACLS + int pxsize; + struct POSIX_SECURITY *pxcached; +#endif + unsigned int index1; + unsigned int index2; + int i; + + /* cacheing is only possible if a security_id has been defined */ + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) { + /* + * Immediately test the most frequent situation + * where the entry exists + */ + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (cacheentry->valid && cacheentry->pxdesc) + free(cacheentry->pxdesc); + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } else { + if (!pcache) { + /* create the first cache block */ + pcache = create_caches(scx, securindex); + } else { + if (index1 > pcache->head.last) { + resize_cache(scx, securindex); + pcache = *scx->pseccache; + } + } + /* allocate block, if cache table was allocated */ + if (pcache && (index1 <= pcache->head.last)) { + cacheblock = (struct CACHED_PERMISSIONS*) + malloc(sizeof(struct CACHED_PERMISSIONS) + << CACHE_PERMISSIONS_BITS); + pcache->cachetable[index1] = cacheblock; + for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++) + cacheblock[i].valid = 0; + cacheentry = &cacheblock[index2]; + if (cacheentry) { + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } + } else + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + } else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; +#if CACHE_LEGACY_SIZE + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.perm.uid = uid; + wanted.perm.gid = gid; +#if POSIXACLS + wanted.perm.mode = pxdesc->mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); +#else + wanted.perm.mode = mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) { + cacheentry = &legacy->perm; +#if POSIXACLS + /* + * give direct access to the cached pxdesc + * in the permissions structure + */ + cacheentry->pxdesc = legacy->variable; +#endif + } + } +#endif + } + return (cacheentry); +} + +/* + * Fetch owner, group and permission of a file, if cached + * + * Beware : do not use the returned entry after a cache update : + * the cache may be relocated making the returned entry meaningless + * + * returns the cache entry, or NULL if not available + */ + +static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni) +{ + struct CACHED_PERMISSIONS *cacheentry; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; + unsigned int index1; + unsigned int index2; + + /* cacheing is only possible if a security_id has been defined */ + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (test_nino_flag(ni, v3_Extensions) + && (ni->security_id)) { + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + /* reject if entry is not valid */ + if (!cacheentry->valid) + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + else + pcache->head.p_hits++; + if (pcache) + pcache->head.p_reads++; + } + } +#if CACHE_LEGACY_SIZE + else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) cacheentry = &legacy->perm; + } + } +#endif +#if POSIXACLS + if (cacheentry && !cacheentry->pxdesc) { + ntfs_log_error("No Posix descriptor in cache\n"); + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } +#endif + return (cacheentry); +} + +/* + * Retrieve a security attribute from $Secure + */ + +static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) +{ + struct SII *psii; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int found; + size_t size; + size_t rdsize; + s64 offs; + ntfs_inode *ni; + ntfs_index_context *xsii; + char *securattr; + + securattr = (char*)NULL; + ni = vol->secure_ni; + xsii = vol->secure_xsii; + if (ni && xsii) { + ntfs_index_ctx_reinit(xsii); + found = + !ntfs_index_lookup((char*)&id, + sizeof(SII_INDEX_KEY), xsii); + if (found) { + psii = (struct SII*)xsii->entry; + size = + (size_t) le32_to_cpu(psii->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + /* work around bad alignment problem */ + realign.parts.dataoffsh = psii->dataoffsh; + realign.parts.dataoffsl = psii->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + + securattr = (char*)ntfs_malloc(size); + if (securattr) { + rdsize = ntfs_attr_data_read( + ni, STREAM_SDS, 4, + securattr, size, offs); + if ((rdsize != size) + || !ntfs_valid_descr(securattr, + rdsize)) { + /* error to be logged by caller */ + free(securattr); + securattr = (char*)NULL; + } + } + } else + if (errno != ENOENT) + ntfs_log_perror("Inconsistency in index $SII"); + } + if (!securattr) { + ntfs_log_error("Failed to retrieve a security descriptor\n"); + errno = EIO; + } + return (securattr); +} + +/* + * Get the security descriptor associated to a file + * + * Either : + * - read the security descriptor attribute (v1.x format) + * - or find the descriptor in $Secure:$SDS (v3.x format) + * + * in both case, sanity checks are done on the attribute and + * the descriptor can be assumed safe + * + * The returned descriptor is dynamically allocated and has to be freed + */ + +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) +{ + SII_INDEX_KEY securid; + char *securattr; + s64 readallsz; + + /* + * Warning : in some situations, after fixing by chkdsk, + * v3_Extensions are marked present (long standard informations) + * with a default security descriptor inserted in an + * attribute + */ + if (test_nino_flag(ni, v3_Extensions) + && vol->secure_ni && ni->security_id) { + /* get v3.x descriptor in $Secure */ + securid.security_id = ni->security_id; + securattr = retrievesecurityattr(vol,securid); + if (!securattr) + ntfs_log_error("Bad security descriptor for 0x%lx\n", + (long)le32_to_cpu(ni->security_id)); + } else { + /* get v1.x security attribute */ + readallsz = 0; + securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, &readallsz); + if (securattr && !ntfs_valid_descr(securattr, readallsz)) { + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); + free(securattr); + securattr = (char*)NULL; + } + } + if (!securattr) { + /* + * in some situations, there is no security + * descriptor, and chkdsk does not detect or fix + * anything. This could be a normal situation. + * When this happens, simulate a descriptor with + * minimum rights, so that a real descriptor can + * be created by chown or chmod + */ + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); + securattr = ntfs_build_descr(0, 0, adminsid, adminsid); + } + return (securattr); +} + +#if POSIXACLS + +/* + * Determine which access types to a file are allowed + * according to the relation of current process to the file + * + * Do not call if default_permissions is set + */ + +static int access_check_posix(struct SECURITY_CONTEXT *scx, + struct POSIX_SECURITY *pxdesc, mode_t request, + uid_t uid, gid_t gid) +{ + struct POSIX_ACE *pxace; + int userperms; + int groupperms; + int mask; + BOOL somegroup; + BOOL needgroups; + mode_t perms; + int i; + + perms = pxdesc->mode; + /* owner and root access */ + if (!scx->uid || (uid == scx->uid)) { + if (!scx->uid) { + /* root access if owner or other execution */ + if (perms & 0101) + perms = 07777; + else { + /* root access if some group execution */ + groupperms = 0; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER_OBJ : + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + groupperms |= pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + default : + break; + } + } + perms = (groupperms & mask & 1) | 6; + } + } else + perms &= 07700; + } else { + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ + userperms = -1; + groupperms = -1; + needgroups = FALSE; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER : + if ((uid_t)pxace->id == scx->uid) + userperms = pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; + default : + break; + } + } + /* designated users */ + if (userperms >= 0) + perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; + else { + /* owning group */ + if (!(~(perms >> 3) & request & mask) + && ((gid == scx->gid) + || groupmember(scx, scx->uid, gid))) + perms &= 07070; + else { + /* other groups */ + groupperms = -1; + somegroup = FALSE; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + if ((pxace->tag == POSIX_ACL_GROUP) + && groupmember(scx, uid, pxace->id)) { + if (!(~pxace->perms & request & mask)) + groupperms = pxace->perms; + somegroup = TRUE; + } + } + if (groupperms >= 0) + perms = (perms & 07000) + (groupperms & mask); + else + if (somegroup) + perms = 0; + else + perms &= 07007; + } + } + } + return (perms); +} + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * Do no call if default_permissions is set + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + struct POSIX_SECURITY *pxdesc; + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + perm = access_check_posix(scx,cached->pxdesc,request,uid,gid); + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, pxdesc); + } + if (pxdesc) { + perm = access_check_posix(scx,pxdesc,request,uid,gid); + free(pxdesc); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + } + return (perm); +} + +/* + * Get a Posix ACL + * + * returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + size_t outsize; + + outsize = 0; /* default to error */ + if (!scx->mapping[MAPUSERS]) + errno = ENOTSUP; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) + pxdesc = cached->pxdesc; + else { + securattr = getsecurityattr(scx->vol, ni); + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + + /* + * fetch owner and group for cacheing + */ + if (pxdesc) { + perm = pxdesc->mode & 07777; + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, + securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS) + enter_cache(scx, ni, uid, + gid, pxdesc); + } + free(securattr); + } else + pxdesc = (struct POSIX_SECURITY*)NULL; + } + + if (pxdesc) { + if (ntfs_valid_posix(pxdesc)) { + if (!strcmp(name,"system.posix_acl_default")) { + if (ni->mrec->flags + & MFT_RECORD_IS_DIRECTORY) + outsize = sizeof(struct POSIX_ACL) + + pxdesc->defcnt*sizeof(struct POSIX_ACE); + else { + /* + * getting default ACL from plain file : + * return EACCES if size > 0 as + * indicated in the man, but return ok + * if size == 0, so that ls does not + * display an error + */ + if (size > 0) { + outsize = 0; + errno = EACCES; + } else + outsize = sizeof(struct POSIX_ACL); + } + if (outsize && (outsize <= size)) { + memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL)); + memcpy(&value[sizeof(struct POSIX_ACL)], + &pxdesc->acl.ace[pxdesc->firstdef], + outsize-sizeof(struct POSIX_ACL)); + } + } else { + outsize = sizeof(struct POSIX_ACL) + + pxdesc->acccnt*sizeof(struct POSIX_ACE); + if (outsize <= size) + memcpy(value,&pxdesc->acl,outsize); + } + } else { + outsize = 0; + errno = EIO; + ntfs_log_error("Invalid Posix ACL built\n"); + } + if (!cached) + free(pxdesc); + } else + outsize = 0; + } + return (outsize ? (int)outsize : -errno); +} + +#else /* POSIXACLS */ + + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + BOOL isdir; + uid_t uid; + gid_t gid; + int perm; + + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + uid = cached->uid; + gid = cached->gid; + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, perm); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + if (perm >= 0) { + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm = 07777; + else + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; + } + } + return (perm); +} + +#endif /* POSIXACLS */ + +/* + * Get an NTFS ACL + * + * Returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) +{ + char *securattr; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + outsize = ntfs_attr_size(securattr); + if (outsize <= size) { + memcpy(value,securattr,outsize); + } + free(securattr); + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Get owner, group and permissions in an stat structure + * returns permissions, or -1 if there is a problem + */ + +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, struct stat *stbuf) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + const struct CACHED_PERMISSIONS *cached; + int perm; + BOOL isdir; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + stbuf->st_uid = cached->uid; + stbuf->st_gid = cached->gid; + stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; + } else { + perm = -1; /* default to error */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; +#else + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); +#endif + /* + * fetch owner and group for cacheing + */ + if (perm >= 0) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!perm && ntfs_same_sid(usid, adminsid)) { + stbuf->st_uid = + find_tenant(scx, + securattr); + if (stbuf->st_uid) + perm = 0700; + } else + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + stbuf->st_mode = + (stbuf->st_mode & ~07777) + perm; +#if POSIXACLS + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, pxdesc); + free(pxdesc); +#else + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, perm); +#endif + } + free(securattr); + } + } + } + return (perm); +} + +#if POSIXACLS + +/* + * Get the base for a Posix inheritance and + * build an inherited Posix descriptor + */ + +static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) +{ + const struct CACHED_PERMISSIONS *cached; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pydesc; + char *securattr; + const SID *usid; + const SID *gsid; + uid_t uid; + gid_t gid; + + pydesc = (struct POSIX_SECURITY*)NULL; + /* check whether parent directory is available in cache */ + cached = fetch_cache(scx,dir_ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + pxdesc = cached->pxdesc; + if (pxdesc) { + pydesc = ntfs_build_inherited_posix(pxdesc,mode, + scx->umask,isdir); + } + } else { + securattr = getsecurityattr(scx->vol, dir_ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + if (pxdesc && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(dir_ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, dir_ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(dir_ni, v3_Extensions)) { + enter_cache(scx, dir_ni, uid, + gid, pxdesc); + } + pydesc = ntfs_build_inherited_posix(pxdesc, + mode, scx->umask, isdir); + free(pxdesc); + } + free(securattr); + } + } + return (pydesc); +} + +/* + * Allocate a security_id for a file being created + * + * Returns zero if not possible (NTFS v3.x required) + */ + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + struct POSIX_SECURITY *pxdesc; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = pxdesc->mode & mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + free(pxdesc); + } +#endif + return (securid); +} + +/* + * Apply Posix inheritance to a newly created file + * (for NTFS 1.x only : no securid) + */ + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode) +{ + struct POSIX_SECURITY *pxdesc; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + int res; + + res = -1; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + /* Adjust Windows read-only flag */ + res = update_secur_descr(scx->vol, newattr, ni); + if (!res && !isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = pxdesc; + legacy.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(newattr); + + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + return (res); +} + +#else + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr(mode, isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } +#endif + return (securid); +} + +#endif + +/* + * Update ownership and mode of a file, reusing an existing + * security descriptor when possible + * + * Returns zero if successful + */ + +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode, + struct POSIX_SECURITY *pxdesc) +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode) +#endif +{ + int res; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + + res = 0; + + /* check whether target securid is known in cache */ + + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; +#if POSIXACLS + wanted.variable = (void*)pxdesc; + if (pxdesc) + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + else + wanted.varsize = 0; +#else + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + if (test_nino_flag(ni, v3_Extensions)) { + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) { + ni->security_id = cached->securid; + NInoSetDirty(ni); + } + } else cached = (struct CACHED_SECURID*)NULL; + + if (!cached) { + /* + * Do not use usid and gsid from former attributes, + * but recompute them to get repeatable results + * which can be kept in cache. + */ + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", + uid, gid); + usid = gsid = adminsid; + } +#if POSIXACLS + if (pxdesc) + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#endif + if (newattr) { + res = update_secur_descr(scx->vol, newattr, ni); + if (!res) { + /* adjust Windows read-only flag */ + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } + /* update cache, for subsequent use */ + if (test_nino_flag(ni, v3_Extensions)) { + wanted.securid = ni->security_id; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; +#if POSIXACLS + legacy.variable = wanted.variable; + legacy.varsize = wanted.varsize; +#else + legacy.variable = (void*)NULL; + legacy.varsize = 0; +#endif + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + res = -1; + } + } + return (res); +} + +/* + * Check whether user has ownership rights on a file + * + * Returns TRUE if allowed + * if not, errno tells why + */ + +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) +{ + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + uid_t processuid; + uid_t uid; + BOOL gotowner; + int allowed; + + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; +#endif + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } + } + return (allowed); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +#if POSIXACLS + +/* + * Set a new access or default Posix ACL to a file + * (or remove ACL if no input data) + * Validity of input data is checked after merging + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t processuid; + const SID *usid; + const SID *gsid; + uid_t uid; + uid_t gid; + int res; + mode_t mode; + BOOL isdir; + BOOL deflt; + BOOL exist; + int count; + struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc; + + /* get the current pxsec, either from cache or from old attribute */ + res = -1; + deflt = !strcmp(name,"system.posix_acl_default"); + if (size) + count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + else + count = 0; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = (struct POSIX_SECURITY*)NULL; + if ((!value + || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION)) + && (!deflt || isdir || (!size && !value))) { + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + mode = oldpxdesc->mode; + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + oldpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (oldpxdesc) { + if (deflt) + exist = oldpxdesc->defcnt > 0; + else + exist = oldpxdesc->acccnt > 3; + if ((exist && (flags & XATTR_CREATE)) + || (!exist && (flags & XATTR_REPLACE))) { + errno = (exist ? EEXIST : ENODATA); + } else { + mode = oldpxdesc->mode; + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + free(oldpxdesc); + } + free(oldattr); + } + } + } else + errno = EINVAL; + + if (newpxdesc) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) { + newpxdesc->mode &= ~S_ISGID; + } + res = ntfs_set_owner_mode(scx, ni, uid, gid, + newpxdesc->mode, newpxdesc); + } else + errno = EPERM; + free(newpxdesc); + } + return (res ? -1 : 0); +} + +/* + * Remove a default Posix ACL from a file + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) +{ + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); +} + +#endif + +/* + * Set a new NTFS ACL to a file + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + char *attr; + int res; + + res = -1; + if ((size > 0) + && !(flags & XATTR_CREATE) + && ntfs_valid_descr(value,size) + && (ntfs_attr_size(value) == size)) { + /* need copying in order to write */ + attr = (char*)ntfs_malloc(size); + if (attr) { + memcpy(attr,value,size); + res = update_secur_descr(scx->vol, attr, ni); + /* + * No need to invalidate standard caches : + * the relation between a securid and + * the associated protection is unchanged, + * only the relation between a file and + * its securid and protection is changed. + */ +#if CACHE_LEGACY_SIZE + /* + * we must however invalidate the legacy + * cache, which is based on inode numbers. + * For safety, invalidate even if updating + * failed. + */ + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = (char*)NULL; + legacy.varsize = 0; + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(attr); + } else + errno = ENOMEM; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Set new permissions to a file + * Checks user mapping has been defined before request for setting + * + * rejected if request is not originated by owner or root + * + * returns 0 on success + * -1 on failure, with errno = EIO + */ + +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t processuid; + uid_t uid; + uid_t gid; + int res; +#if POSIXACLS + BOOL isdir; + int pxsize; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + + /* get the current owner, either from cache or from old attribute */ + res = 0; + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } else + newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if POSIXACLS + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + + if (!res) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) + mode &= ~S_ISGID; +#if POSIXACLS + if (newpxdesc) { + newpxdesc->mode = mode; + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); + } else + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + errno = EPERM; + res = -1; /* neither owner nor root */ + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + if (newpxdesc) free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Create a default security descriptor for files whose descriptor + * cannot be inherited + */ + +int ntfs_sd_add_everyone(ntfs_inode *ni) +{ + /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + int ret, sd_len; + + /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ + /* + * Calculate security descriptor length. We have 2 sub-authorities in + * owner and group SIDs, but structure SID contain only one, so add + * 4 bytes to every SID. + */ + sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len); + if (!sd) + return -1; + + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; + + sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); + + sid = (SID*)((u8*)sid + sizeof(SID) + 4); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->group = cpu_to_le32((u8*)sid - (u8*)sd); + + acl = (ACL*)((u8*)sid + sizeof(SID) + 4); + acl->revision = ACL_REVISION; + acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); + acl->ace_count = const_cpu_to_le16(1); + sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); + + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); + ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */ + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 1; + ace->sid.sub_authority[0] = const_cpu_to_le32(0); + ace->sid.identifier_authority.value[5] = 1; + + ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd, + sd_len); + if (ret) + ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); + + free(sd); + return ret; +} + +/* + * Check whether user can access a file in a specific way + * + * Returns 1 if access is allowed, including user is root or no + * user mapping defined + * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX + * 0 and sets errno if there is a problem or if access + * is not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, + int accesstype) /* access type required (S_Ixxx values) */ +{ + int perm; + int res; + int allow; + struct stat stbuf; + + /* + * Always allow for root unless execution is requested. + * (was checked by fuse until kernel 2.6.29) + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] + || (!scx->uid + && (!(accesstype & S_IEXEC) + || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) + allow = 1; + else { + perm = ntfs_get_perm(scx, ni, accesstype); + if (perm >= 0) { + res = EACCES; + switch (accesstype) { + case S_IEXEC: + allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + break; + case S_IWRITE: + allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0; + break; + case S_IWRITE + S_IEXEC: + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD: + allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0; + break; + case S_IREAD + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); + break; + case S_IWRITE + S_IEXEC + S_ISVTX: + if (perm & S_ISVTX) { + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid)) + allow = 1; + else + allow = 2; + } else + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + default : + res = EINVAL; + allow = 0; + break; + } + if (!allow) + errno = res; + } else + allow = 0; + } + return (allow); +} + +#if 0 /* not needed any more */ + +/* + * Check whether user can access the parent directory + * of a file in a specific way + * + * Returns true if access is allowed, including user is root and + * no user mapping defined + * + * Sets errno if there is a problem or if not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype) +{ + int allow; + char *dirpath; + char *name; + ntfs_inode *ni; + ntfs_inode *dir_ni; + struct stat stbuf; + + allow = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); + if (dir_ni) { + allow = ntfs_allowed_access(scx, + dir_ni, accesstype); + ntfs_inode_close(dir_ni); + /* + * for an not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allow == 2)) { + ni = ntfs_pathname_to_inode(scx->vol, NULL, + path); + allow = FALSE; + if (ni) { + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid); + ntfs_inode_close(ni); + } + } + } + free(dirpath); + } + return (allow); /* errno is set if not allowed */ +} + +#endif + +/* + * Define a new owner/group to a file + * + * returns zero if successful + */ + +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + mode_t mode; + int perm; + BOOL isdir; + int res; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + BOOL pxdescbuilt = FALSE; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; + mode = cached->mode; +#if POSIXACLS + pxdesc = cached->pxdesc; + if (!pxdesc) + res = -1; +#endif + } else { + fileuid = 0; + filegid = 0; + mode = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (pxdesc) { + pxdescbuilt = TRUE; + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + mode = perm = pxdesc->mode; + } else + res = -1; +#else + mode = perm = ntfs_build_permissions(oldattr, + usid, gsid, isdir); + if (perm >= 0) { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } else + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; + /* clear setuid and setgid if owner has changed */ + /* unless request originated by root */ + if (uid && (fileuid != uid)) + mode &= 01777; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, pxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } +#if POSIXACLS + if (pxdescbuilt) + free(pxdesc); +#endif + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } + return (res ? -1 : 0); +} + +/* + * Define new owner/group and mode to a file + * + * returns zero if successful + */ + +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + BOOL isdir; + int res; +#if POSIXACLS + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } +#endif + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Build a security id for a descriptor inherited from + * parent directory the Windows way + */ + +static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, + const char *parentattr, BOOL fordir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *pphead; + const ACL *ppacl; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + int offpacl; + int offowner; + int offgroup; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + ACL *pnacl; + int parentattrsz; + char *newattr; + int newattrsz; + int aclsz; + int usidsz; + int gsidsz; + int pos; + le32 securid; + + parentattrsz = ntfs_attr_size(parentattr); + pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr; + if (scx->mapping[MAPUSERS]) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid); + if (!usid) + usid = adminsid; + if (!gsid) + gsid = adminsid; + } else { + /* + * If there is no user mapping, we have to copy owner + * and group from parent directory. + * Windows never has to do that, because it can always + * rely on a user mapping + */ + offowner = le32_to_cpu(pphead->owner); + usid = (const SID*)&parentattr[offowner]; + offgroup = le32_to_cpu(pphead->group); + gsid = (const SID*)&parentattr[offgroup]; + } + /* + * new attribute is smaller than parent's + * except for differences in SIDs which appear in + * owner, group and possible grants and denials in + * generic creator-owner and creator-group ACEs. + * For directories, an ACE may be duplicated for + * access and inheritance, so we double the count. + */ + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + newattrsz = parentattrsz + 3*usidsz + 3*gsidsz; + if (fordir) + newattrsz *= 2; + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + pnhead->control = SE_SELF_RELATIVE; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + /* + * locate and inherit DACL + * do not test SE_DACL_PRESENT (wrong for "DR Watson") + */ + pnhead->dacl = const_cpu_to_le32(0); + if (pphead->dacl) { + offpacl = le32_to_cpu(pphead->dacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->dacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_DACL_PRESENT; + } + } + /* + * locate and inherit SACL + */ + pnhead->sacl = const_cpu_to_le32(0); + if (pphead->sacl) { + offpacl = le32_to_cpu(pphead->sacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->sacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_SACL_PRESENT; + } + } + /* + * inherit or redefine owner + */ + memcpy(&newattr[pos],usid,usidsz); + pnhead->owner = cpu_to_le32(pos); + pos += usidsz; + /* + * inherit or redefine group + */ + memcpy(&newattr[pos],gsid,gsidsz); + pnhead->group = cpu_to_le32(pos); + pos += usidsz; + securid = setsecurityattr(scx->vol, + (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); + free(newattr); + } else + securid = const_cpu_to_le32(0); + return (securid); +} + +/* + * Get an inherited security id + * + * For Windows compatibility, the normal initial permission setting + * may be inherited from the parent directory instead of being + * defined by the creation arguments. + * + * The following creates an inherited id for that purpose. + * + * Note : the owner and group of parent directory are also + * inherited (which is not the case on Windows) if no user mapping + * is defined. + * + * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) + */ + +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir) +{ + struct CACHED_PERMISSIONS *cached; + char *parentattr; + le32 securid; + + securid = const_cpu_to_le32(0); + cached = (struct CACHED_PERMISSIONS*)NULL; + /* + * Try to get inherited id from cache + */ + if (test_nino_flag(dir_ni, v3_Extensions) + && dir_ni->security_id) { + cached = fetch_cache(scx, dir_ni); + if (cached) + securid = (fordir ? cached->inh_dirid + : cached->inh_fileid); + } + /* + * Not cached or not available in cache, compute it all + * Note : if parent directory has no id, it is not cacheable + */ + if (!securid) { + parentattr = getsecurityattr(scx->vol, dir_ni); + if (parentattr) { + securid = build_inherited_id(scx, + parentattr, fordir); + free(parentattr); + /* + * Store the result into cache for further use + */ + if (securid) { + cached = fetch_cache(scx, dir_ni); + if (cached) { + if (fordir) + cached->inh_dirid = securid; + else + cached->inh_fileid = securid; + } + } + } + } + return (securid); +} + +/* + * Link a group to a member of group + * + * Returns 0 if OK, -1 (and errno set) if error + */ + +static int link_single_group(struct MAPPING *usermapping, struct passwd *user, + gid_t gid) +{ + struct group *group; + char **grmem; + int grcnt; + gid_t *groups; + int res; + + res = 0; + group = getgrgid(gid); + if (group && group->gr_mem) { + grcnt = usermapping->grcnt; + groups = usermapping->groups; + grmem = group->gr_mem; + while (*grmem && strcmp(user->pw_name, *grmem)) + grmem++; + if (*grmem) { + if (!grcnt) + groups = (gid_t*)malloc(sizeof(gid_t)); + else + groups = (gid_t*)realloc(groups, + (grcnt+1)*sizeof(gid_t)); + if (groups) + groups[grcnt++] = gid; + else { + res = -1; + errno = ENOMEM; + } + } + usermapping->grcnt = grcnt; + usermapping->groups = groups; + } + return (res); +} + + +/* + * Statically link group to users + * This is based on groups defined in /etc/group and does not take + * the groups dynamically set by setgroups() nor any changes in + * /etc/group into account + * + * Only mapped groups and root group are linked to mapped users + * + * Returns 0 if OK, -1 (and errno set) if error + * + */ + +static int link_group_members(struct SECURITY_CONTEXT *scx) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + struct passwd *user; + int res; + + res = 0; + for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res; + usermapping=usermapping->next) { + usermapping->grcnt = 0; + usermapping->groups = (gid_t*)NULL; + user = getpwuid(usermapping->xid); + if (user && user->pw_name) { + for (groupmapping=scx->mapping[MAPGROUPS]; + groupmapping && !res; + groupmapping=groupmapping->next) { + if (link_single_group(usermapping, user, + groupmapping->xid)) + res = -1; + } + if (!res && link_single_group(usermapping, + user, (gid_t)0)) + res = -1; + } + } + return (res); +} + +/* + * Apply default single user mapping + * returns zero if successful + */ + +static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, const SID *usid) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + SID *sid; + int sidsz; + int res; + + res = -1; + sidsz = ntfs_sid_size(usid); + sid = (SID*)ntfs_malloc(sidsz); + if (sid) { + memcpy(sid,usid,sidsz); + usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (usermapping) { + groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (groupmapping) { + usermapping->sid = sid; + usermapping->xid = uid; + usermapping->next = (struct MAPPING*)NULL; + groupmapping->sid = sid; + groupmapping->xid = gid; + groupmapping->next = (struct MAPPING*)NULL; + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + res = 0; + } + } + } + return (res); +} + +/* + * Make sure there are no ambiguous mapping + * Ambiguous mapping may lead to undesired configurations and + * we had rather be safe until the consequences are understood + */ + +#if 0 /* not activated for now */ + +static BOOL check_mapping(const struct MAPPING *usermapping, + const struct MAPPING *groupmapping) +{ + const struct MAPPING *mapping1; + const struct MAPPING *mapping2; + BOOL ambiguous; + + ambiguous = FALSE; + for (mapping1=usermapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + for (mapping1=groupmapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + return (ambiguous); +} + +#endif + +#if 0 /* not used any more */ + +/* + * Try and apply default single user mapping + * returns zero if successful + */ + +static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + char *securattr; + const SID *usid; + int res; + + res = -1; + ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); + if (ni) { + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; + if (ntfs_is_user_sid(usid)) + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); + free(securattr); + } + ntfs_inode_close(ni); + } + return (res); +} + +#endif + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Build the user mapping + * - according to a mapping file if defined (or default present), + * - or try default single user mapping if possible + * + * The mapping is specific to a mounted device + * No locking done, mounting assumed non multithreaded + * + * returns zero if mapping is successful + * (failure should not be interpreted as an error) + */ + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) +{ + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + ntfs_inode *ni; + int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; + + /* be sure not to map anything until done */ + scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; + scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL; + + if (!usermap_path) usermap_path = MAPPINGFILE; + if (usermap_path[0] == '/') { + fd = open(usermap_path,O_RDONLY); + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } else + firstitem = (struct MAPLIST*)NULL; + } else { + ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path); + if (ni) { + firstitem = ntfs_read_mapping(localread, ni); + ntfs_inode_close(ni); + } else + firstitem = (struct MAPLIST*)NULL; + } + + + if (firstitem) { + usermapping = ntfs_do_user_mapping(firstitem); + groupmapping = ntfs_do_group_mapping(firstitem); + if (usermapping && groupmapping) { + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + } else + ntfs_log_error("There were no valid user or no valid group\n"); + /* now we can free the memory copy of input text */ + /* and rely on internal representation */ + while (firstitem) { + item = firstitem->next; + free(firstitem); + firstitem = item; + } + } else { + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) + ntfs_log_info("Using default user mapping\n"); + } + } + return (!scx->mapping[MAPUSERS] || link_group_members(scx)); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs attribute into an extended attribute + * The attribute is returned according to cpu endianness + */ + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) +{ + u32 attrib; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + outsize = sizeof(FILE_ATTR_FLAGS); + if (size >= outsize) { + if (value) + memcpy(value,&attrib,outsize); + else + errno = EINVAL; + } + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Return the ntfs attribute into an extended attribute + * The attribute is expected according to cpu endianness + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + u32 attrib; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = -1; + if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { + if (!(flags & XATTR_CREATE)) { + /* copy to avoid alignment problems */ + memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); + settable = FILE_ATTR_SETTABLE; + res = 0; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); + } + } else + errno = EEXIST; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open $Secure once for all + * returns zero if it succeeds + * non-zero if it fails. This is not an error (on NTFS v1.x) + */ + + +int ntfs_open_secure(ntfs_volume *vol) +{ + ntfs_inode *ni; + int res; + + res = -1; + vol->secure_ni = (ntfs_inode*)NULL; + vol->secure_xsii = (ntfs_index_context*)NULL; + vol->secure_xsdh = (ntfs_index_context*)NULL; + if (vol->major_ver >= 3) { + /* make sure this is a genuine $Secure inode 9 */ + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (ni && (ni->mft_no == 9)) { + vol->secure_reentry = 0; + vol->secure_xsii = ntfs_index_ctx_get(ni, + sii_stream, 4); + vol->secure_xsdh = ntfs_index_ctx_get(ni, + sdh_stream, 4); + if (ni && vol->secure_xsii && vol->secure_xsdh) { + vol->secure_ni = ni; + res = 0; + } + } + } + return (res); +} + +/* + * Final cleaning + * Allocated memory is freed to facilitate the detection of memory leaks + */ + +void ntfs_close_secure(struct SECURITY_CONTEXT *scx) +{ + ntfs_volume *vol; + + vol = scx->vol; + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsii); + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_inode_close(vol->secure_ni); + + } + ntfs_free_mapping(scx->mapping); + free_caches(scx); +} + +/* + * API for direct access to security descriptors + * based on Win32 API + */ + + +/* + * Selective feeding of a security descriptor into user buffer + * + * Returns TRUE if successful + */ + +static BOOL feedsecurityattr(const char *attr, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + const ACL *pdacl; + const ACL *psacl; + const SID *pusid; + const SID *pgsid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int daclsz; + unsigned int saclsz; + unsigned int usidsz; + unsigned int gsidsz; + unsigned int size; /* size of requested attributes */ + BOOL ok; + unsigned int pos; + unsigned int avail; + le16 control; + + avail = 0; + control = SE_SELF_RELATIVE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + size = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* locate DACL if requested and available */ + if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) { + offdacl = le32_to_cpu(phead->dacl); + pdacl = (const ACL*)&attr[offdacl]; + daclsz = le16_to_cpu(pdacl->size); + size += daclsz; + avail |= DACL_SECURITY_INFORMATION; + } else + offdacl = daclsz = 0; + + /* locate owner if requested and available */ + offowner = le32_to_cpu(phead->owner); + if (offowner && (selection & OWNER_SECURITY_INFORMATION)) { + /* find end of USID */ + pusid = (const SID*)&attr[offowner]; + usidsz = ntfs_sid_size(pusid); + size += usidsz; + avail |= OWNER_SECURITY_INFORMATION; + } else + offowner = usidsz = 0; + + /* locate group if requested and available */ + offgroup = le32_to_cpu(phead->group); + if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) { + /* find end of GSID */ + pgsid = (const SID*)&attr[offgroup]; + gsidsz = ntfs_sid_size(pgsid); + size += gsidsz; + avail |= GROUP_SECURITY_INFORMATION; + } else + offgroup = gsidsz = 0; + + /* locate SACL if requested and available */ + if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) { + /* find end of SACL */ + offsacl = le32_to_cpu(phead->sacl); + psacl = (const ACL*)&attr[offsacl]; + saclsz = le16_to_cpu(psacl->size); + size += saclsz; + avail |= SACL_SECURITY_INFORMATION; + } else + offsacl = saclsz = 0; + + /* + * Check having enough size in destination buffer + * (required size is returned nevertheless so that + * the request can be reissued with adequate size) + */ + if (size > buflen) { + *psize = size; + errno = EINVAL; + ok = FALSE; + } else { + if (selection & OWNER_SECURITY_INFORMATION) + control |= phead->control & SE_OWNER_DEFAULTED; + if (selection & GROUP_SECURITY_INFORMATION) + control |= phead->control & SE_GROUP_DEFAULTED; + if (selection & DACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + if (selection & SACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy header and feed new flags, even if no detailed data + */ + memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf; + pnhead->control = control; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* copy DACL if requested and available */ + if (selection & avail & DACL_SECURITY_INFORMATION) { + pnhead->dacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offdacl],daclsz); + pos += daclsz; + } else + pnhead->dacl = const_cpu_to_le32(0); + + /* copy SACL if requested and available */ + if (selection & avail & SACL_SECURITY_INFORMATION) { + pnhead->sacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offsacl],saclsz); + pos += saclsz; + } else + pnhead->sacl = const_cpu_to_le32(0); + + /* copy owner if requested and available */ + if (selection & avail & OWNER_SECURITY_INFORMATION) { + pnhead->owner = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offowner],usidsz); + pos += usidsz; + } else + pnhead->owner = const_cpu_to_le32(0); + + /* copy group if requested and available */ + if (selection & avail & GROUP_SECURITY_INFORMATION) { + pnhead->group = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offgroup],gsidsz); + pos += gsidsz; + } else + pnhead->group = const_cpu_to_le32(0); + if (pos != size) + ntfs_log_error("Error in security descriptor size\n"); + *psize = size; + ok = TRUE; + } + + return (ok); +} + +/* + * Merge a new security descriptor into the old one + * and assign to designated file + * + * Returns TRUE if successful + */ + +static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr, + const char *newattr, u32 selection, ntfs_inode *ni) +{ + const SECURITY_DESCRIPTOR_RELATIVE *oldhead; + const SECURITY_DESCRIPTOR_RELATIVE *newhead; + SECURITY_DESCRIPTOR_RELATIVE *targhead; + const ACL *pdacl; + const ACL *psacl; + const SID *powner; + const SID *pgroup; + int offdacl; + int offsacl; + int offowner; + int offgroup; + unsigned int size; + le16 control; + char *target; + int pos; + int oldattrsz; + int newattrsz; + BOOL ok; + + ok = FALSE; /* default return */ + oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; + newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr; + oldattrsz = ntfs_attr_size(oldattr); + newattrsz = ntfs_attr_size(newattr); + target = (char*)ntfs_malloc(oldattrsz + newattrsz); + if (target) { + targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + control = SE_SELF_RELATIVE; + /* + * copy new DACL if selected + * or keep old DACL if any + */ + if ((selection & DACL_SECURITY_INFORMATION) ? + newhead->dacl : oldhead->dacl) { + if (selection & DACL_SECURITY_INFORMATION) { + offdacl = le32_to_cpu(newhead->dacl); + pdacl = (const ACL*)&newattr[offdacl]; + } else { + offdacl = le32_to_cpu(oldhead->dacl); + pdacl = (const ACL*)&oldattr[offdacl]; + } + size = le16_to_cpu(pdacl->size); + memcpy(&target[pos], pdacl, size); + targhead->dacl = cpu_to_le32(pos); + pos += size; + } else + targhead->dacl = const_cpu_to_le32(0); + if (selection & DACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_PROTECTED); + if (newhead->control & SE_DACL_AUTO_INHERIT_REQ) + control |= SE_DACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + /* + * copy new SACL if selected + * or keep old SACL if any + */ + if ((selection & SACL_SECURITY_INFORMATION) ? + newhead->sacl : oldhead->sacl) { + if (selection & SACL_SECURITY_INFORMATION) { + offsacl = le32_to_cpu(newhead->sacl); + psacl = (const ACL*)&newattr[offsacl]; + } else { + offsacl = le32_to_cpu(oldhead->sacl); + psacl = (const ACL*)&oldattr[offsacl]; + } + size = le16_to_cpu(psacl->size); + memcpy(&target[pos], psacl, size); + targhead->sacl = cpu_to_le32(pos); + pos += size; + } else + targhead->sacl = const_cpu_to_le32(0); + if (selection & SACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_PROTECTED); + if (newhead->control & SE_SACL_AUTO_INHERIT_REQ) + control |= SE_SACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy new OWNER if selected + * or keep old OWNER if any + */ + if ((selection & OWNER_SECURITY_INFORMATION) ? + newhead->owner : oldhead->owner) { + if (selection & OWNER_SECURITY_INFORMATION) { + offowner = le32_to_cpu(newhead->owner); + powner = (const SID*)&newattr[offowner]; + } else { + offowner = le32_to_cpu(oldhead->owner); + powner = (const SID*)&oldattr[offowner]; + } + size = ntfs_sid_size(powner); + memcpy(&target[pos], powner, size); + targhead->owner = cpu_to_le32(pos); + pos += size; + } else + targhead->owner = const_cpu_to_le32(0); + if (selection & OWNER_SECURITY_INFORMATION) + control |= newhead->control & SE_OWNER_DEFAULTED; + else + control |= oldhead->control & SE_OWNER_DEFAULTED; + /* + * copy new GROUP if selected + * or keep old GROUP if any + */ + if ((selection & GROUP_SECURITY_INFORMATION) ? + newhead->group : oldhead->group) { + if (selection & GROUP_SECURITY_INFORMATION) { + offgroup = le32_to_cpu(newhead->group); + pgroup = (const SID*)&newattr[offgroup]; + control |= newhead->control + & SE_GROUP_DEFAULTED; + } else { + offgroup = le32_to_cpu(oldhead->group); + pgroup = (const SID*)&oldattr[offgroup]; + control |= oldhead->control + & SE_GROUP_DEFAULTED; + } + size = ntfs_sid_size(pgroup); + memcpy(&target[pos], pgroup, size); + targhead->group = cpu_to_le32(pos); + pos += size; + } else + targhead->group = const_cpu_to_le32(0); + if (selection & GROUP_SECURITY_INFORMATION) + control |= newhead->control & SE_GROUP_DEFAULTED; + else + control |= oldhead->control & SE_GROUP_DEFAULTED; + targhead->revision = SECURITY_DESCRIPTOR_REVISION; + targhead->alignment = 0; + targhead->control = control; + ok = !update_secur_descr(vol, target, ni); + free(target); + } + return (ok); +} + +/* + * Return the security descriptor of a file + * This is intended to be similar to GetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI GetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION RequestedInformation, + * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, + * __in DWORD nLength, + * __out LPDWORD lpnLengthNeeded + * ); + * + */ + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + ntfs_inode *ni; + char *attr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attr = getsecurityattr(scapi->security.vol, ni); + if (attr) { + if (feedsecurityattr(attr,selection, + buf,buflen,psize)) { + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(attr); + } + ntfs_inode_close(ni); + } else + errno = ENOENT; + if (!res) *psize = 0; + } else + errno = EINVAL; /* do not clear *psize */ + return (res); +} + + +/* + * Set the security descriptor of a file or directory + * This is intended to be similar to SetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI SetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION SecurityInformation, + * __in PSECURITY_DESCRIPTOR pSecurityDescriptor + * ); + */ + +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + int attrsz; + BOOL missing; + char *oldattr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && attr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + attrsz = ntfs_attr_size(attr); + /* if selected, owner and group must be present or defaulted */ + missing = ((selection & OWNER_SECURITY_INFORMATION) + && !phead->owner + && !(phead->control & SE_OWNER_DEFAULTED)) + || ((selection & GROUP_SECURITY_INFORMATION) + && !phead->group + && !(phead->control & SE_GROUP_DEFAULTED)); + if (!missing + && (phead->control & SE_SELF_RELATIVE) + && ntfs_valid_descr(attr, attrsz)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, + NULL, path); + if (ni) { + oldattr = getsecurityattr(scapi->security.vol, + ni); + if (oldattr) { + if (mergesecurityattr( + scapi->security.vol, + oldattr, attr, + selection, ni)) { + if (test_nino_flag(ni, + v3_Extensions)) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(oldattr); + } + ntfs_inode_close(ni); + } + } else + errno = EINVAL; + } else + errno = EINVAL; + return (res); +} + + +/* + * Return the attributes of a file + * This is intended to be similar to GetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) + * + * The Win32 API is : + * + * DWORD WINAPI GetFileAttributes( + * __in LPCTSTR lpFileName + * ); + */ + +int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path) +{ + ntfs_inode *ni; + s32 attrib; + + attrib = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + + ntfs_inode_close(ni); + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (attrib); +} + + +/* + * Set attributes to a file or directory + * This is intended to be similar to SetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * Only a few flags can be set (same list as Win32) + * + * returns zero if unsuccessful (following Win32 conventions) + * nonzero if successful + * + * The Win32 API is : + * + * BOOL WINAPI SetFileAttributes( + * __in LPCTSTR lpFileName, + * __in DWORD dwFileAttributes + * ); + */ + +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib) +{ + ntfs_inode *ni; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + settable = FILE_ATTR_SETTABLE; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoSetDirty(ni); + } + if (!ntfs_inode_close(ni)) + res = -1; + } else + errno = ENOENT; + } + return (res); +} + + +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context) +{ + ntfs_inode *ni; + BOOL ok; + s64 pos; + + ok = FALSE; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && callback) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + pos = 0; + ntfs_readdir(ni,&pos,context,callback); + ok = !ntfs_inode_close(ni); + } else { + ntfs_inode_close(ni); + errno = ENOTDIR; + } + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (ok); +} + +/* + * read $SDS (for auditing security data) + * + * Returns the number or read bytes, or -1 if there is an error + */ + +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset) +{ + int got; + + got = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + if (scapi->security.vol->secure_ni) + got = ntfs_attr_data_read(scapi->security.vol->secure_ni, + STREAM_SDS, 4, buf, size, offset); + else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (got); +} + +/* + * read $SII (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SII_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsii; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsii = scapi->security.vol->secure_xsii; + if (xsii) { + if (!entry) { + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SII_INDEX_KEY), xsii); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsii->entry; + } else + ret = ntfs_index_next(entry,xsii); + if (!ret) + errno = ENODATA; + } else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (ret); +} + +/* + * read $SDH (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SDH_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsdh; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsdh = scapi->security.vol->secure_xsdh; + if (xsdh) { + if (!entry) { + key.hash = const_cpu_to_le32(0); + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsdh->entry; + } else + ret = ntfs_index_next(entry,xsdh); + if (!ret) + errno = ENODATA; + } else errno = ENOTSUP; + } else + errno = EINVAL; + return (ret); +} + +/* + * Get the mapped user SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf) +{ + const SID *usid; + BIGSID defusid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid); + if (usid) { + size = ntfs_sid_size(usid); + memcpy(buf,usid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the mapped group SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf) +{ + const SID *gsid; + BIGSID defgsid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid); + if (gsid) { + size = ntfs_sid_size(gsid); + memcpy(buf,gsid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the user mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid) +{ + int uid; + + uid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) { + if (ntfs_same_sid(usid,adminsid)) + uid = 0; + else { + uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid); + if (!uid) { + uid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (uid); +} + +/* + * Get the group mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) +{ + int gid; + + gid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) { + if (ntfs_same_sid(gsid,adminsid)) + gid = 0; + else { + gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid); + if (!gid) { + gid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (gid); +} + +/* + * Initializations before calling ntfs_get_file_security() + * ntfs_set_file_security() and ntfs_read_directory() + * + * Only allowed for root + * + * Returns an (obscured) struct SECURITY_API* needed for further calls + * NULL if not root (EPERM) or device is mounted (EBUSY) + */ + +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags) +{ + ntfs_volume *vol; + unsigned long mntflag; + int mnt; + struct SECURITY_API *scapi; + struct SECURITY_CONTEXT *scx; + + scapi = (struct SECURITY_API*)NULL; + mnt = ntfs_check_if_mounted(device, &mntflag); + if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) { + vol = ntfs_mount(device, flags); + if (vol) { + scapi = (struct SECURITY_API*) + ntfs_malloc(sizeof(struct SECURITY_API)); + if (!ntfs_volume_get_free_space(vol) + && scapi) { + scapi->magic = MAGIC_API; + scapi->seccache = (struct PERMISSIONS_CACHE*)NULL; + scx = &scapi->security; + scx->vol = vol; + scx->uid = getuid(); + scx->gid = getgid(); + scx->pseccache = &scapi->seccache; + scx->vol->secure_flags = 0; + /* accept no mapping and no $Secure */ + ntfs_build_mapping(scx,(const char*)NULL,TRUE); + ntfs_open_secure(vol); + } else { + if (scapi) + free(scapi); + else + errno = ENOMEM; + mnt = ntfs_umount(vol,FALSE); + scapi = (struct SECURITY_API*)NULL; + } + } + } else + if (getuid()) + errno = EPERM; + else + errno = EBUSY; + return (scapi); +} + +/* + * Leaving after ntfs_initialize_file_security() + * + * Returns FALSE if FAILED + */ + +BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) +{ + int ok; + ntfs_volume *vol; + + ok = FALSE; + if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { + vol = scapi->security.vol; + ntfs_close_secure(&scapi->security); + free(scapi); + if (!ntfs_umount(vol, 0)) + ok = TRUE; + } + return (ok); +} + diff --git a/lib/libntfs/orig/source/security.h b/lib/libntfs/orig/source/security.h new file mode 100644 index 0000000..9d7dd33 --- /dev/null +++ b/lib/libntfs/orig/source/security.h @@ -0,0 +1,361 @@ +/* + * security.h - Exports for handling security/ACLs in NTFS. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SECURITY_H +#define _NTFS_SECURITY_H + +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "dir.h" + +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif + +typedef u16 be16; +typedef u32 be32; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) +#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) +#else +#define const_cpu_to_be16(x) (x) +#define const_cpu_to_be32(x) (x) +#endif + +/* + * item in the mapping list + */ + +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ +}; + +/* + * Entry in the permissions cache + * Note : this cache is not organized as a generic cache + */ + +struct CACHED_PERMISSIONS { + uid_t uid; + gid_t gid; + le32 inh_fileid; + le32 inh_dirid; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + unsigned int pxdescsize:16; +#endif + unsigned int mode:12; + unsigned int valid:1; +} ; + +/* + * Entry in the permissions cache for directories with no security_id + */ + +struct CACHED_PERMISSIONS_LEGACY { + struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 mft_no; + struct CACHED_PERMISSIONS perm; +} ; + +/* + * Entry in the securid cache + */ + +struct CACHED_SECURID { + struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + uid_t uid; + gid_t gid; + unsigned int dmode; + le32 securid; +} ; + +/* + * Header of the security cache + * (has no cache structure by itself) + */ + +struct CACHED_PERMISSIONS_HEADER { + unsigned int last; + /* statistics for permissions */ + unsigned long p_writes; + unsigned long p_reads; + unsigned long p_hits; +} ; + +/* + * The whole permissions cache + */ + +struct PERMISSIONS_CACHE { + struct CACHED_PERMISSIONS_HEADER head; + struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */ +} ; + +/* + * Security flags values + */ + +enum { + SECURITY_DEFAULT, /* rely on fuse for permissions checking */ + SECURITY_RAW, /* force same ownership/permissions on files */ + SECURITY_ACL, /* enable Posix ACLs (when compiled in) */ + SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ + SECURITY_STATICGRPS, /* use static groups for access control */ + SECURITY_WANTED /* a security related option was present */ +} ; + +/* + * Security context, needed by most security functions + */ + +enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; + +struct SECURITY_CONTEXT { + ntfs_volume *vol; + struct MAPPING *mapping[MAPCOUNT]; + struct PERMISSIONS_CACHE **pseccache; + uid_t uid; /* uid of user requesting (not the mounter) */ + gid_t gid; /* gid of user requesting (not the mounter) */ + pid_t tid; /* thread id of thread requesting */ + mode_t umask; /* umask of requesting thread */ + } ; + +#if POSIXACLS + +/* + * Posix ACL structures + */ + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} __attribute__((__packed__)); + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + s32 alignment[0]; + struct POSIX_ACL acl; +} ; + +/* + * Posix tags, cpu-endian 16 bits + */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ +} ; + +#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK) + +/* + * Posix permissions, cpu-endian 16 bits + */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ +} ; + +#define POSIX_VERSION 2 + +#endif + +extern BOOL ntfs_guid_is_zero(const GUID *guid); +extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); + +/** + * ntfs_sid_is_valid - determine if a SID is valid + * @sid: SID for which to determine if it is valid + * + * Determine if the SID pointed to by @sid is valid. + * + * Return TRUE if it is valid and FALSE otherwise. + */ +static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) +{ + if (!sid || sid->revision != SID_REVISION || + sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) + return FALSE; + return TRUE; +} + +extern int ntfs_sid_to_mbs_size(const SID *sid); +extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, + size_t sid_str_size); +extern void ntfs_generate_guid(GUID *guid); +extern int ntfs_sd_add_everyone(ntfs_inode *ni); + +extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, + const u32 len); + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, int accesstype); +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype); + +#if POSIXACLS +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); +#else +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir); +#endif +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + mode_t mode, struct POSIX_SECURITY *pxdesc); +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#endif +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir); +int ntfs_open_secure(ntfs_volume *vol); +void ntfs_close_secure(struct SECURITY_CONTEXT *scx); + +#if POSIXACLS + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); +#endif + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + + +/* + * Security API for direct access to security descriptors + * based on Win32 API + */ + +#define MAGIC_API 0x09042009 + +struct SECURITY_API { + u32 magic; + struct SECURITY_CONTEXT security; + struct PERMISSIONS_CACHE *seccache; +} ; + +/* + * The following constants are used in interfacing external programs. + * They are not to be stored on disk and must be defined in their + * native cpu representation. + * When disk representation (le) is needed, use SE_DACL_PRESENT, etc. + */ +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize); +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr); +int ntfs_get_file_attributes(struct SECURITY_API *scapi, + const char *path); +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib); +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context); +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset); +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags); +BOOL ntfs_leave_file_security(struct SECURITY_API *scx); + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf); +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid); +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid); + +#endif /* defined _NTFS_SECURITY_H */ diff --git a/lib/libntfs/orig/source/support.h b/lib/libntfs/orig/source/support.h new file mode 100644 index 0000000..6af4761 --- /dev/null +++ b/lib/libntfs/orig/source/support.h @@ -0,0 +1,85 @@ +/* + * support.h - Useful definitions and macros. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SUPPORT_H +#define _NTFS_SUPPORT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDDEF_H +#include +#endif + +/* + * Our mailing list. Use this define to prevent typos in email address. + */ +#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net" + +/* + * Generic macro to convert pointers to values for comparison purposes. + */ +#ifndef p2n +#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) +#endif + +/* + * The classic min and max macros. + */ +#ifndef min +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif + +/* + * Useful macro for determining the offset of a struct member. + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Simple bit operation macros. NOTE: These are NOT atomic. + */ +#define test_bit(bit, var) ((var) & (1 << (bit))) +#define set_bit(bit, var) (var) |= 1 << (bit) +#define clear_bit(bit, var) (var) &= ~(1 << (bit)) + +#define test_and_set_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + set_bit(bit, var); \ + old_state; \ +}) + +#define test_and_clear_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + clear_bit(bit, var); \ + old_state; \ +}) + +#endif /* defined _NTFS_SUPPORT_H */ + diff --git a/lib/libntfs/orig/source/types.h b/lib/libntfs/orig/source/types.h new file mode 100644 index 0000000..77758ea --- /dev/null +++ b/lib/libntfs/orig/source/types.h @@ -0,0 +1,141 @@ +/* + * types.h - Misc type definitions not related to on-disk structure. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_TYPES_H +#define _NTFS_TYPES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STDINT_H || !HAVE_CONFIG_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef GEKKO +#include +#include "compat.h" +#else /* GEKKO */ + +typedef uint8_t u8; /* Unsigned types of an exact size */ +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; /* Signed types of an exact size */ +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#endif /* GEKKO */ + +typedef u16 le16; +typedef u32 le32; +typedef u64 le64; + +/* + * Declare sle{16,32,64} to be unsigned because we do not want sign extension + * on BE architectures. + */ +typedef u16 sle16; +typedef u32 sle32; +typedef u64 sle64; + +typedef u16 ntfschar; /* 2-byte Unicode character type. */ +#define UCHAR_T_SIZE_BITS 1 + +/* + * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN + * and VCN, to allow for type checking and better code readability. + */ +typedef s64 VCN; +typedef sle64 leVCN; +typedef s64 LCN; +typedef sle64 leLCN; + +/* + * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit + * values. We define our own type LSN, to allow for type checking and better + * code readability. + */ +typedef s64 LSN; +typedef sle64 leLSN; + +/* + * Cygwin has a collision between our BOOL and 's + * As long as this file will be included after were fine. + */ +#ifndef GEKKO +#ifndef _WINDEF_H +/** + * enum BOOL - These are just to make the code more readable... + */ +typedef enum { +#ifndef FALSE + FALSE = 0, +#endif +#ifndef NO + NO = 0, +#endif +#ifndef ZERO + ZERO = 0, +#endif +#ifndef TRUE + TRUE = 1, +#endif +#ifndef YES + YES = 1, +#endif +#ifndef ONE + ONE = 1, +#endif +} BOOL; +#endif /* defined _WINDEF_H */ +#endif /* defined GECKO */ + +/** + * enum IGNORE_CASE_BOOL - + */ +typedef enum { + CASE_SENSITIVE = 0, + IGNORE_CASE = 1, +} IGNORE_CASE_BOOL; + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) +#define STATUS_KEEP_SEARCHING (-3) +#define STATUS_NOT_FOUND (-4) + +/* + * Force alignment in a struct if required by processor + */ +union ALIGNMENT { + u64 u64align; + void *ptralign; +} ; + +#endif /* defined _NTFS_TYPES_H */ + diff --git a/lib/libntfs/orig/source/unistr.c b/lib/libntfs/orig/source/unistr.c new file mode 100644 index 0000000..ffaabe0 --- /dev/null +++ b/lib/libntfs/orig/source/unistr.c @@ -0,0 +1,1521 @@ +/** + * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2011 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_WCHAR_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +#include +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#include "compat.h" +#include "attrib.h" +#include "types.h" +#include "unistr.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ + +/* + * IMPORTANT + * ========= + * + * All these routines assume that the Unicode characters are in little endian + * encoding inside the strings!!! + */ + +static int use_utf8 = 1; /* use UTF-8 encoding for file names */ + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +/** + * This variable controls whether or not automatic normalization form conversion + * should be performed when translating NTFS unicode file names to UTF-8. + * Defaults to on, but can be controlled from the outside using the function + * int ntfs_macosx_normalize_filenames(int normalize); + */ +static int nfconvert_utf8 = 1; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +/* + * This is used by the name collation functions to quickly determine what + * characters are (in)valid. + */ +#if 0 +static const u8 legal_ansi_char_array[0x40] = { + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, + + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, +}; +#endif + +/** + * ntfs_names_are_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) + * + * Compare the names @s1 and @s2 and return TRUE (1) if the names are + * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, + * the @upcase table is used to perform a case insensitive comparison. + */ +BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size) +{ + if (s1_len != s2_len) + return FALSE; + if (!s1_len) + return TRUE; + if (ic == CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: + TRUE; +} + +/* + * ntfs_names_full_collate() fully collate two Unicode names + * + * @name1: first Unicode name to compare + * @name1_len: length of first Unicode name to compare + * @name2: second Unicode name to compare + * @name2_len: length of second Unicode name to compare + * @ic: either CASE_SENSITIVE or IGNORE_CASE + * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) + * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) + * + * -1 if the first name collates before the second one, + * 0 if the names match, + * 1 if the second name collates before the first one, or + * + */ +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 cnt; + u16 c1, c2; + u16 u1, u2; + +#ifdef DEBUG + if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); + exit(1); + } +#endif + cnt = min(name1_len, name2_len); + if (cnt > 0) { + if (ic == CASE_SENSITIVE) { + while (--cnt && (*name1 == *name2)) { + name1++; + name2++; + } + u1 = c1 = le16_to_cpu(*name1); + u2 = c2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + name1++; + u1 = le16_to_cpu(*name1); + name2++; + u2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { + do { + u1 = c1 = le16_to_cpu(*name1); + name1++; + u2 = c2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + return 0; +} + +/** + * ntfs_ucsncmp - compare two little endian Unicode strings + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * The strings in little endian format and appropriate le16_to_cpu() + * conversion is performed on non-little endian machines. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) +{ + ntfschar c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2) { + ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + c2 = le16_to_cpu(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * @upcase: upcase table + * @upcase_size: upcase table size in Unicode characters + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * ignoring case. The strings in little endian format and appropriate + * le16_to_cpu() conversion is performed on non-little endian machines. + * + * Each character is uppercased using the @upcase table before the comparison. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size) +{ + u16 c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2 || !upcase) { + ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + if ((c1 = le16_to_cpu(s1[i])) < upcase_size) + c1 = le16_to_cpu(upcase[c1]); + if ((c2 = le16_to_cpu(s2[i])) < upcase_size) + c2 = le16_to_cpu(upcase[c2]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsnlen - determine the length of a little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return the number of Unicode characters in the little endian Unicode + * string @s up to a maximum of maxlen Unicode characters, not including + * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s + * and @s + @maxlen, @maxlen is returned. + * + * This function never looks beyond @s + @maxlen. + */ +u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) +{ + u32 i; + + for (i = 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; +} + +/** + * ntfs_ucsndup - duplicate little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return a pointer to a new little endian Unicode string which is a duplicate + * of the string s. Memory for the new string is obtained with ntfs_malloc(3), + * and can be freed with free(3). + * + * A maximum of @maxlen Unicode characters are copied and a terminating + * (ntfschar)'\0' little endian Unicode character is added. + * + * This function never looks beyond @s + @maxlen. + * + * Return a pointer to the new little endian Unicode string on success and NULL + * on failure with errno set to the error code. + */ +ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) +{ + ntfschar *dst; + u32 len; + + len = ntfs_ucsnlen(s, maxlen); + dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); + if (dst) { + memcpy(dst, s, len * sizeof(ntfschar)); + dst[len] = cpu_to_le16(L'\0'); + } + return dst; +} + +/** + * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent + * @name: + * @name_len: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 i; + u16 u; + + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < upcase_len) + name[i] = upcase[u]; +} + +/** + * ntfs_name_locase - Map a Unicode name to its lowercase equivalent + */ +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) +{ + u32 i; + u16 u; + + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; +} + +/** + * ntfs_file_value_upcase - Convert a filename to upper case + * @file_name_attr: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len) +{ + ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, + file_name_attr->file_name_length, upcase, upcase_len); +} + +/* + NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough + for now]) for path names, but the Unicode code points need to be + converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI, + glibc does this even without a locale in a hard-coded fashion as that + appears to be is easy because the low 7-bit ASCII range appears to be + available in all charsets but it does not convert anything if + there was some error with the locale setup or none set up like + when mount is called during early boot where he (by policy) do + not use locales (and may be not available if /usr is not yet mounted), + so this patch fixes the resulting issues for systems which use + UTF-8 and for others, specifying the locale in fstab brings them + the encoding which they want. + + If no locale is defined or there was a problem with setting one + up and whenever nl_langinfo(CODESET) returns a sting starting with + "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix + the bug where NTFS-3G does not show any path names which include + international characters!!! (and also fails on creating them) as result. + + Author: Bernhard Kaindl + Jean-Pierre Andre made it compliant with RFC3629/RFC2781. +*/ + +/* + * Return the amount of 8-bit elements in UTF-8 needed (without the terminating + * null) to store a given UTF-16LE string. + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) +{ + int i, ret = -1; + int count = 0; + BOOL surrog; + + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; +#if NOREVBOM + else if ((c >= 0xe000) && (c < 0xfffe)) +#else + else if (c >= 0xe000) +#endif + count += 3; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } + } + if (surrog) + goto fail; + + ret = count; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *original_outs_value = *outs; + int original_outs_len = outs_len; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + char *t; + int i, size, ret = -1; + int halfpair; + + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; + + size = utf16_to_utf8_size(ins, ins_len, outs_len); + + if (size < 0) + goto out; + + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } + + t = *outs; + + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else + goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else + goto fail; + } + } + *t = '\0'; + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(nfconvert_utf8 && (t - *outs) > 0) { + char *new_outs = NULL; + int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form + if(new_outs_len >= 0 && new_outs != NULL) { + if(original_outs_value != *outs) { + // We have allocated outs ourselves. + free(*outs); + *outs = new_outs; + t = *outs + new_outs_len; + } + else { + // We need to copy new_outs into the fixed outs buffer. + memset(*outs, 0, original_outs_len); + strncpy(*outs, new_outs, original_outs_len-1); + t = *outs + original_outs_len; + free(new_outs); + } + } + else { + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs); + ntfs_log_error(" new_outs=0x%p\n", new_outs); + ntfs_log_error(" new_outs_len=%d\n", new_outs_len); + } + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + ret = t - *outs; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * Return the amount of 16-bit elements in UTF-16LE needed + * (without the terminating null) to store given UTF-8 string. + * + * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. + * + * Note: This does not check whether the input sequence is a valid utf8 string, + * and should be used only in context where such check is made! + */ +static int utf8_to_utf16_size(const char *s) +{ + int ret = -1; + unsigned int byte; + size_t count = 0; + + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX) + goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + } + ret = count; +out: + return ret; +fail: + errno = ENAMETOOLONG; + goto out; +} +/* + * This converts one UTF-8 sequence to cpu-endian Unicode value + * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF + * + * Return the number of used utf8 bytes or -1 with errno set + * if sequence is invalid. + */ +static int utf8_to_unicode(u32 *wc, const char *s) +{ + unsigned int byte = *((const unsigned char *)s); + + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ +#if NOREVBOM + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; +#else + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; +#endif + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } +fail: + errno = EILSEQ; + return -1; +} + +/** + * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + * + * Return -1 with errno set. + */ +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *new_ins = NULL; + if(nfconvert_utf8) { + int new_ins_len; + new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form + if(new_ins_len >= 0) + ins = new_ins; + else + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins); + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + const char *t = ins; + u32 wc; + BOOL allocated; + ntfschar *outpos; + int shorts, ret = -1; + + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) + goto fail; + + allocated = FALSE; + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + allocated = TRUE; + } + + outpos = *outs; + + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + t += m; + } + + ret = --outpos - *outs; +fail: +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(new_ins != NULL) + free(new_ins); +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + return ret; +} + +/** + * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string + * @ins: input Unicode string buffer + * @ins_len: length of input string in Unicode characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Convert the input little endian, 2-byte Unicode string @ins, of length + * @ins_len into the multibyte string format dictated by the current locale. + * + * If *@outs is NULL, the function allocates the string and the caller is + * responsible for calling free(*@outs); when finished with it. + * + * On success the function returns the number of bytes written to the output + * string *@outs (>= 0), not counting the terminating NULL byte. If the output + * string buffer was allocated, *@outs is set to it. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a multibyte + * sequence according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len) +{ + char *mbs; + int mbs_len; +#ifdef MB_CUR_MAX + wchar_t wc; + int i, o; + int cnt = 0; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + mbs = *outs; + mbs_len = outs_len; + if (mbs && !mbs_len) { + errno = ENAMETOOLONG; + return -1; + } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); +#ifdef MB_CUR_MAX + if (!mbs) { + mbs_len = (ins_len + 1) * MB_CUR_MAX; + mbs = ntfs_malloc(mbs_len); + if (!mbs) + return -1; + } +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + wctomb(NULL, 0); +#endif + for (i = o = 0; i < ins_len; i++) { + /* Reallocate memory if necessary or abort. */ + if ((int)(o + MB_CUR_MAX) > mbs_len) { + char *tc; + if (mbs == *outs) { + errno = ENAMETOOLONG; + return -1; + } + tc = ntfs_malloc((mbs_len + 64) & ~63); + if (!tc) + goto err_out; + memcpy(tc, mbs, mbs_len); + mbs_len = (mbs_len + 64) & ~63; + free(mbs); + mbs = tc; + } + /* Convert the LE Unicode character to a CPU wide character. */ + wc = (wchar_t)le16_to_cpu(ins[i]); + if (!wc) + break; + /* Convert the CPU endian wide character to multibyte. */ +#ifdef HAVE_MBSINIT + cnt = wcrtomb(mbs + o, wc, &mbstate); +#else + cnt = wctomb(mbs + o, wc); +#endif + if (cnt == -1) + goto err_out; + if (cnt <= 0) { + ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + o += cnt; + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_debug("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + mbs[o] = '\0'; + if (*outs != mbs) + *outs = mbs; + return o; +err_out: + if (mbs != *outs) { + int eo = errno; + free(mbs); + errno = eo; + } +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/** + * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output Unicode string + * + * Convert the input multibyte string @ins, from the current locale into the + * corresponding little endian, 2-byte Unicode string. + * + * The function allocates the string and the caller is responsible for calling + * free(*@outs); when finished with it. + * + * On success the function returns the number of Unicode characters written to + * the output string *@outs (>= 0), not counting the terminating Unicode NULL + * character. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a Unicode + * string according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_mbstoucs(const char *ins, ntfschar **outs) +{ +#ifdef MB_CUR_MAX + ntfschar *ucs; + const char *s; + wchar_t wc; + int i, o, cnt, ins_len, ucs_len, ins_size; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); + +#ifdef MB_CUR_MAX + /* Determine the size of the multi-byte string in bytes. */ + ins_size = strlen(ins); + /* Determine the length of the multi-byte string. */ + s = ins; +#if defined(HAVE_MBSINIT) + memset(&mbstate, 0, sizeof(mbstate)); + ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); +#ifdef __CYGWIN32__ + if (!ins_len && *ins) { + /* Older Cygwin had broken mbsrtowcs() implementation. */ + ins_len = strlen(ins); + } +#endif +#elif !defined(DJGPP) + ins_len = mbstowcs(NULL, s, 0); +#else + /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ + ins_len = strlen(ins); +#endif + if (ins_len == -1) + return ins_len; +#ifdef HAVE_MBSINIT + if ((s != ins) || !mbsinit(&mbstate)) { +#else + if (s != ins) { +#endif + errno = EILSEQ; + return -1; + } + /* Add the NULL terminator. */ + ins_len++; + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + mbtowc(NULL, NULL, 0); +#endif + for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { + /* Reallocate memory if necessary. */ + if (o >= ucs_len) { + ntfschar *tc; + ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; + tc = realloc(ucs, ucs_len); + if (!tc) + goto err_out; + ucs = tc; + ucs_len /= sizeof(ntfschar); + } + /* Convert the multibyte character to a wide character. */ +#ifdef HAVE_MBSINIT + cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); +#else + cnt = mbtowc(&wc, ins + i, ins_size - i); +#endif + if (!cnt) + break; + if (cnt == -1) + goto err_out; + if (cnt < -1) { + ntfs_log_trace("Eeek. cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + /* Make sure we are not overflowing the NTFS Unicode set. */ + if ((unsigned long)wc >= (unsigned long)(1 << + (8 * sizeof(ntfschar)))) { + errno = EILSEQ; + goto err_out; + } + /* Convert the CPU wide character to a LE Unicode character. */ + ucs[o] = cpu_to_le16(wc); + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_trace("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + ucs[o] = cpu_to_le16(L'\0'); + *outs = ucs; + return o; +err_out: + free(ucs); +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/* + * Turn a UTF8 name uppercase + * + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) + */ + +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) +{ + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; + + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); +} + +/** + * ntfs_upcase_table_build - build the default upcase table for NTFS + * @uc: destination buffer where to store the built table + * @uc_len: size of destination buffer in bytes + * + * ntfs_upcase_table_build() builds the default upcase table for NTFS and + * stores it in the caller supplied buffer @uc of size @uc_len. + * + * Note, @uc_len must be at least 128kiB in size or bad things will happen! + */ +void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) +{ +#if 1 /* Vista */ + /* + * This is the table as defined by Vista + */ + /* + * "Start" is inclusive and "End" is exclusive, every value has the + * value of "Add" added to it. + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, + {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, + {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, + {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, + {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, + {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, + {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, + {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, + {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, + {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, + {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, + {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, + {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, + {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, + {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, + {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} + }; + /* + * "Start" is exclusive and "End" is inclusive, every second value is + * decremented by one. + */ + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, + {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, + {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, + {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, + {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, + {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, + {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, + {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, + {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} + }; + /* + * Set the Unicode character at offset "Offset" to "Value". Note, + * "Value" is host endian. + */ + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, + {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, + {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, + {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, + {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, + {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, + {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, + {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, + {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, + {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, + {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, + {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} + }; +#else /* Vista */ + /* + * This is the table as defined by Windows XP + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; +#endif /* Vista */ + int i, r; + int k, off; + + memset((char*)uc, 0, uc_len); + uc_len >>= 1; + if (uc_len > 65536) + uc_len = 65536; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) { + off = uc_run_table[r][2]; + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(i + off); + } + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(i); + for (r = 0; uc_byte_table[r][0]; r++) { + k = uc_byte_table[r][1]; + uc[uc_byte_table[r][0]] = cpu_to_le16(k); + } +} + +/* + * Allocate and build the default upcase table + * + * Returns the number of entries + * 0 if failed + */ + +#define UPCASE_LEN 65536 /* default number of entries in upcase */ + +u32 ntfs_upcase_build_default(ntfschar **upcase) +{ + u32 upcase_len; + + *upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2); + if (*upcase) { + ntfs_upcase_table_build(*upcase, UPCASE_LEN*2); + upcase_len = UPCASE_LEN; + } + return (upcase_len); +} + +/* + * Build a table for converting to lower case + * + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. + */ + +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) +{ + ntfschar *lc; + u32 upp; + u32 i; + + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i NTFS_MAX_NAME_LEN) { + free(ucs); + errno = ENAMETOOLONG; + return NULL; + } + if (!ucs || !*len) { + ucs = AT_UNNAMED; + *len = 0; + } + return ucs; +} + +/** + * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() + * @ucs input string to be freed + * + * Free memory at @ucs and which was allocated by ntfs_str2ucs. + * + * Return value: none. + */ +void ntfs_ucsfree(ntfschar *ucs) +{ + if (ucs && (ucs != AT_UNNAMED)) + free(ucs); +} + +/* + * Check whether a name contains no chars forbidden + * for DOS or Win32 use + * + * If there is a bad char, errno is set to EINVAL + */ + +BOOL ntfs_forbidden_chars(const ntfschar *name, int len) +{ + BOOL forbidden; + int ch; + int i; + u32 mainset = (1L << ('\"' - 0x20)) + | (1L << ('*' - 0x20)) + | (1L << ('/' - 0x20)) + | (1L << (':' - 0x20)) + | (1L << ('<' - 0x20)) + | (1L << ('>' - 0x20)) + | (1L << ('?' - 0x20)); + + forbidden = (len == 0) + || (le16_to_cpu(name[len-1]) == ' ') + || (le16_to_cpu(name[len-1]) == '.'); + for (i=0; i= vol->upcase_len) + || ((shortname[i] != longname[i]) + && (shortname[i] != vol->upcase[ch]))) + collapsible = FALSE; + } + return (collapsible); +} + +/* + * Define the character encoding to be used. + * Use UTF-8 unless specified otherwise. + */ + +int ntfs_set_char_encoding(const char *locale) +{ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ +} + +#if defined(__APPLE__) || defined(__DARWIN__) + +int ntfs_macosx_normalize_filenames(int normalize) { +#ifdef ENABLE_NFCONV + if(normalize == 0 || normalize == 1) { + nfconvert_utf8 = normalize; + return 0; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} + +int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, + int composed) { +#ifdef ENABLE_NFCONV + /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + CFStringRef cfSourceString; + CFMutableStringRef cfMutableString; + CFRange rangeToProcess; + CFIndex requiredBufferLength; + char *result = NULL; + int resultLength = -1; + + /* Convert the UTF-8 string to a CFString. */ + cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8); + if(cfSourceString == NULL) { + ntfs_log_error("CFStringCreateWithCString failed!\n"); + return -2; + } + + /* Create a mutable string from cfSourceString that we are free to modify. */ + cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString); + CFRelease(cfSourceString); /* End-of-life. */ + if(cfMutableString == NULL) { + ntfs_log_error("CFStringCreateMutableCopy failed!\n"); + return -3; + } + + /* Normalize the mutable string to the desired normalization form. */ + CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD)); + + /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ + rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString)); + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) { + resultLength = sizeof(char)*(requiredBufferLength + 1); + result = ntfs_calloc(resultLength); + + if(result != NULL) { + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, + 0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) { + ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n"); + free(result); + result = NULL; + } + } + else + ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength); + } + else + ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); + + + CFRelease(cfMutableString); + + if(result != NULL) { + *target = result; + return resultLength - 1; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ diff --git a/lib/libntfs/orig/source/unistr.h b/lib/libntfs/orig/source/unistr.h new file mode 100644 index 0000000..8cadc3c --- /dev/null +++ b/lib/libntfs/orig/source/unistr.h @@ -0,0 +1,119 @@ +/* + * unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_UNISTR_H +#define _NTFS_UNISTR_H + +#include "types.h" +#include "layout.h" + +extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size); + +extern int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); + +extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size); + +extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); + +extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); + +extern void ntfs_name_upcase(ntfschar *name, u32 name_len, + const ntfschar *upcase, const u32 upcase_len); + +extern void ntfs_name_locase(ntfschar *name, u32 name_len, + const ntfschar *locase, const u32 locase_len); + +extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); + +extern char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_len); + +extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); +extern u32 ntfs_upcase_build_default(ntfschar **upcase); +extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt); + +extern ntfschar *ntfs_str2ucs(const char *s, int *len); + +extern void ntfs_ucsfree(ntfschar *ucs); + +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen); + +extern int ntfs_set_char_encoding(const char *locale); + +#if defined(__APPLE__) || defined(__DARWIN__) +/** + * Mac OS X only. + * + * Sets file name Unicode normalization form conversion on or off. + * normalize=0 : Off + * normalize=1 : On + * If set to on, all filenames returned by ntfs-3g will be converted to the NFD + * normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC + * normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X + * mostly uses NFD, this conversion increases compatibility between Mac applications and + * NTFS-3G. + * + * @param normalize decides whether or not the string functions will do automatic filename + * normalization when converting to and from UTF-8. 0 means normalization is disabled, + * 1 means it is enabled. + * @return -1 if the argument was invalid or an error occurred, 0 if all went well. + */ +extern int ntfs_macosx_normalize_filenames(int normalize); + +/** + * Mac OS X only. + * + * Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC. + * The parameter "composed" decides whether output should be in composed, NFC, form + * (composed == 1) or decomposed, NFD, form (composed == 0). + * Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly + * ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the + * allocated string when it's no longer needed. + * + * @param utf8_string the input string, which may be in any normalization form. + * @param target a pointer where the resulting string will be stored. + * @param composed decides which composition form to normalize the input string to. 0 means + * composed form (NFC), 1 means decomposed form (NFD). + * @return -1 if the normalization failed for some reason, otherwise the length of the + * normalized string stored in target. + */ +extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed); +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#endif /* defined _NTFS_UNISTR_H */ + diff --git a/lib/libntfs/orig/source/volume.c b/lib/libntfs/orig/source/volume.c new file mode 100644 index 0000000..28e4c90 --- /dev/null +++ b/lib/libntfs/orig/source/volume.c @@ -0,0 +1,1732 @@ +/** + * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "compat.h" +#include "volume.h" +#include "attrib.h" +#include "mft.h" +#include "bootsect.h" +#include "device.h" +#include "debug.h" +#include "inode.h" +#include "runlist.h" +#include "logfile.h" +#include "dir.h" +#include "logging.h" +#include "cache.h" +#include "misc.h" + +const char *ntfs_home = +"News, support and information: http://tuxera.com\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume and shutdown Windows\n" +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" +"\n"; + +static const char *unclean_journal_msg = +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; + +static const char *opened_volume_msg = +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *fakeraid_msg = +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; + +static const char *access_denied_msg = +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; + +/** + * ntfs_volume_alloc - Create an NTFS volume object and initialise it + * + * Description... + * + * Returns: + */ +ntfs_volume *ntfs_volume_alloc(void) +{ + return ntfs_calloc(sizeof(ntfs_volume)); +} + +static void ntfs_attr_free(ntfs_attr **na) +{ + if (na && *na) { + ntfs_attr_close(*na); + *na = NULL; + } +} + +static int ntfs_inode_free(ntfs_inode **ni) +{ + int ret = -1; + + if (ni && *ni) { + ret = ntfs_inode_close(*ni); + *ni = NULL; + } + + return ret; +} + +static void ntfs_error_set(int *err) +{ + if (!*err) + *err = errno; +} + +/** + * __ntfs_volume_release - Destroy an NTFS volume object + * @v: + * + * Description... + * + * Returns: + */ +static int __ntfs_volume_release(ntfs_volume *v) +{ + int err = 0; + + if (ntfs_inode_free(&v->vol_ni)) + ntfs_error_set(&err); + /* + * FIXME: Inodes must be synced before closing + * attributes, otherwise unmount could fail. + */ + if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) + ntfs_inode_sync(v->lcnbmp_ni); + ntfs_attr_free(&v->lcnbmp_na); + if (ntfs_inode_free(&v->lcnbmp_ni)) + ntfs_error_set(&err); + + if (v->mft_ni && NInoDirty(v->mft_ni)) + ntfs_inode_sync(v->mft_ni); + ntfs_attr_free(&v->mftbmp_na); + ntfs_attr_free(&v->mft_na); + if (ntfs_inode_free(&v->mft_ni)) + ntfs_error_set(&err); + + if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) + ntfs_inode_sync(v->mftmirr_ni); + ntfs_attr_free(&v->mftmirr_na); + if (ntfs_inode_free(&v->mftmirr_ni)) + ntfs_error_set(&err); + + if (v->dev) { + struct ntfs_device *dev = v->dev; + + if (dev->d_ops->sync(dev)) + ntfs_error_set(&err); + if (dev->d_ops->close(dev)) + ntfs_error_set(&err); + } + + ntfs_free_lru_caches(v); + free(v->vol_name); + free(v->upcase); + if (v->locase) free(v->locase); + free(v->attrdef); + free(v); + + errno = err; + return errno ? -1 : 0; +} + +static void ntfs_attr_setup_flag(ntfs_inode *ni) +{ + STANDARD_INFORMATION *si; + + si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL); + if (si) { + ni->flags = si->file_attributes; + free(si); + } +} + +/** + * ntfs_mft_load - load the $MFT and setup the ntfs volume with it + * @vol: ntfs volume whose $MFT to load + * + * Load $MFT from @vol and setup @vol with it. After calling this function the + * volume @vol is ready for use by all read access functions provided by the + * ntfs library. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_load(ntfs_volume *vol) +{ + VCN next_vcn, last_vcn, highest_vcn; + s64 l; + MFT_RECORD *mb = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ATTR_RECORD *a; + int eo; + + /* Manually setup an ntfs_inode. */ + vol->mft_ni = ntfs_inode_allocate(vol); + mb = ntfs_malloc(vol->mft_record_size); + if (!vol->mft_ni || !mb) { + ntfs_log_perror("Error allocating memory for $MFT"); + goto error_exit; + } + vol->mft_ni->mft_no = 0; + vol->mft_ni->mrec = mb; + /* Can't use any of the higher level functions yet! */ + l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, + vol->mft_record_size, mb); + if (l != 1) { + if (l != -1) + errno = EIO; + ntfs_log_perror("Error reading $MFT"); + goto error_exit; + } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + goto mft_has_no_attr_list; + } + NInoSetAttrList(vol->mft_ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (l <= 0 || l > 0x40000) { + ntfs_log_error("$MFT/$ATTR_LIST invalid length (%lld).\n", + (long long)l); + goto io_error_exit; + } + vol->mft_ni->attr_list_size = l; + vol->mft_ni->attr_list = ntfs_malloc(l); + if (!vol->mft_ni->attr_list) + goto error_exit; + + l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); + if (!l) { + ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n"); + goto io_error_exit; + } + if (l != vol->mft_ni->attr_list_size) { + ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != " + "%u).\n", (long long)l, + vol->mft_ni->attr_list_size); + goto io_error_exit; + } + +mft_has_no_attr_list: + + ntfs_attr_setup_flag(vol->mft_ni); + + /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + + /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ + vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mft_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Read all extents from the $DATA attribute in $MFT. */ + ntfs_attr_reinit_search_ctx(ctx); + last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; + highest_vcn = next_vcn = 0; + a = NULL; + while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, + ctx)) { + runlist_element *nrl; + + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + ntfs_log_error("$MFT must be non-resident.\n"); + goto io_error_exit; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED) { + ntfs_log_error("$MFT must be uncompressed and " + "unencrypted.\n"); + goto io_error_exit; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing runlist. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); + if (!nrl) { + ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); + goto error_exit; + } + vol->mft_na->rl = nrl; + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + } + if (!a) { + ntfs_log_error("$MFT/$DATA attribute not found.\n"); + goto io_error_exit; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + ntfs_log_error("Failed to load runlist for $MFT/$DATA.\n"); + ntfs_log_error("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", + (long long)highest_vcn, (long long)last_vcn - 1); + goto io_error_exit; + } + /* Done with the $Mft mft record. */ + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* + * The volume is now setup so we can use all read access functions. + */ + vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!vol->mftbmp_na) { + ntfs_log_perror("Failed to open $MFT/$BITMAP"); + goto error_exit; + } + return 0; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (vol->mft_na) { + ntfs_attr_close(vol->mft_na); + vol->mft_na = NULL; + } + if (vol->mft_ni) { + ntfs_inode_close(vol->mft_ni); + vol->mft_ni = NULL; + } + errno = eo; + return -1; +} + +/** + * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it + * @vol: ntfs volume whose $MFTMirr to load + * + * Load $MFTMirr from @vol and setup @vol with it. After calling this function + * the volume @vol is ready for use by all write access functions provided by + * the ntfs library (assuming ntfs_mft_load() has been called successfully + * beforehand). + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mftmirr_load(ntfs_volume *vol) +{ + int err; + + vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); + if (!vol->mftmirr_ni) { + ntfs_log_perror("Failed to open inode $MFTMirr"); + return -1; + } + + vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mftmirr_na) { + ntfs_log_perror("Failed to open $MFTMirr/$DATA"); + goto error_exit; + } + + if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { + ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); + goto error_exit; + } + + return 0; + +error_exit: + err = errno; + if (vol->mftmirr_na) { + ntfs_attr_close(vol->mftmirr_na); + vol->mftmirr_na = NULL; + } + ntfs_inode_close(vol->mftmirr_ni); + vol->mftmirr_ni = NULL; + errno = err; + return -1; +} + +/** + * ntfs_volume_startup - allocate and setup an ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After + * calling this function, the volume is setup sufficiently to call all read + * and write access functions provided by the library. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) +{ + LCN mft_zone_size, mft_lcn; + s64 br; + ntfs_volume *vol; + NTFS_BOOT_SECTOR *bs; + int eo; + + if (!dev || !dev->d_ops || !dev->d_name) { + errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); + return NULL; + } + + bs = ntfs_malloc(sizeof(NTFS_BOOT_SECTOR)); + if (!bs) + return NULL; + + /* Allocate the volume structure. */ + vol = ntfs_volume_alloc(); + if (!vol) + goto error_exit; + + /* Create the default upcase table. */ + vol->upcase_len = ntfs_upcase_build_default(&vol->upcase); + if (!vol->upcase_len || !vol->upcase) + goto error_exit; + + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (flags & MS_RDONLY) + NVolSetReadOnly(vol); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } + /* Attach the device to the volume. */ + vol->dev = dev; + + /* Now read the bootsector. */ + br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); + if (br != sizeof(NTFS_BOOT_SECTOR)) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading bootsector"); + goto error_exit; + } + if (!ntfs_boot_sector_is_ntfs(bs)) { + errno = EINVAL; + goto error_exit; + } + if (ntfs_boot_sector_parse(vol, bs) < 0) + goto error_exit; + + free(bs); + bs = NULL; + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, vol->sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may affect performance " + "but should be harmless otherwise. Error: " + "%s\n", strerror(errno)); + + /* We now initialize the cluster allocator. */ + vol->full_zones = 0; + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ + + /* Setup the mft zone. */ + vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; + ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos); + + /* + * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs + * source) and if the actual mft_lcn is in the expected place or even + * further to the front of the volume, extend the mft_zone to cover the + * beginning of the volume as well. This is in order to protect the + * area reserved for the mft bitmap as well within the mft_zone itself. + * On non-standard volumes we don't protect it as the overhead would be + * higher than the speed increase we would get by doing it. + */ + mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; + if (mft_lcn * vol->cluster_size < 16 * 1024) + mft_lcn = (16 * 1024 + vol->cluster_size - 1) / + vol->cluster_size; + if (vol->mft_zone_start <= mft_lcn) + vol->mft_zone_start = 0; + ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)vol->mft_zone_start); + + /* + * Need to cap the mft zone on non-standard volumes so that it does + * not point outside the boundaries of the volume. We do this by + * halving the zone size until we are inside the volume. + */ + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + while (vol->mft_zone_end >= vol->nr_clusters) { + mft_zone_size >>= 1; + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + } + ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); + + /* + * Set the current position within each data zone to the start of the + * respective zone. + */ + vol->data1_zone_pos = vol->mft_zone_end; + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); + vol->data2_zone_pos = 0; + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); + + /* Set the mft data allocation position to mft record 24. */ + vol->mft_data_pos = 24; + + /* + * The cluster allocator is now fully operational. + */ + + /* Need to setup $MFT so we can use the library read functions. */ + if (ntfs_mft_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFT"); + goto error_exit; + } + + /* Need to setup $MFTMirr so we can use the write functions, too. */ + if (ntfs_mftmirr_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFTMirr"); + goto error_exit; + } + return vol; +error_exit: + eo = errno; + free(bs); + if (vol) + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/** + * ntfs_volume_check_logfile - check logfile on target volume + * @vol: volume on which to check logfile + * + * Return 0 on success and -1 on error with errno set error code. + */ +static int ntfs_volume_check_logfile(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + RESTART_PAGE_HEADER *rp = NULL; + int err = 0; + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + errno = EIO; + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + err = EIO; + goto out; + } + + if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) + err = EOPNOTSUPP; + free(rp); + ntfs_attr_close(na); +out: + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + if (err) { + errno = err; + return -1; + } + return 0; +} + +/** + * ntfs_hiberfile_open - Find and open '/hiberfil.sys' + * @vol: An ntfs volume obtained from ntfs_mount + * + * Return: inode Success, hiberfil.sys is valid + * NULL hiberfil.sys doesn't exist or some other error occurred + */ +static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) +{ + u64 inode; + ntfs_inode *ni_root; + ntfs_inode *ni_hibr = NULL; + ntfschar *unicode = NULL; + int unicode_len; + const char *hiberfile = "hiberfil.sys"; + + if (!vol) { + errno = EINVAL; + return NULL; + } + + ni_root = ntfs_inode_open(vol, FILE_root); + if (!ni_root) { + ntfs_log_debug("Couldn't open the root directory.\n"); + return NULL; + } + + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); + if (unicode_len < 0) { + ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); + goto out; + } + + inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); + if (inode == (u64)-1) { + ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); + goto out; + } + + inode = MREF(inode); + ni_hibr = ntfs_inode_open(vol, inode); + if (!ni_hibr) { + ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); + goto out; + } +out: + if (ntfs_inode_close(ni_root)) { + ntfs_inode_close(ni_hibr); + ni_hibr = NULL; + } + free(unicode); + return ni_hibr; +} + + +#define NTFS_HIBERFILE_HEADER_SIZE 4096 + +/** + * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is + * hibernated on the target volume + * @vol: volume on which to check hiberfil.sys + * + * Return: 0 if Windows isn't hibernated for sure + * -1 otherwise and errno is set to the appropriate value + */ +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + int bytes_read, err; + char *buf = NULL; + + ni = ntfs_hiberfile_open(vol); + if (!ni) { + if (errno == ENOENT) + return 0; + return -1; + } + + buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); + if (!buf) + goto out; + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open hiberfil.sys data attribute"); + goto out; + } + + bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); + if (bytes_read == -1) { + ntfs_log_perror("Failed to read hiberfil.sys"); + goto out; + } + if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); + errno = EPERM; + goto out; + } + if (memcmp(buf, "hibr", 4) == 0) { + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); + errno = EPERM; + goto out; + } + /* All right, all header bytes are zero */ + errno = 0; +out: + if (na) + ntfs_attr_close(na); + free(buf); + err = errno; + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + errno = err; + return errno ? -1 : 0; +} + +/* + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) + * + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. + * + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno + */ + +static int fix_txf_data(ntfs_volume *vol) +{ + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; + + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); +} + +/** + * ntfs_device_mount - open ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @dev should describe the device which + * to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: + * MS_RDONLY - mount volume read-only + * + * The function opens the device @dev and verifies that it contains a valid + * bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) +{ + s64 l; + ntfs_volume *vol; + u8 *m = NULL, *m2 = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_inode *ni; + ntfs_attr *na; + ATTR_RECORD *a; + VOLUME_INFORMATION *vinf; + ntfschar *vname; + int i, j, eo; + unsigned int k; + u32 u; + + vol = ntfs_volume_startup(dev, flags); + if (!vol) + return NULL; + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) + goto error_exit; + + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + if (l == -1) + ntfs_log_perror("Failed to read $MFT"); + else { + ntfs_log_error("Failed to read $MFT, unexpected length " + "(%lld != %d).\n", (long long)l, + vol->mftmirr_size); + errno = EIO; + } + goto error_exit; + } + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + if (l == -1) { + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + vol->mftmirr_size = l; + } + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec)) { + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "'%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec)) { + ntfs_log_error("$MFT error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in '%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + ntfs_log_error("$MFTMirr does not match $MFT (record " + "%d).\n", i); + goto io_error_exit; + } + } + + free(m2); + free(m); + m = m2 = NULL; + + /* Now load the bitmap from $Bitmap. */ + ntfs_log_debug("Loading $Bitmap...\n"); + vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + ntfs_log_perror("Failed to open inode FILE_Bitmap"); + goto error_exit; + } + + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } + + /* Now load the upcase table from $UpCase. */ + ntfs_log_debug("Loading $UpCase...\n"); + ni = ntfs_inode_open(vol, FILE_UpCase); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_UpCase"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + ntfs_log_error("Error: Upcase table is too big (max 32-bit " + "allowed).\n"); + errno = EINVAL; + goto error_exit; + } + if (vol->upcase_len != na->data_size >> 1) { + vol->upcase_len = na->data_size >> 1; + /* Throw away default table. */ + free(vol->upcase); + vol->upcase = ntfs_malloc(na->data_size); + if (!vol->upcase) + goto error_exit; + } + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); + if (l != na->data_size) { + ntfs_log_error("Failed to read $UpCase, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $UpCase mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + goto error_exit; + } + /* Consistency check of $UpCase, restricted to plain ASCII chars */ + k = 0x20; + while ((k < vol->upcase_len) + && (k < 0x7f) + && (le16_to_cpu(vol->upcase[k]) + == ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a'))) + k++; + if (k < 0x7f) { + ntfs_log_error("Corrupted file $UpCase\n"); + goto io_error_exit; + } + + /* + * Now load $Volume and set the version information and flags in the + * vol structure accordingly. + */ + ntfs_log_debug("Loading $Volume...\n"); + vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); + if (!vol->vol_ni) { + ntfs_log_perror("Failed to open inode FILE_Volume"); + goto error_exit; + } + /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $VOLUME_INFORMATION attribute. */ + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " + "$Volume"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be " + "resident but it isn't.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + le32_to_cpu( + a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); + errno = EIO; + goto error_exit; + } + /* Setup vol from the volume information attribute value. */ + vol->major_ver = vinf->major_ver; + vol->minor_ver = vinf->minor_ver; + /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are + defined using cpu_to_le16() macro and hence are consistent. */ + vol->flags = vinf->flags; + /* + * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " + "$Volume failed"); + goto error_exit; + } + /* + * Attribute not present. This has been seen in the field. + * Treat this the same way as if the attribute was present but + * had zero length. + */ + vol->vol_name = ntfs_malloc(1); + if (!vol->vol_name) + goto error_exit; + vol->vol_name[0] = '\0'; + } else { + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("$VOLUME_NAME must be resident.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* + * Convert Unicode volume name to current locale multibyte + * format. + */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + ntfs_log_perror("Volume name could not be converted " + "to current locale"); + ntfs_log_debug("Forcing name into ASCII by replacing " + "non-ASCII characters with underscores.\n"); + vol->vol_name = ntfs_malloc(u + 1); + if (!vol->vol_name) + goto error_exit; + + for (j = 0; j < (s32)u; j++) { + u16 uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (u16)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* Now load the attribute definitions from $AttrDef. */ + ntfs_log_debug("Loading $AttrDef...\n"); + ni = ntfs_inode_open(vol, FILE_AttrDef); + if (!ni) { + ntfs_log_perror("Failed to open $AttrDef"); + goto error_exit; + } + /* Get an ntfs attribute for $AttrDef/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Check we don't overflow 32-bits. */ + if (na->data_size > 0xffffffffLL) { + ntfs_log_error("Attribute definition table is too big (max " + "32-bit allowed).\n"); + errno = EINVAL; + goto error_exit; + } + vol->attrdef_len = na->data_size; + vol->attrdef = ntfs_malloc(na->data_size); + if (!vol->attrdef) + goto error_exit; + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); + if (l != na->data_size) { + ntfs_log_error("Failed to read $AttrDef, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $AttrDef mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $AttrDef"); + goto error_exit; + } + /* + * Check for dirty logfile and hibernated Windows. + * We care only about read-write mounts. + */ + if (!(flags & (MS_RDONLY | MS_FORENSIC))) { + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) + goto error_exit; + if (ntfs_volume_check_logfile(vol) < 0) { + if (!(flags & MS_RECOVER)) + goto error_exit; + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } + /* make $TXF_DATA resident if present on the root directory */ + if (fix_txf_data(vol)) + goto error_exit; + } + + return vol; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(m); + free(m2); + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/* + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. + */ + +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) +{ + int res; + + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); +} + +/* + * Set ignore case mode + */ + +int ntfs_set_ignore_case(ntfs_volume *vol) +{ + int res; + + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); +} + +/** + * ntfs_mount - open ntfs volume + * @name: name of device/file to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @name should contain the name of the + * device/file to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flags + * is implemented: + * MS_RDONLY - mount volume read-only + * + * The function opens the device or file @name and verifies that it contains a + * valid bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + * + * Note, that a copy is made of @name, and hence it can be discarded as + * soon as the function returns. + */ +ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), + unsigned long flags __attribute__((unused))) +{ +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + struct ntfs_device *dev; + ntfs_volume *vol; + + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return NULL; + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, flags); + if (!vol) { + int eo = errno; + ntfs_device_free(dev); + errno = eo; + } else + ntfs_create_lru_caches(vol); + return vol; +#else + /* + * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is + * defined as there are no device operations available in libntfs in + * this case. + */ + errno = EOPNOTSUPP; + return NULL; +#endif +} + +/** + * ntfs_umount - close ntfs volume + * @vol: address of ntfs_volume structure of volume to close + * @force: if true force close the volume even if it is busy + * + * Deallocate all structures (including @vol itself) associated with the ntfs + * volume @vol. + * + * Return 0 on success. On error return -1 with errno set appropriately + * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that + * an operation is in progress and if you try the close later the operation + * might be completed and the close succeed. + * + * If @force is true (i.e. not zero) this function will close the volume even + * if this means that data might be lost. + * + * @vol must have previously been returned by a call to ntfs_mount(). + * + * @vol itself is deallocated and should no longer be dereferenced after this + * function returns success. If it returns an error then nothing has been done + * so it is safe to continue using @vol. + */ +int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) +{ + struct ntfs_device *dev; + int ret; + + if (!vol) { + errno = EINVAL; + return -1; + } + dev = vol->dev; + ret = __ntfs_volume_release(vol); + ntfs_device_free(dev); + return ret; +} + +#ifdef HAVE_MNTENT_H + +#ifndef HAVE_REALPATH +/** + * realpath - If there is no realpath on the system + */ +static char *realpath(const char *path, char *resolved_path) +{ + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; +} +#endif + +/** + * ntfs_mntent_check - desc + * + * If you are wanting to use this, you actually wanted to use + * ntfs_check_if_mounted(), you just didn't realize. (-: + * + * See description of ntfs_check_if_mounted(), below. + */ +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) +{ + struct mntent *mnt; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; + + real_file = ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = ntfs_malloc(PATH_MAX + 1); + if (!real_fsname) { + err = errno; + goto exit; + } + if (!realpath(file, real_file)) { + err = errno; + goto exit; + } + if (!(f = setmntent(MOUNTED, "r"))) { + err = errno; + goto exit; + } + while ((mnt = getmntent(f))) { + if (!realpath(mnt->mnt_fsname, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) + break; + } + endmntent(f); + if (!mnt) + goto exit; + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_dir, "/")) + *mnt_flags |= NTFS_MF_ISROOT; +#ifdef HAVE_HASMNTOPT + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; +#endif +exit: + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; +} +#endif /* HAVE_MNTENT_H */ + +/** + * ntfs_check_if_mounted - check if an ntfs volume is currently mounted + * @file: device file to check + * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) + * + * If the running system does not support the {set,get,end}mntent() calls, + * just return 0 and set *@mnt_flags to zero. + * + * When the system does support the calls, ntfs_check_if_mounted() first tries + * to find the device @file in /etc/mtab (or wherever this is kept on the + * running system). If it is not found, assume the device is not mounted and + * return 0 and set *@mnt_flags to zero. + * + * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. + * + * Further if @file is mounted as the file system root ("/"), set the flag + * NTFS_MF_ISROOT in *@mnt_flags. + * + * Finally, check if the file system is mounted read-only, and if so set the + * NTFS_MF_READONLY flag in *@mnt_flags. + * + * On success return 0 with *@mnt_flags set to the ntfs mount flags. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_check_if_mounted(const char *file __attribute__((unused)), + unsigned long *mnt_flags) +{ + *mnt_flags = 0; +#ifdef HAVE_MNTENT_H + return ntfs_mntent_check(file, mnt_flags); +#else + return 0; +#endif +} + +/** + * ntfs_version_is_supported - check if NTFS version is supported. + * @vol: ntfs volume whose version we're interested in. + * + * The function checks if the NTFS volume version is known or not. + * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. + * Version 2.x is used by Windows 2000 Betas. + * Version 3.0 is used by Windows 2000. + * Version 3.1 is used by Windows XP, Windows Server 2003 and Longhorn. + * + * Return 0 if NTFS version is supported otherwise -1 with errno set. + * + * The following error codes are defined: + * EOPNOTSUPP - Unknown NTFS version + * EINVAL - Invalid argument + */ +int ntfs_version_is_supported(ntfs_volume *vol) +{ + u8 major, minor; + + if (!vol) { + errno = EINVAL; + return -1; + } + + major = vol->major_ver; + minor = vol->minor_ver; + + if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) + return 0; + + if (NTFS_V2_X(major, minor)) + return 0; + + if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) + return 0; + + errno = EOPNOTSUPP; + return -1; +} + +/** + * ntfs_logfile_reset - "empty" $LogFile data attribute value + * @vol: ntfs volume whose $LogFile we intend to reset. + * + * Fill the value of the $LogFile data attribute, i.e. the contents of + * the file, with 0xff's, thus marking the journal as empty. + * + * FIXME(?): We might need to zero the LSN field of every single mft + * record as well. (But, first try without doing that and see what + * happens, since chkdsk might pickup the pieces and do it for us...) + * + * On success return 0. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_logfile_reset(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na; + int eo; + + if (!vol) { + errno = EINVAL; + return -1; + } + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + eo = errno; + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + goto error_exit; + } + + if (ntfs_empty_logfile(na)) { + eo = errno; + ntfs_attr_close(na); + goto error_exit; + } + + ntfs_attr_close(na); + return ntfs_inode_close(ni); + +error_exit: + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * ntfs_volume_write_flags - set the flags of an ntfs volume + * @vol: ntfs volume where we set the volume flags + * @flags: new flags + * + * Set the on-disk volume flags in the mft record of $Volume and + * on volume @vol to @flags. + * + * Return 0 if successful and -1 if not with errno set to the error code. + */ +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) +{ + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol || !vol->vol_ni) { + errno = EINVAL; + return -1; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " + "in $Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "but it isn't.\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; + /* Write them to disk. */ + ntfs_inode_mark_dirty(vol->vol_ni); + if (ntfs_inode_sync(vol->vol_ni)) + goto err_out; + + ret = 0; /* success */ +err_out: + ntfs_attr_put_search_ctx(ctx); + return ret; +} + +int ntfs_volume_error(int err) +{ + int ret; + + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; +} + + +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) +{ + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } +} + +int ntfs_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } + return 0; +} + +/* + * Feed the counts of free clusters and free mft records + */ + +int ntfs_volume_get_free_space(ntfs_volume *vol) +{ + ntfs_attr *na; + int ret; + + ret = -1; /* default return */ + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + } else { + na = vol->mftbmp_na; + vol->free_mft_records = ntfs_attr_get_free_bits(na); + + if (vol->free_mft_records >= 0) + vol->free_mft_records += (na->allocated_size - na->data_size) << 3; + + if (vol->free_mft_records < 0) + ntfs_log_perror("Failed to calculate free MFT records"); + else + ret = 0; + } + return (ret); +} diff --git a/lib/libntfs/orig/source/volume.h b/lib/libntfs/orig/source/volume.h new file mode 100644 index 0000000..b3f47bf --- /dev/null +++ b/lib/libntfs/orig/source/volume.h @@ -0,0 +1,307 @@ +/* + * volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_VOLUME_H +#define _NTFS_VOLUME_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_MNTENT_H +#include +#endif + +/* + * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, + * so we define them ourselves. + */ +#ifndef MS_RDONLY +#define MS_RDONLY 1 +#endif + +#define MS_EXCLUSIVE 0x08000000 + +#ifndef MS_RECOVER +#define MS_RECOVER 0x10000000 +#endif + +#define MS_IGNORE_HIBERFILE 0x20000000 +#define MS_FORENSIC 0x04000000 /* No modification during mount */ + +/* Forward declaration */ +typedef struct _ntfs_volume ntfs_volume; + +#include "param.h" +#include "types.h" +#include "support.h" +#include "device.h" +#include "inode.h" +#include "attrib.h" +#include "index.h" + +/** + * enum ntfs_mount_flags - + * + * Flags returned by the ntfs_check_if_mounted() function. + */ +typedef enum { + NTFS_MF_MOUNTED = 1, /* Device is mounted. */ + NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ + NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ +} ntfs_mount_flags; + +extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); + +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 +} ntfs_volume_status; + +/** + * enum ntfs_volume_state_bits - + * + * Defined bits for the state field in the ntfs_volume structure. + */ +typedef enum { + NV_ReadOnly, /* 1: Volume is read-only. */ + NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ + NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ +} ntfs_volume_state_bits; + +#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) +#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) +#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) + +#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) +#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) +#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) + +#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) +#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) +#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) + +#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) +#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) +#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) + +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) + +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) + +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) + +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) + +/* + * NTFS version 1.1 and 1.2 are used by Windows NT4. + * NTFS version 2.x is used by Windows 2000 Beta + * NTFS version 3.0 is used by Windows 2000. + * NTFS version 3.1 is used by Windows XP, 2003 and Vista. + */ + +#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1) +#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2) +#define NTFS_V2_X(major, minor) ((major) == 2) +#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0) +#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1) + +#define NTFS_BUF_SIZE 8192 + +/** + * struct _ntfs_volume - structure describing an open volume in memory. + */ +struct _ntfs_volume { + union { + struct ntfs_device *dev; /* NTFS device associated with + the volume. */ + void *sb; /* For kernel porting compatibility. */ + }; + char *vol_name; /* Name of the volume. */ + unsigned long state; /* NTFS specific flags describing this volume. + See ntfs_volume_state_bits above. */ + + ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ + u8 major_ver; /* Ntfs major version of volume. */ + u8 minor_ver; /* Ntfs minor version of volume. */ + le16 flags; /* Bit array of VOLUME_* flags. */ + + u16 sector_size; /* Byte size of a sector. */ + u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ + u32 cluster_size; /* Byte size of a cluster. */ + u32 mft_record_size; /* Byte size of a mft record. */ + u32 indx_record_size; /* Byte size of a INDX record. */ + u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ + u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ + u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ + + /* Variables used by the cluster and mft allocators. */ + u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ + u8 full_zones; /* cluster zones which are full */ + s64 mft_data_pos; /* Mft record number at which to allocate the + next mft record. */ + LCN mft_zone_start; /* First cluster of the mft zone. */ + LCN mft_zone_end; /* First cluster beyond the mft zone. */ + LCN mft_zone_pos; /* Current position in the mft zone. */ + LCN data1_zone_pos; /* Current position in the first data zone. */ + LCN data2_zone_pos; /* Current position in the second data zone. */ + + s64 nr_clusters; /* Volume size in clusters, hence also the + number of bits in lcn_bitmap. */ + ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ + ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute + of FILE_Bitmap. Each bit represents a + cluster on the volume, bit 0 representing + lcn 0 and so on. A set bit means that the + cluster and vice versa. */ + + LCN mft_lcn; /* Logical cluster number of the data attribute + for FILE_MFT. */ + ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ + ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute + of FILE_MFT. */ + ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute + of FILE_MFT. Each bit represents an mft + record in the $DATA attribute, bit 0 + representing mft record 0 and so on. A set + bit means that the mft record is in use and + vice versa. */ + + ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */ + ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */ + ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */ + int secure_reentry; /* check for non-rentries */ + unsigned int secure_flags; /* flags, see security.h for values */ + + int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ + LCN mftmirr_lcn; /* Logical cluster number of the data attribute + for FILE_MFTMirr. */ + ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ + ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute + of FILE_MFTMirr. */ + + ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte + Unicode characters. Obtained from + FILE_UpCase. */ + u32 upcase_len; /* Length in Unicode characters of the upcase + table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ + + ATTR_DEF *attrdef; /* Attribute definitions. Obtained from + FILE_AttrDef. */ + s32 attrdef_len; /* Size of the attribute definition table in + bytes. */ + + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ + BOOL efs_raw; /* volume is mounted for raw access to + efs-encrypted files */ +#ifdef XATTR_MAPPINGS + struct XATTRMAPPING *xattr_mapping; +#endif /* XATTR_MAPPINGS */ +#if CACHE_INODE_SIZE + struct CACHE_HEADER *xinode_cache; +#endif +#if CACHE_NIDATA_SIZE + struct CACHE_HEADER *nidata_cache; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHE_HEADER *lookup_cache; +#endif +#if CACHE_SECURID_SIZE + struct CACHE_HEADER *securid_cache; +#endif +#if CACHE_LEGACY_SIZE + struct CACHE_HEADER *legacy_cache; +#endif + +}; + +extern const char *ntfs_home; + +extern ntfs_volume *ntfs_volume_alloc(void); + +extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + unsigned long flags); + +extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, + unsigned long flags); + +extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); +extern int ntfs_umount(ntfs_volume *vol, const BOOL force); + +extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); +extern int ntfs_logfile_reset(ntfs_volume *vol); + +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); + +extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); + +extern int ntfs_volume_get_free_space(ntfs_volume *vol); + +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); +extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); + +#endif /* defined _NTFS_VOLUME_H */ + diff --git a/lib/libntfs/orig/source/xattrs.c b/lib/libntfs/orig/source/xattrs.c new file mode 100644 index 0000000..5be2c06 --- /dev/null +++ b/lib/libntfs/orig/source/xattrs.c @@ -0,0 +1,791 @@ +/** + * xattrs.c : common functions to deal with system extended attributes + * + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SETXATTR /* extended attributes support required */ + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "security.h" +#include "acls.h" +#include "efs.h" +#include "reparse.h" +#include "object_id.h" +#include "misc.h" +#include "logging.h" +#include "xattrs.h" + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Posix ACL structures + */ + +struct LE_POSIX_ACE { + le16 tag; + le16 perms; + le32 id; +} __attribute__((__packed__)); + +struct LE_POSIX_ACL { + u8 version; + u8 flags; + le16 filler; + struct LE_POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +#endif +#endif + +static const char xattr_ntfs_3g[] = "ntfs-3g."; +static const char nf_ns_user_prefix[] = "user."; +static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; + +static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; +static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; +static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; +static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo"; +static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; +static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id"; +static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; +static const char nf_ns_xattr_times[] = "system.ntfs_times"; +static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be"; +static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime"; +static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be"; +static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; +static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; + +static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; + +struct XATTRNAME { + enum SYSTEMXATTRS xattr; + const char *name; +} ; + +static struct XATTRNAME nf_ns_xattr_names[] = { + { XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl }, + { XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib }, + { XATTR_NTFS_ATTRIB_BE, nf_ns_xattr_attrib_be }, + { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, + { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, + { XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id }, + { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, + { XATTR_NTFS_TIMES, nf_ns_xattr_times }, + { XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be }, + { XATTR_NTFS_CRTIME, nf_ns_xattr_crtime }, + { XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be }, + { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, + { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, + { XATTR_UNMAPPED, (char*)NULL } /* terminator */ +}; + +/* + * Make an integer big-endian + * + * Swap bytes on a small-endian computer and does nothing on a + * big-endian computer. + */ + +static void fix_big_endian(char *p, int size) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + int i,j; + int c; + + i = 0; + j = size - 1; + while (i < j) { + c = p[i]; + p[i++] = p[j]; + p[j--] = c; + } +#endif +} + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Make a Posix ACL CPU endian + */ + +static int le_acl_to_cpu(const struct LE_POSIX_ACL *le_acl, size_t size, + struct POSIX_ACL *acl) +{ + int i; + int cnt; + + acl->version = le_acl->version; + acl->flags = le_acl->flags; + acl->filler = 0; + cnt = (size - sizeof(struct LE_POSIX_ACL)) / sizeof(struct LE_POSIX_ACE); + for (i=0; iace[i].tag = le16_to_cpu(le_acl->ace[i].tag); + acl->ace[i].perms = le16_to_cpu(le_acl->ace[i].perms); + acl->ace[i].id = le32_to_cpu(le_acl->ace[i].id); + } + return (0); +} + +/* + * Make a Posix ACL little endian + */ + +int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size, + struct LE_POSIX_ACL *le_acl) +{ + int i; + int cnt; + + le_acl->version = acl->version; + le_acl->flags = acl->flags; + le_acl->filler = const_cpu_to_le16(0); + cnt = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + for (i=0; iace[i].tag = cpu_to_le16(acl->ace[i].tag); + le_acl->ace[i].perms = cpu_to_le16(acl->ace[i].perms); + le_acl->ace[i].id = cpu_to_le32(acl->ace[i].id); + } + return (0); +} + +#endif +#endif + +/* + * Determine whether an extended attribute is mapped to + * internal data (original name in system namespace, or renamed) + */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol) +{ + struct XATTRNAME *p; + enum SYSTEMXATTRS ret; +#ifdef XATTR_MAPPINGS + const struct XATTRMAPPING *q; +#endif /* XATTR_MAPPINGS */ + + p = nf_ns_xattr_names; + while (p->name && strcmp(p->name,name)) + p++; + ret = p->xattr; +#ifdef XATTR_MAPPINGS + if (!p->name && vol && vol->xattr_mapping) { + q = vol->xattr_mapping; + while (q && strcmp(q->name,name)) + q = q->next; + if (q) + ret = q->xattr; + } +#else /* XATTR_MAPPINGS */ + if (!p->name + && vol + && vol->efs_raw + && !strcmp(nf_ns_alt_xattr_efsinfo,name)) + ret = XATTR_NTFS_EFSINFO; +#endif /* XATTR_MAPPINGS */ + return (ret); +} + +#ifdef XATTR_MAPPINGS + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + * Note : errors are logged, but not returned +// TODO partially share with acls.c + */ + +static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *pe; + char *ps; + char *pu; + enum SYSTEMXATTRS xattr; + int gotend; + char maptext[LINESZ]; + struct XATTRMAPPING *item; + + src = *psrc; + dst = 0; + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + /* ignore spaces */ + if ((dst < LINESZ) + && (buf[src] != '\r') + && (buf[src] != '\t') + && (buf[src] != ' ')) + maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((maptext[0] == '#') || !gotend)); + item = (struct XATTRMAPPING*)NULL; + if (gotend) { + /* decompose into system name and user name */ + ps = maptext; + pu = strchr(maptext,':'); + if (pu) { + *pu++ = 0; + pe = strchr(pu,':'); + if (pe) + *pe = 0; + /* check name validity */ + if ((strlen(pu) < 6) || strncmp(pu,"user.",5)) + pu = (char*)NULL; + xattr = ntfs_xattr_system_type(ps, + (ntfs_volume*)NULL); + if (xattr == XATTR_UNMAPPED) + pu = (char*)NULL; + } + if (pu) { + item = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(pu)); + if (item) { + item->xattr = xattr; + strcpy(item->name,pu); + item->next = (struct XATTRMAPPING*)NULL; + } + } else { + ntfs_log_early_error("Bad xattr mapping item, aborting\n"); + } + } + *psrc = src; + return (item); +} + +/* + * Read xattr mapping file and split into their attribute. + * Parameters are kept in a chained list. + * Returns the head of list, if any + * Errors are logged, but not returned + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader, + void *fileid) +{ + char buf[BUFSZ]; + struct XATTRMAPPING *item; + struct XATTRMAPPING *current; + struct XATTRMAPPING *firstitem; + struct XATTRMAPPING *lastitem; + BOOL duplicated; + int src; + off_t offs; + s64 size; + + firstitem = (struct XATTRMAPPING*)NULL; + lastitem = (struct XATTRMAPPING*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + /* check no double mapping */ + duplicated = FALSE; + for (current=firstitem; current; current=current->next) + if ((current->xattr == item->xattr) + || !strcmp(current->name,item->name)) + duplicated = TRUE; + if (duplicated) { + free(item); + ntfs_log_early_error("Conflicting xattr mapping ignored\n"); + } else { + item->next = (struct XATTRMAPPING*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } + } while (item); + } + return (firstitem); +} + +/* + * Build the extended attribute mappings to user namespace + * + * Note : no error is returned. If we refused mounting when there + * is an error it would be too difficult to fix the offending file + */ + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *xattrmap_path) +{ + struct XATTRMAPPING *firstmapping; + struct XATTRMAPPING *mapping; + BOOL user_efs; + BOOL notfound; + ntfs_inode *ni; + int fd; + + firstmapping = (struct XATTRMAPPING*)NULL; + notfound = FALSE; + if (!xattrmap_path) + xattrmap_path = XATTRMAPPINGFILE; + if (xattrmap_path[0] == '/') { + fd = open(xattrmap_path,O_RDONLY); + if (fd > 0) { + firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd); + close(fd); + } else + notfound = TRUE; + } else { + ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path); + if (ni) { + firstmapping = ntfs_read_xattr_mapping(localread, ni); + ntfs_inode_close(ni); + } else + notfound = TRUE; + } + if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) { + ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path); + } + if (vol->efs_raw) { + user_efs = TRUE; + for (mapping=firstmapping; mapping; mapping=mapping->next) + if (mapping->xattr == XATTR_NTFS_EFSINFO) + user_efs = FALSE; + } else + user_efs = FALSE; + if (user_efs) { + mapping = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(nf_ns_alt_xattr_efsinfo)); + if (mapping) { + mapping->next = firstmapping; + mapping->xattr = XATTR_NTFS_EFSINFO; + strcpy(mapping->name,nf_ns_alt_xattr_efsinfo); + firstmapping = mapping; + } + } + return (firstmapping); +} + +void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) +{ + struct XATTRMAPPING *p, *q; + + p = mapping; + while (p) { + q = p->next; + free(p); + p = q; + } +} + +#endif /* XATTR_MAPPINGS */ + + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int res; + int i; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + /* + * the returned value is the needed + * size. If it is too small, no copy + * is done, and the caller has to + * issue a new call with correct size. + */ + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_get_ntfs_acl(scx, ni, value, size); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_access, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_default, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access, + value, size); + break; + case XATTR_POSIX_DEF : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(ni, value, size); + break; + case XATTR_NTFS_ATTRIB_BE : + res = ntfs_get_ntfs_attrib(ni, value, size); + if ((res == 4) && value) { + if (size >= 4) + fix_big_endian(value,4); + else + res = -EINVAL; + } + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_get_efs_info(ni, value, size); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(ni, value, size); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_get_ntfs_object_id(ni, value, size); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(ni, value, size); + break; + case XATTR_NTFS_TIMES_BE: + res = ntfs_inode_get_times(ni, value, size); + if ((res > 0) && value) { + for (i=0; (i+1)*sizeof(u64)<=(unsigned int)res; i++) + fix_big_endian(&value[i*sizeof(u64)], + sizeof(u64)); + } + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + break; + case XATTR_NTFS_CRTIME_BE: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + if ((res >= (int)sizeof(u64)) && value) + fix_big_endian(value,sizeof(u64)); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res; + int i; + char buf[4*sizeof(u64)]; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(scx, ni, value, size, flags); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_access, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_default, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access, + value, size, flags); + break; + case XATTR_POSIX_DEF : + res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size, flags); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_ATTRIB_BE : + if (value && (size >= 4)) { + memcpy(buf,value,4); + fix_big_endian(buf,4); + res = ntfs_set_ntfs_attrib(ni, buf, 4, flags); + } else + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_set_efs_info(ni, value, size, flags); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_set_ntfs_object_id(ni, value, size, flags); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + /* warning : this closes both inodes */ + res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, + size, flags); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_TIMES_BE: + if (value && (size > 0) && (size <= 4*sizeof(u64))) { + memcpy(buf,value,size); + for (i=0; (i+1)*sizeof(u64)<=size; i++) + fix_big_endian(&buf[i*sizeof(u64)], + sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, size, flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_set_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size), flags); + break; + case XATTR_NTFS_CRTIME_BE: + if (value && (size >= sizeof(u64))) { + memcpy(buf,value,sizeof(u64)); + fix_big_endian(buf,sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, sizeof(u64), flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = 0; + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; +#if POSIXACLS + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_posix_acl(scx, ni, + (attr == XATTR_POSIX_ACC ? + nf_ns_xattr_posix_access : + nf_ns_xattr_posix_default))) + res = -errno; + } else + res = -errno; + break; +#endif + case XATTR_NTFS_REPARSE_DATA : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_reparse_data(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_OBJECT_ID : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_object_id(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_DOS_NAME: + if (ni && dir_ni) { + if (ntfs_remove_ntfs_dos_name(ni,dir_ni)) + res = -errno; + /* ni and dir_ni have been closed */ + } else + res = -errno; + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/orig/source/xattrs.h b/lib/libntfs/orig/source/xattrs.h new file mode 100644 index 0000000..5158311 --- /dev/null +++ b/lib/libntfs/orig/source/xattrs.h @@ -0,0 +1,75 @@ +/* + * xattrs.h : definitions related to system extended attributes + * + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_XATTR_H_ +#define _NTFS_XATTR_H_ + +/* + * Identification of data mapped to the system name space + */ + +enum SYSTEMXATTRS { + XATTR_UNMAPPED, + XATTR_NTFS_ACL, + XATTR_NTFS_ATTRIB, + XATTR_NTFS_ATTRIB_BE, + XATTR_NTFS_EFSINFO, + XATTR_NTFS_REPARSE_DATA, + XATTR_NTFS_OBJECT_ID, + XATTR_NTFS_DOS_NAME, + XATTR_NTFS_TIMES, + XATTR_NTFS_TIMES_BE, + XATTR_NTFS_CRTIME, + XATTR_NTFS_CRTIME_BE, + XATTR_POSIX_ACC, + XATTR_POSIX_DEF +} ; + +struct XATTRMAPPING { + struct XATTRMAPPING *next; + enum SYSTEMXATTRS xattr; + char name[1]; /* variable length */ +} ; + +#ifdef XATTR_MAPPINGS + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *path); +void ntfs_xattr_free_mapping(struct XATTRMAPPING*); + +#endif /* XATTR_MAPPINGS */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol); + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni); + +#endif /* _NTFS_XATTR_H_ */ diff --git a/lib/libntfs/src/AUTHORS b/lib/libntfs/src/AUTHORS new file mode 100644 index 0000000..361193b --- /dev/null +++ b/lib/libntfs/src/AUTHORS @@ -0,0 +1,32 @@ + +Present authors of ntfs-3g in alphabetical order: + +Jean-Pierre Andre +Alon Bar-Lev +Martin Bene +Dominique L Bouix +Csaba Henk +Bernhard Kaindl +Erik Larsson +Alejandro Pulver +Szabolcs Szakacsits +Miklos Szeredi + + +Past authors in alphabetical order: + +Anton Altaparmakov +Mario Emmenlauer +Yuval Fledel +Yura Pakhuchiy +Richard Russon + + +Nintendo GameCube/Wii port authors in alphabetical order: + +Rhys "Shareese" Koedijk +Dimok +Miigotu +oggzee +WiiPower + diff --git a/lib/libntfs/src/CREDITS b/lib/libntfs/src/CREDITS new file mode 100644 index 0000000..3c1bc7c --- /dev/null +++ b/lib/libntfs/src/CREDITS @@ -0,0 +1,50 @@ +The following people have contributed directly or indirectly +to the ntfs-3g project. + +Please let ntfs-3g-devel@lists.sf.net know if you believe +someone is missing, or if you prefer not to be listed. + +Dominique L Bouix +Csaba Henk +Max Khon +Auri Hautam�ki +Gergely Erdelyi +Anton Altaparmakov +Peter Boross +Don Bright +Mario Emmenlauer +Yuval Fledel +Kano from Kanotix +Roland Kletzing +Maarten Lankhorst +Gergely Madarasz +Patrick McLean +Florent Mertens +Yura Pakhuchiy +Miklos Szeredi +Bartosz Taudul +Zhanglinbao +Wade Fitzpatrick +Carsten Einig +Adam Cecile +Bruno Damour +Ales Fruman +Curt McDowell +Thomas Franken +Jonatan Lambert +Klaus Knopper +Zhanglinbao +Ismail Donmez +Laszlo Dvornik +Pallaghy Ajtony +Szabolcs Szakacsits +Jean-Pierre Andre +Alejandro Pulver +Erik Larsson +Alon Bar-Lev + +The following people have contributed directly or indirectly +to the Nintendo GameCube/Wii port of ntfs-3g. + +Michael "Chishm" Chisholm +rodries diff --git a/lib/libntfs/src/LICENSE b/lib/libntfs/src/LICENSE new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/lib/libntfs/src/LICENSE @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/lib/libntfs/src/Makefile b/lib/libntfs/src/Makefile new file mode 100644 index 0000000..b1b3281 --- /dev/null +++ b/lib/libntfs/src/Makefile @@ -0,0 +1,35 @@ + +#default: cube-release wii-release +default: wii-release + +all: debug release + +debug: cube-debug wii-debug + +release: cube-release wii-release + +cube-debug: + $(MAKE) -C source PLATFORM=cube BUILD=cube_debug + +wii-debug: + $(MAKE) -C source PLATFORM=wii BUILD=wii_debug + +cube-release: + $(MAKE) -C source PLATFORM=cube BUILD=cube_release + +wii-release: + $(MAKE) -C source PLATFORM=wii BUILD=wii_release + +clean: + $(MAKE) -C source PLATFORM=wii clean + +#install: cube-release wii-release +# $(MAKE) -C source install + +install: wii-release + $(MAKE) -C source PLATFORM=wii install + +run: install + $(MAKE) -C example + $(MAKE) -C example run + diff --git a/lib/libntfs/src/READMII b/lib/libntfs/src/READMII new file mode 100644 index 0000000..ca8f1ab --- /dev/null +++ b/lib/libntfs/src/READMII @@ -0,0 +1,54 @@ + +INTRODUCTION +============ + +The NTFS-3G driver is an open source, freely available read/write NTFS driver +for Linux, FreeBSD, Mac OS X, NetBSD, Solaris and Haiku. It provides safe and +fast handling of the Windows XP, Windows Server 2003, Windows 2000, Windows +Vista, and Windows Server 2008 file systems. + +The purpose of the project is to develop, continuously quality test and +support a trustable, featureful and high performance solution for hardware +platforms and operating systems whose users need to reliably interoperate +with NTFS. Besides this practical goal, the project also aims to explore +the limits of the hybrid, kernel/user space filesystem driver approach, +performance, reliability and feature richness per invested effort wise. + +The driver is in STABLE status. The test methods, the test suites used +can be found at + + http://ntfs-3g.org/quality.html + +News, support answers, problem submission instructions, support and discussion +forums, performance numbers and other information are available on the project +web site at + + http://ntfs-3g.org + +For more details on the NTFS-3G project see the 'original' folder included +with this package. + +COMPILING AND INSTALLATION +========================== + +Make sure you have devKitPPC and the latest libogc installed. Then type: + + make + make install # or 'sudo make install' if you aren't root. + + +USAGE +===== + +NTFS related routines can be accessed by adding the following line to your +source file(s). + + #include + +When compiling you must also link against the libntfs. To do this add -lntfs +to the LIBS section of your application Makefile. For example: + + LIBS := -lwiiuse -lbte -lntfs -lfat -logc -lm + +For a more practical example of using NTFS in your application, +see the included 'example' folder. diff --git a/lib/libntfs/src/include/ntfs.h b/lib/libntfs/src/include/ntfs.h new file mode 100644 index 0000000..62f2f71 --- /dev/null +++ b/lib/libntfs/src/include/ntfs.h @@ -0,0 +1,148 @@ +/** + * ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LIBNTFS_H +#define _LIBNTFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* NTFS errno values */ +#define ENOPART 3000 /* No partition was found */ +#define EINVALPART 3001 /* Specified partition is invalid or not supported */ +#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */ +#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */ + +/* NTFS cache options */ +#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */ +#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */ + +/* NTFS mount flags */ +#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */ +#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */ +#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */ +#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */ +#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */ +#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */ +#define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */ +#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */ +#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES +#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE + +/** + * ntfs_md - NTFS mount descriptor + */ +typedef struct _ntfs_md { + char name[32]; /* Mount name (can be accessed as "name:/") */ + const DISC_INTERFACE *interface; /* Block device containing the mounted partition */ + sec_t startSector; /* Local block address to first sector of partition */ +} ntfs_md; + +/** + * Find all NTFS partitions on a block device. + * + * @param INTERFACE The block device to search + * @param PARTITIONS (out) A pointer to receive the array of partition start sectors + * + * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing PARTITIONS when finished with it + */ +extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions); + +/** + * Mount all NTFS partitions on all inserted block devices. + * + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note All device caches are setup using default values (see above) + */ +extern int ntfsMountAll (ntfs_md **mounts, u32 flags); + +/** + * Mount all NTFS partitions on a block devices. + * + * @param INTERFACE The block device to mount. + * @param MOUNTS (out) A pointer to receive the array of mount descriptors + * @param FLAGS Additional mounting flags. (see above) + * + * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing MOUNTS when finished with it + * @note The device cache is setup using default values (see above) + */ +extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags); + +/** + * Mount a NTFS partition from a specific sector on a block device. + * + * @param NAME The name to mount the device under (can then be accessed as "NAME:/") + * @param INTERFACE The block device to mount + * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) + * @param CACHEPAGECOUNT The total number of pages in the device cache + * @param CACHEPAGESIZE The number of sectors per cache page + * @param FLAGS Additional mounting flags (see above) + * + * @return True if mount was successful, false if no partition was found or an error occurred (see errno) + * @note ntfsFindPartitions should be used first to locate the partitions start sector + */ +extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags); + +/** + * Unmount a NTFS partition. + * + * @param NAME The name of mount used in ntfsMountSimple() and ntfsMount() + * @param FORCE If true unmount even if the device is busy (may lead to data lose) + */ +extern void ntfsUnmount (const char *name, bool force); + +/** + * Get the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * + * @return The volumes name if successful or NULL if an error occurred (see errno) + */ +extern const char *ntfsGetVolumeName (const char *name); + +/** + * Set the volume name of a mounted NTFS partition. + * + * @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount) + * @param VOLUMENAME The new volume name + * + * @return True if mount was successful, false if an error occurred (see errno) + * @note The mount must be write-enabled else this will fail + */ +extern bool ntfsSetVolumeName (const char *name, const char *volumeName); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBNTFS_H */ diff --git a/lib/libntfs/src/include/ntfs_frag.h b/lib/libntfs/src/include/ntfs_frag.h new file mode 100644 index 0000000..889abb1 --- /dev/null +++ b/lib/libntfs/src/include/ntfs_frag.h @@ -0,0 +1,15 @@ +#ifndef NTFS_FRAG_H_ +#define NTFS_FRAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); +int _NTFS_get_fragments (const char *path, _ntfs_frag_append_t append_fragment, void *callback_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libntfs/src/source/Makefile b/lib/libntfs/src/source/Makefile new file mode 100644 index 0000000..5dd32ea --- /dev/null +++ b/lib/libntfs/src/source/Makefile @@ -0,0 +1,143 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +ifeq ($(PLATFORM),wii) +include $(DEVKITPPC)/wii_rules +endif + +ifeq ($(PLATFORM),cube) +include $(DEVKITPPC)/gamecube_rules +endif + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +BUILD ?= wii_release +SOURCES := . +INCLUDES := ../include +LIBDIR := ../lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +#CFLAGS = -ffast-math -O3 -pipe -mrvl -mcpu=750 -meabi -mhard-float -Wall $(MACHDEP) $(INCLUDE) -DGEKKO -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +#CFLAGS = -ffast-math -Os -pipe -mrvl -mcpu=750 -meabi -mhard-float -Wall $(MACHDEP) $(INCLUDE) -DGEKKO -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +CFLAGS = -Os -Wall -ffast-math -pipe $(MACHDEP) $(INCLUDE) -DHAVE_CONFIG_H +CXXFLAGS = $(CFLAGS) +ASFLAGS := -g +export NTFSBIN := $(LIBDIR)/$(PLATFORM)/libntfs.a + +ifeq ($(BUILD),cube_debug) +CFLAGS += -DDEBUG +CXXFLAGS += -DDEBUG +endif +ifeq ($(BUILD),wii_debug) +CFLAGS += -DDEBUG +CXXFLAGS += -DDEBUG +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) + +all: $(NTFSBIN) + +install_ogc: + cp ../include/ntfs.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii + cp ../lib/cube/libntfs.a $(DEVKITPRO)/libogc/lib/cube + +wii-install: + cp ../include/ntfs.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libntfs.a $(DEVKITPRO)/libogc/lib/wii + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../../include +DEST_LIB := ../../lib$(DKV) + +install: + mkdir -p $(DEST_INC) $(DEST_LIB) + cp ../include/ntfs.h $(DEST_INC) + cp ../include/ntfs_frag.h $(DEST_INC) + cp ../lib/wii/libntfs.a $(DEST_LIB) + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(NTFSBIN): $(OFILES) $(LIBDIR)/$(PLATFORM) + @rm -f "../$(NTFSBIN)" + @$(AR) rcs "../$(NTFSBIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIBDIR)/$(PLATFORM): + mkdir -p ../$(LIBDIR)/$(PLATFORM) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libntfs/src/source/acls.c b/lib/libntfs/src/source/acls.c new file mode 100644 index 0000000..fe466c2 --- /dev/null +++ b/lib/libntfs/src/source/acls.c @@ -0,0 +1,4256 @@ +/** + * acls.c - General function to process NTFS ACLs + * + * This module is part of ntfs-3g library, but may also be + * integrated in tools running over Linux or Windows + * + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H + /* + * integration into ntfs-3g + */ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif +#include +#include +#include + +#include "types.h" +#include "layout.h" +#include "security.h" +#include "acls.h" +#include "misc.h" +#else + + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + + /* + * integration into secaudit/Win32 + */ +#ifdef WIN32 +#include +#include +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#else + /* + * integration into secaudit/STSC + */ +#ifdef STSC +#include +#undef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#else + /* + * integration into secaudit/Linux + */ +#include +#include +#include +#include +#endif /* STSC */ +#endif /* WIN32 */ +#include "secaudit.h" +#endif /* HAVE_CONFIG_H */ + +/* + * A few useful constants + */ + +/* + * null SID (S-1-0-0) + */ + +static const char nullsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 0, /* base */ + 0, 0, 0, 0 /* 1st level */ + }; + +static const SID *nullsid = (const SID*)nullsidbytes; + +/* + * SID for world (S-1-1-0) + */ + +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +const SID *worldsid = (const SID*)worldsidbytes; + +/* + * SID for administrator + */ + +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ +}; + +const SID *adminsid = (const SID*)adminsidbytes; + +/* + * SID for system + */ + +static const char systemsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 18, 0, 0, 0 /* 1st level */ + }; + +static const SID *systemsid = (const SID*)systemsidbytes; + +/* + * SID for generic creator-owner + * S-1-3-0 + */ + +static const char ownersidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +static const SID *ownersid = (const SID*)ownersidbytes; + +/* + * SID for generic creator-group + * S-1-3-1 + */ + +static const char groupsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 1, 0, 0, 0 /* 1st level */ +} ; + +static const SID *groupsid = (const SID*)groupsidbytes; + +/* + * Determine the size of a SID + */ + +int ntfs_sid_size(const SID * sid) +{ + return (sid->sub_authority_count * 4 + 8); +} + +/* + * Test whether two SID are equal + */ + +BOOL ntfs_same_sid(const SID *first, const SID *second) +{ + int size; + + size = ntfs_sid_size(first); + return ((ntfs_sid_size(second) == size) + && !memcmp(first, second, size)); +} + +/* + * Test whether a SID means "world user" + * Local users group also recognized as world + */ + +static int is_world_sid(const SID * usid) +{ + return ( + /* check whether S-1-1-0 : world */ + ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(1)) + && (usid->sub_authority[0] == const_cpu_to_le32(0))) + + /* check whether S-1-5-32-545 : local user */ + || ((usid->sub_authority_count == 2) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(32)) + && (usid->sub_authority[1] == const_cpu_to_le32(545))) + ); +} + +/* + * Test whether a SID means "some user (or group)" + * Currently we only check for S-1-5-21... but we should + * probably test for other configurations + */ + +BOOL ntfs_is_user_sid(const SID *usid) +{ + return ((usid->sub_authority_count == 5) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(21))); +} + +/* + * Determine the size of a security attribute + * whatever the order of fields + */ + +unsigned int ntfs_attr_size(const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + const SID *psid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int endsid; + unsigned int endacl; + unsigned int attrsz; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + /* + * First check group, which is the last field in all descriptors + * we build, and in most descriptors built by Windows + */ + attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + offgroup = le32_to_cpu(phead->group); + if (offgroup >= attrsz) { + /* find end of GSID */ + psid = (const SID*)&attr[offgroup]; + endsid = offgroup + ntfs_sid_size(psid); + if (endsid > attrsz) attrsz = endsid; + } + offowner = le32_to_cpu(phead->owner); + if (offowner >= attrsz) { + /* find end of USID */ + psid = (const SID*)&attr[offowner]; + endsid = offowner + ntfs_sid_size(psid); + attrsz = endsid; + } + offsacl = le32_to_cpu(phead->sacl); + if (offsacl >= attrsz) { + /* find end of SACL */ + psacl = (const ACL*)&attr[offsacl]; + endacl = offsacl + le16_to_cpu(psacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + + + /* find end of DACL */ + offdacl = le32_to_cpu(phead->dacl); + if (offdacl >= attrsz) { + pdacl = (const ACL*)&attr[offdacl]; + endacl = offdacl + le16_to_cpu(pdacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + return (attrsz); +} + +/* + * Do sanity checks on a SID read from storage + * (just check revision and number of authorities) + */ + +BOOL ntfs_valid_sid(const SID *sid) +{ + return ((sid->revision == SID_REVISION) + && (sid->sub_authority_count >= 1) + && (sid->sub_authority_count <= 8)); +} + +/* + * Check whether a SID is acceptable for an implicit + * mapping pattern. + * It should have been already checked it is a valid user SID. + * + * The last authority reference has to be >= 1000 (Windows usage) + * and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits + * from a gid an be inserted with no overflow. + */ + +BOOL ntfs_valid_pattern(const SID *sid) +{ + int cnt; + u32 auth; + le32 leauth; + + cnt = sid->sub_authority_count; + leauth = sid->sub_authority[cnt-1]; + auth = le32_to_cpu(leauth); + return ((auth >= 1000) && (auth <= 0x7fffffff)); +} + +/* + * Compute the uid or gid associated to a SID + * through an implicit mapping + * + * Returns 0 (root) if it does not match pattern + */ + +static u32 findimplicit(const SID *xsid, const SID *pattern, int parity) +{ + BIGSID defsid; + SID *psid; + u32 xid; /* uid or gid */ + int cnt; + u32 carry; + le32 leauth; + u32 uauth; + u32 xlast; + u32 rlast; + + memcpy(&defsid,pattern,ntfs_sid_size(pattern)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + xid = 0; + if (xsid->sub_authority_count == cnt) { + psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1]; + leauth = xsid->sub_authority[cnt-1]; + xlast = le32_to_cpu(leauth); + leauth = pattern->sub_authority[cnt-1]; + rlast = le32_to_cpu(leauth); + + if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) { + /* direct check for basic situation */ + if (ntfs_same_sid(psid,xsid)) + xid = ((xlast - rlast) >> 1) & 0x3fffffff; + else { + /* + * check whether part of mapping had to be + * recorded in a higher level authority + */ + carry = 1; + do { + leauth = psid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + 1; + psid->sub_authority[cnt-2] + = cpu_to_le32(uauth); + } while (!ntfs_same_sid(psid,xsid) + && (++carry < 4)); + if (carry < 4) + xid = (((xlast - rlast) >> 1) + & 0x3fffffff) | (carry << 30); + } + } + } + return (xid); +} + +/* + * Find usid mapped to a Linux user + * Returns NULL if not found + */ + +const SID *ntfs_find_usid(const struct MAPPING* usermapping, + uid_t uid, SID *defusid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!uid) + sid = adminsid; + else { + p = usermapping; + while (p && p->xid && ((uid_t)p->xid != uid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defusid, p->sid, ntfs_sid_size(p->sid)); + cnt = defusid->sub_authority_count; + leauth = defusid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff); + defusid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (uid & 0xc0000000) { + leauth = defusid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3); + defusid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defusid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +const SID *ntfs_find_gsid(const struct MAPPING* groupmapping, + gid_t gid, SID *defgsid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!gid) + sid = adminsid; + else { + p = groupmapping; + while (p && p->xid && ((gid_t)p->xid != gid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defgsid, p->sid, ntfs_sid_size(p->sid)); + cnt = defgsid->sub_authority_count; + leauth = defgsid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1; + defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (gid & 0xc0000000) { + leauth = defgsid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3); + defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defgsid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux owner mapped to a usid + * Returns 0 (root) if not found + */ + +uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid) +{ + uid_t uid; + const struct MAPPING *p; + + p = usermapping; + while (p && p->xid && !ntfs_same_sid(usid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + uid = findimplicit(usid,p->sid,0); + else + uid = (p ? p->xid : 0); + return (uid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) +{ + gid_t gid; + const struct MAPPING *p; + + p = groupmapping; + while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + gid = findimplicit(gsid,p->sid,1); + else + gid = (p ? p->xid : 0); + return (gid); +} + +/* + * Check the validity of the ACEs in a DACL or SACL + */ + +static BOOL valid_acl(const ACL *pacl, unsigned int end) +{ + const ACCESS_ALLOWED_ACE *pace; + unsigned int offace; + unsigned int acecnt; + unsigned int acesz; + unsigned int nace; + BOOL ok; + + ok = TRUE; + acecnt = le16_to_cpu(pacl->ace_count); + offace = sizeof(ACL); + for (nace = 0; (nace < acecnt) && ok; nace++) { + /* be sure the beginning is within range */ + if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end) + ok = FALSE; + else { + pace = (const ACCESS_ALLOWED_ACE*) + &((const char*)pacl)[offace]; + acesz = le16_to_cpu(pace->size); + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + ok = FALSE; + offace += acesz; + } + } + return (ok); +} + +/* + * Do sanity checks on security descriptors read from storage + * basically, we make sure that every field holds within + * allocated storage + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + BOOL ok; + + ok = TRUE; + + /* + * first check overall size if within allocation range + * and a DACL is present + * and owner and group SID are valid + */ + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + offsacl = le32_to_cpu(phead->sacl); + offowner = le32_to_cpu(phead->owner); + offgroup = le32_to_cpu(phead->group); + pdacl = (const ACL*)&securattr[offdacl]; + psacl = (const ACL*)&securattr[offsacl]; + + /* + * size check occurs before the above pointers are used + * + * "DR Watson" standard directory on WinXP has an + * old revision and no DACL though SE_DACL_PRESENT is set + */ + if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (phead->revision == SECURITY_DESCRIPTOR_REVISION) + && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offowner + 2) < attrsz) + && (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offgroup + 2) < attrsz) + && (!offdacl + || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offdacl+sizeof(ACL) < attrsz))) + && (!offsacl + || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offsacl+sizeof(ACL) < attrsz))) + && !(phead->owner & const_cpu_to_le32(3)) + && !(phead->group & const_cpu_to_le32(3)) + && !(phead->dacl & const_cpu_to_le32(3)) + && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) + && ntfs_valid_sid((const SID*)&securattr[offowner]) + && ntfs_valid_sid((const SID*)&securattr[offgroup]) + /* + * if there is an ACL, as indicated by offdacl, + * require SE_DACL_PRESENT + * but "Dr Watson" has SE_DACL_PRESENT though no DACL + */ + && (!offdacl + || ((phead->control & SE_DACL_PRESENT) + && ((pdacl->revision == ACL_REVISION) + || (pdacl->revision == ACL_REVISION_DS)))) + /* same for SACL */ + && (!offsacl + || ((phead->control & SE_SACL_PRESENT) + && ((psacl->revision == ACL_REVISION) + || (psacl->revision == ACL_REVISION_DS))))) { + /* + * Check the DACL and SACL if present + */ + if ((offdacl && !valid_acl(pdacl,attrsz - offdacl)) + || (offsacl && !valid_acl(psacl,attrsz - offsacl))) + ok = FALSE; + } else + ok = FALSE; + return (ok); +} + +/* + * Copy the inheritable parts of an ACL + * + * Returns the size of the new ACL + * or zero if nothing is inheritable + */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir) +{ + unsigned int src; + unsigned int dst; + int oldcnt; + int newcnt; + unsigned int selection; + int nace; + int acesz; + int usidsz; + int gsidsz; + const ACCESS_ALLOWED_ACE *poldace; + ACCESS_ALLOWED_ACE *pnewace; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + + /* ACL header */ + + newacl->revision = ACL_REVISION; + newacl->alignment1 = 0; + newacl->alignment2 = const_cpu_to_le16(0); + src = dst = sizeof(ACL); + + selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE); + newcnt = 0; + oldcnt = le16_to_cpu(oldacl->ace_count); + for (nace = 0; nace < oldcnt; nace++) { + poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); + acesz = le16_to_cpu(poldace->size); + /* inheritance for access */ + if (poldace->flags & selection) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(acesz); + } + if (pnewace->mask & GENERIC_ALL) { + pnewace->mask &= ~GENERIC_ALL; + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_WRITE + | DIR_EXEC; + else + /* + * The last flag is not defined for a file, + * however Windows sets it, so do the same + */ + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_WRITE + | FILE_EXEC + | cpu_to_le32(0x40); + } + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + dst += acesz; + newcnt++; + } + /* inheritance for further inheritance */ + if (fordir + && (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + acesz = usidsz + 8; + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + acesz = gsidsz + 8; + } + dst += acesz; + newcnt++; + } + src += acesz; + } + /* + * Adjust header if something was inherited + */ + if (dst > sizeof(ACL)) { + newacl->ace_count = cpu_to_le16(newcnt); + newacl->size = cpu_to_le16(dst); + } else + dst = 0; + return (dst); +} + +#if POSIXACLS + +/* + * Do sanity checks on a Posix descriptor + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc) +{ + const struct POSIX_ACL *pacl; + int i; + BOOL ok; + u16 tag; + u32 id; + int perms; + struct { + u16 previous; + u32 previousid; + u16 tagsset; + mode_t mode; + int owners; + int groups; + int others; + } checks[2], *pchk; + + for (i=0; i<2; i++) { + checks[i].mode = 0; + checks[i].tagsset = 0; + checks[i].owners = 0; + checks[i].groups = 0; + checks[i].others = 0; + checks[i].previous = 0; + checks[i].previousid = 0; + } + ok = TRUE; + pacl = &pxdesc->acl; + /* + * header (strict for now) + */ + if ((pacl->version != POSIX_VERSION) + || (pacl->flags != 0) + || (pacl->filler != 0)) + ok = FALSE; + /* + * Reject multiple owner, group or other + * but do not require them to be present + * Also check the ACEs are in correct order + * which implies there is no duplicates + */ + for (i=0; iacccnt + pxdesc->defcnt; i++) { + if (i >= pxdesc->firstdef) + pchk = &checks[1]; + else + pchk = &checks[0]; + perms = pacl->ace[i].perms; + tag = pacl->ace[i].tag; + pchk->tagsset |= tag; + id = pacl->ace[i].id; + if (perms & ~7) ok = FALSE; + if ((tag < pchk->previous) + || ((tag == pchk->previous) + && (id <= pchk->previousid))) + ok = FALSE; + pchk->previous = tag; + pchk->previousid = id; + switch (tag) { + case POSIX_ACL_USER_OBJ : + if (pchk->owners++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + if (pchk->groups++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + case POSIX_ACL_OTHER : + if (pchk->others++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms; + break; + case POSIX_ACL_USER : + case POSIX_ACL_GROUP : + if (id == (u32)-1) + ok = FALSE; + break; + case POSIX_ACL_MASK : + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + default : + ok = FALSE; + break; + } + } + if ((pxdesc->acccnt > 0) + && ((checks[0].owners != 1) || (checks[0].groups != 1) + || (checks[0].others != 1))) + ok = FALSE; + /* do not check owner, group or other are present in */ + /* the default ACL, Windows does not necessarily set them */ + /* descriptor */ + if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef)) + ok = FALSE; + if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0)) + ok = FALSE; + /* check mode, unless null or no tag set */ + if (pxdesc->mode + && checks[0].tagsset + && (checks[0].mode != (pxdesc->mode & 0777))) + ok = FALSE; + /* check tagsset */ + if (pxdesc->tagsset != checks[0].tagsset) + ok = FALSE; + return (ok); +} + +/* + * Set standard header data into a Posix ACL + * The mode argument should provide the 3 upper bits of target mode + */ + +static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode) +{ + mode_t mode; + u16 tagsset; + struct POSIX_ACE *pace; + int i; + + mode = basemode & 07000; + tagsset = 0; + for (i=0; iacccnt; i++) { + pace = &pxdesc->acl.ace[i]; + tagsset |= pace->tag; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + mode |= (pace->perms & 7) << 6; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((pace->perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= pace->perms & 7; + break; + default : + break; + } + } + pxdesc->tagsset = tagsset; + pxdesc->mode = mode; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + return (mode); +} + +/* + * Sort ACEs in a Posix ACL + * This is useful for always getting reusable converted ACLs, + * it also helps in merging ACEs. + * Repeated tag+id are allowed and not merged here. + * + * Tags should be in ascending sequence and for a repeatable tag + * ids should be in ascending sequence. + */ + +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc) +{ + struct POSIX_ACL *pacl; + struct POSIX_ACE ace; + int i; + int offs; + BOOL done; + u16 tag; + u16 previous; + u32 id; + u32 previousid; + + + /* + * Check sequencing of tag+id in access ACE's + */ + pacl = &pxdesc->acl; + do { + done = TRUE; + previous = pacl->ace[0].tag; + previousid = pacl->ace[0].id; + for (i=1; iacccnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } while (!done); + /* + * Same for default ACEs + */ + do { + done = TRUE; + if ((pxdesc->defcnt) > 1) { + offs = pxdesc->firstdef; + previous = pacl->ace[offs].tag; + previousid = pacl->ace[offs].id; + for (i=offs+1; idefcnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } + } while (!done); +} + +/* + * Merge a new mode into a Posix descriptor + * The Posix descriptor is not reallocated, its size is unchanged + * + * returns 0 if ok + */ + +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode) +{ + int i; + BOOL maskfound; + struct POSIX_ACE *pace; + int todo; + + maskfound = FALSE; + todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER; + for (i=pxdesc->acccnt-1; i>=0; i--) { + pace = &pxdesc->acl.ace[i]; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + pace->perms = (mode >> 6) & 7; + todo &= ~POSIX_ACL_USER_OBJ; + break; + case POSIX_ACL_GROUP_OBJ : + if (!maskfound) + pace->perms = (mode >> 3) & 7; + todo &= ~POSIX_ACL_GROUP_OBJ; + break; + case POSIX_ACL_MASK : + pace->perms = (mode >> 3) & 7; + maskfound = TRUE; + break; + case POSIX_ACL_OTHER : + pace->perms = mode & 7; + todo &= ~POSIX_ACL_OTHER; + break; + default : + break; + } + } + pxdesc->mode = mode; + return (todo ? -1 : 0); +} + +/* + * Replace an access or default Posix ACL + * The resulting ACL is checked for validity + * + * Returns a new ACL or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt) +{ + struct POSIX_SECURITY *newpxdesc; + size_t newsize; + int offset; + int oldoffset; + int i; + + if (deflt) + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE); + else + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(newsize); + if (newpxdesc) { + if (deflt) { + offset = oldpxdesc->acccnt; + newpxdesc->acccnt = oldpxdesc->acccnt; + newpxdesc->defcnt = count; + newpxdesc->firstdef = offset; + /* copy access ACEs */ + for (i=0; iacccnt; i++) + newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i]; + /* copy default ACEs */ + for (i=0; iacl.ace[i + offset] = newacl->ace[i]; + } else { + offset = count; + newpxdesc->acccnt = count; + newpxdesc->defcnt = oldpxdesc->defcnt; + newpxdesc->firstdef = count; + /* copy access ACEs */ + for (i=0; iacl.ace[i] = newacl->ace[i]; + /* copy default ACEs */ + oldoffset = oldpxdesc->firstdef; + for (i=0; idefcnt; i++) + newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset]; + } + /* assume special flags unchanged */ + posix_header(newpxdesc, oldpxdesc->mode); + if (!ntfs_valid_posix(newpxdesc)) { + /* do not log, this is an application error */ + free(newpxdesc); + newpxdesc = (struct POSIX_SECURITY*)NULL; + errno = EINVAL; + } + } else + errno = ENOMEM; + return (newpxdesc); +} + +/* + * Build an inherited Posix descriptor from parent + * descriptor (if any) restricted to creation mode + * + * Returns the inherited descriptor or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t mask, BOOL isdir) +{ + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + int count; + int defcnt; + int size; + int i; + s16 tagsset; + + if (pxdesc && pxdesc->defcnt) { + if (isdir) + count = 2*pxdesc->defcnt + 3; + else + count = pxdesc->defcnt + 3; + } else + count = 3; + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE)); + if (pydesc) { + /* + * Copy inherited tags and adapt perms + * Use requested mode, ignoring umask + * (not possible with older versions of fuse) + */ + tagsset = 0; + defcnt = (pxdesc ? pxdesc->defcnt : 0); + for (i=defcnt-1; i>=0; i--) { + pyace = &pydesc->acl.ace[i]; + *pyace = pxdesc->acl.ace[pxdesc->firstdef + i]; + switch (pyace->tag) { + case POSIX_ACL_USER_OBJ : + pyace->perms &= (mode >> 6) & 7; + break; + case POSIX_ACL_GROUP_OBJ : + if (!(tagsset & POSIX_ACL_MASK)) + pyace->perms &= (mode >> 3) & 7; + break; + case POSIX_ACL_OTHER : + pyace->perms &= mode & 7; + break; + case POSIX_ACL_MASK : + pyace->perms &= (mode >> 3) & 7; + break; + default : + break; + } + tagsset |= pyace->tag; + } + pydesc->acccnt = defcnt; + /* + * If some standard tags were missing, append them from mode + * and sort the list + * Here we have to use the umask'ed mode + */ + if (~tagsset & (POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) { + i = defcnt; + /* owner was missing */ + if (!(tagsset & POSIX_ACL_USER_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 6) & 7; + tagsset |= POSIX_ACL_USER_OBJ; + i++; + } + /* owning group was missing */ + if (!(tagsset & POSIX_ACL_GROUP_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 3) & 7; + tagsset |= POSIX_ACL_GROUP_OBJ; + i++; + } + /* other was missing */ + if (!(tagsset & POSIX_ACL_OTHER)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_OTHER; + pyace->id = -1; + pyace->perms = mode & ~mask & 7; + tagsset |= POSIX_ACL_OTHER; + i++; + } + pydesc->acccnt = i; + pydesc->firstdef = i; + pydesc->defcnt = 0; + ntfs_sort_posix(pydesc); + } + + /* + * append as a default ACL if a directory + */ + pydesc->firstdef = pydesc->acccnt; + if (defcnt && isdir) { + size = sizeof(struct POSIX_ACE)*defcnt; + memcpy(&pydesc->acl.ace[pydesc->firstdef], + &pxdesc->acl.ace[pxdesc->firstdef],size); + pydesc->defcnt = defcnt; + } else { + pydesc->defcnt = 0; + } + /* assume special bits are not inherited */ + posix_header(pydesc, mode & 07000); + if (!ntfs_valid_posix(pydesc)) { + ntfs_log_error("Error building an inherited Posix desc\n"); + errno = EIO; + free(pydesc); + pydesc = (struct POSIX_SECURITY*)NULL; + } + } else + errno = ENOMEM; + return (pydesc); +} + +static int merge_lists_posix(struct POSIX_ACE *targetace, + const struct POSIX_ACE *firstace, + const struct POSIX_ACE *secondace, + int firstcnt, int secondcnt) +{ + int k; + + k = 0; + /* + * No list is exhausted : + * if same tag+id in both list : + * ignore ACE from second list + * else take the one with smaller tag+id + */ + while ((firstcnt > 0) && (secondcnt > 0)) + if ((firstace->tag == secondace->tag) + && (firstace->id == secondace->id)) { + secondace++; + secondcnt--; + } else + if ((firstace->tag < secondace->tag) + || ((firstace->tag == secondace->tag) + && (firstace->id < secondace->id))) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } else { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + /* + * One list is exhausted, copy the other one + */ + while (firstcnt > 0) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } + while (secondcnt > 0) { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + return (k); +} + +/* + * Merge two Posix ACLs + * The input ACLs have to be adequately sorted + * + * Returns the merged ACL, which is allocated and has to be freed by caller, + * or NULL if failed + */ + +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second) +{ + struct POSIX_SECURITY *pxdesc; + struct POSIX_ACE *targetace; + const struct POSIX_ACE *firstace; + const struct POSIX_ACE *secondace; + size_t size; + int k; + + size = sizeof(struct POSIX_SECURITY) + + (first->acccnt + first->defcnt + + second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE); + pxdesc = (struct POSIX_SECURITY*)malloc(size); + if (pxdesc) { + /* + * merge access ACEs + */ + firstace = first->acl.ace; + secondace = second->acl.ace; + targetace = pxdesc->acl.ace; + k = merge_lists_posix(targetace,firstace,secondace, + first->acccnt,second->acccnt); + pxdesc->acccnt = k; + /* + * merge default ACEs + */ + pxdesc->firstdef = k; + firstace = &first->acl.ace[first->firstdef]; + secondace = &second->acl.ace[second->firstdef]; + targetace = &pxdesc->acl.ace[k]; + k = merge_lists_posix(targetace,firstace,secondace, + first->defcnt,second->defcnt); + pxdesc->defcnt = k; + /* + * build header + */ + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + pxdesc->mode = 0; + pxdesc->tagsset = 0; + } else + errno = ENOMEM; + return (pxdesc); +} + +struct BUILD_CONTEXT { + BOOL isdir; + BOOL adminowns; + BOOL groupowns; + u16 selfuserperms; + u16 selfgrpperms; + u16 grpperms; + u16 othperms; + u16 mask; + u16 designates; + u16 withmask; + u16 rootspecial; +} ; + + + +static BOOL build_user_denials(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL rejected; + BOOL rootuser; + BOOL avoidmask; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each designated user (except root) + * WRITE_OWNER is inserted so that + * the mask can be identified + */ + if (!avoidmask && !rootuser) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns && !rootuser) { + if (!pset->groupowns) { + mixperms = pset->grpperms | pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } else { + mixperms = ~pset->grpperms & pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + +static BOOL build_user_grants(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + u16 perms; + u16 tag; + BOOL rejected; + BOOL rootuser; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid) + sidsz = ntfs_sid_size(sid); + else + rejected = TRUE; + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + sidsz = ntfs_sid_size(sid); + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->flags = flags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + return (!rejected); +} + + + /* a grant ACE for group */ + /* unless group-obj has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + +static BOOL build_group_denials_grant(ACL *pacl, + const SID *gsid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL avoidmask; + BOOL rootgroup; + BOOL rejected; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + rootgroup = FALSE; + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_GROUP_OBJ) + sid = gsid; + else + if (pxace->id) + sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + pxace->id, (SID*)&defsid); + else { + sid = adminsid; + rootgroup = TRUE; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each group + * WRITE_OWNER is inserted so that + * the mask can be identified + * Note : this mask may lead on Windows to + * deny rights to administrators belonging + * to some user group + */ + if ((!avoidmask && !rootgroup) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ))) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + if (!rejected + && (pset->adminowns + || pset->groupowns + || avoidmask + || rootgroup + || (perms != pset->othperms))) { + grants = WORLD_RIGHTS; + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns + && !pset->groupowns + && !rootgroup) { + mixperms = pset->othperms; + if (tag == POSIX_ACL_GROUP_OBJ) + mixperms |= pset->selfgrpperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + + /* now insert grants to group if more than world */ + if (pset->adminowns + || pset->groupowns + || (avoidmask && (pset->designates || pset->withmask)) + || (perms & ~pset->othperms) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ)) + || (tag == POSIX_ACL_GROUP)) { + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + + +/* + * Build an ACL composed of several ACE's + * returns size of ACL or zero if failed + * + * Three schemes are defined : + * + * 1) if root is neither owner nor group up to 7 ACE's are set up : + * - denials to owner (preventing grants to world or group to apply) + * + mask denials to designated user (unless mask allows all) + * + denials to designated user + * - grants to owner (always present - first grant) + * + grants to designated user + * + mask denial to group (unless mask allows all) + * - denials to group (preventing grants to world to apply) + * - grants to group (unless group has no more than world rights) + * + mask denials to designated group (unless mask allows all) + * + grants to designated group + * + denials to designated group + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * The same scheme is applied for Posix ACLs, with the mask represented + * as denials prepended to grants for designated users and groups + * + * This is inspired by an Internet Draft from Marius Aamodt Eriksen + * for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt) + * More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt) + * are not followed, as they ignore the Posix mask and lead to + * loss of compatibility with Linux implementations on other fs. + * + * Note that denials to group are located after grants to owner. + * This only occurs in the unfrequent situation where world + * has more rights than group and cannot be avoided if owner and other + * have some common right which is denied to group (eg for mode 745 + * executing has to be denied to group, but not to owner or world). + * This rare situation is processed by Windows correctly, but + * Windows utilities may want to change the order, with a + * consequence of applying the group denials to the Windows owner. + * The interpretation on Linux is not affected by the order change. + * + * 2) if root is either owner or group, two problems arise : + * - granting full rights to administrator (as needed to transpose + * to Windows rights bypassing granting to root) would imply + * Linux permissions to always be seen as rwx, no matter the chmod + * - there is no different SID to separate an administrator owner + * from an administrator group. Hence Linux permissions for owner + * would always be similar to permissions to group. + * + * as a work-around, up to 5 ACE's are set up if owner or group : + * - grants to owner, always present at first position + * - grants to group, always present + * - grants to world, unless none + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (owner, group and administrator are the same, + * as a consequence any denials would damage administrator rights) + * but on Linux, privileges to administrator are ignored (they + * are not needed as root has always full privileges), and + * neither grants to group are applied to owner, nor grants to + * world are applied to owner or group. + * + * 3) finally a similar situation arises when group is owner (they + * have the same SID), but is not root. + * In this situation up to 6 ACE's are set up : + * + * - denials to owner (preventing grants to world to apply) + * - grants to owner (always present) + * - grants to group (unless groups has same rights as world) + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (as owner and group are the same), but this has + * no impact on administrator rights + * + * Special flags (S_ISVTX, S_ISGID, S_ISUID) : + * an extra null ACE is inserted to hold these flags, using + * the same conventions as cygwin. + * + */ + +static int buildacls_posix(struct MAPPING* const mapping[], + char *secattr, int offs, const struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + struct BUILD_CONTEXT aceset[2], *pset; + BOOL adminowns; + BOOL groupowns; + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + const struct POSIX_ACE *pxace; + BOOL ok; + mode_t mode; + u16 tag; + u16 perms; + ACE_FLAGS flags; + int pos; + int i; + int k; + BIGSID defsid; + const SID *sid; + int acecnt; + int usidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + + usidsz = ntfs_sid_size(usid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + mode = pxdesc->mode; + /* adminowns and groupowns are used for both lists */ + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + ok = TRUE; + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(0); + pacl->alignment2 = const_cpu_to_le16(0); + + /* + * Determine what is allowed to some group or world + * to prevent designated users or other groups to get + * rights from groups or world + * Do the same if owner and group appear as designated + * user or group + * Also get global mask + */ + for (k=0; k<2; k++) { + pset = &aceset[k]; + pset->selfuserperms = 0; + pset->selfgrpperms = 0; + pset->grpperms = 0; + pset->othperms = 0; + pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + pset->designates = 0; + pset->withmask = 0; + pset->rootspecial = 0; + pset->adminowns = adminowns; + pset->groupowns = groupowns; + pset->isdir = isdir; + } + + for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) { + if (i >= pxdesc->acccnt) { + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + switch (pxace->tag) { + case POSIX_ACL_USER : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,usid)) + pset->selfuserperms |= pxace->perms; + } else + /* root as designated user is processed apart */ + pset->rootspecial = TRUE; + break; + case POSIX_ACL_GROUP : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_GSID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,gsid)) + pset->selfgrpperms |= pxace->perms; + } else + /* root as designated group is processed apart */ + pset->rootspecial = TRUE; + /* fall through */ + case POSIX_ACL_GROUP_OBJ : + pset->grpperms |= pxace->perms; + break; + case POSIX_ACL_OTHER : + pset->othperms = pxace->perms; + break; + case POSIX_ACL_MASK : + pset->withmask++; + pset->mask = pxace->perms; + default : + break; + } + } + +if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) { +ntfs_log_error("** error : access and default not consecutive\n"); +return (0); +} + /* + * First insert all denials for owner and each + * designated user (with mask if needed) + */ + + pacl->ace_count = const_cpu_to_le16(0); + pacl->size = const_cpu_to_le16(sizeof(ACL)); + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* insert denial ACEs for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + + ok = build_user_denials(pacl, + usid, mapping, flags, pxace, pset); + break; + default : + break; + } + } + + /* + * for directories, insert a world execution denial + * inherited to plain files. + * This is to prevent Windows from granting execution + * of files through inheritance from parent directory + */ + + if (isdir && ok) { + pos = le16_to_cpu(pacl->size); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + + /* + * now insert (if needed) + * - grants to owner and designated users + * - mask and denials for all groups + * - grants to other + */ + + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* ACE for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + ok = build_user_grants(pacl,usid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_GROUP : + case POSIX_ACL_GROUP_OBJ : + + /* denials and grants for groups */ + + ok = build_group_denials_grant(pacl,gsid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_OTHER : + + /* grants for other users */ + + pos = le16_to_cpu(pacl->size); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + grants = WORLD_RIGHTS; + if (isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + break; + } + } + + if (!ok) { + errno = EINVAL; + pos = 0; + } else { + /* an ACE for administrators */ + /* always full access */ + + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (isdir) + flags = OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE; + else + flags = NO_PROPAGATE_INHERIT_ACE; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + } + return (ok ? pos : 0); +} + +#endif /* POSIXACLS */ + +static int buildacls(char *secattr, int offs, mode_t mode, int isdir, + const SID * usid, const SID * gsid) +{ + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + BOOL adminowns; + BOOL groupowns; + ACE_FLAGS gflags; + int pos; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + le32 denials; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(1); + pacl->alignment2 = const_cpu_to_le16(0); + pos = sizeof(ACL); + acecnt = 0; + + /* compute a grant ACE for owner */ + /* this ACE will be inserted after denial for owner */ + + grants = OWNER_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXUSR) + grants |= DIR_EXEC; + if (mode & S_IWUSR) + grants |= DIR_WRITE; + if (mode & S_IRUSR) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXUSR) + grants |= FILE_EXEC; + if (mode & S_IWUSR) + grants |= FILE_WRITE; + if (mode & S_IRUSR) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + if (!adminowns) { + if (!groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= DIR_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= DIR_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= FILE_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= FILE_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= FILE_READ; + } + } else { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= DIR_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= DIR_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= FILE_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= FILE_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(usidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + } + } + /* + * for directories, a world execution denial + * inherited to plain files + */ + + if (isdir) { + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + } + + + /* now insert grants to owner */ + pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(usidsz + 8); + pgace->flags = gflags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + + /* a grant ACE for group */ + /* unless group has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + + if (adminowns + || groupowns + || (((mode >> 3) ^ mode) & 7)) { + grants = WORLD_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXGRP) + grants |= DIR_EXEC; + if (mode & S_IWGRP) + grants |= DIR_WRITE; + if (mode & S_IRGRP) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXGRP) + grants |= FILE_EXEC; + if (mode & S_IWGRP) + grants |= FILE_WRITE; + if (mode & S_IRGRP) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + if (!adminowns && !groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + denials |= DIR_EXEC; + if (mode & S_IWOTH) + denials |= DIR_WRITE; + if (mode & S_IROTH) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + denials |= FILE_EXEC; + if (mode & S_IWOTH) + denials |= FILE_WRITE; + if (mode & S_IROTH) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(gsidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + if (adminowns + || groupowns + || ((mode >> 3) & ~mode & 7)) { + /* now insert grants to group */ + /* if more rights than other */ + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = gflags; + pgace->size = cpu_to_le16(gsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + /* an ACE for world users */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + grants = WORLD_RIGHTS; + if (isdir) { + pgace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + grants |= DIR_EXEC; + if (mode & S_IWOTH) + grants |= DIR_WRITE; + if (mode & S_IROTH) + grants |= DIR_READ; + } else { + pgace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + grants |= FILE_EXEC; + if (mode & S_IWOTH) + grants |= FILE_WRITE; + if (mode & S_IROTH) + grants |= FILE_READ; + } + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + + /* an ACE for administrators */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (pos); +} + +#if POSIXACLS + +/* + * Build a full security descriptor from a Posix ACL + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int k; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 3*(8 + gsidsz) /* three possible ACE for group and mask */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (pxdesc->mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + /* account for non-owning users and groups */ + for (k=0; kacccnt; k++) { + if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER) + || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP)) + newattrsz += 3*40; /* fixme : maximum size */ + } + /* account for default ACE's */ + newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls_posix(mapping,newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + pxdesc, isdir, usid, gsid); + if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz)) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* ACL failure (errno set) or overflow */ + free(newattr); + newattr = (char*)NULL; + if (aclsz) { + /* hope error was detected before overflowing */ + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } + } else + errno = ENOMEM; + return (newattr); +} + +#endif /* POSIXACLS */ + +/* + * Build a full security descriptor + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 2*(8 + gsidsz) /* two possible ACE for group */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls(newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + mode, isdir, usid, gsid); + if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* hope error was detected before overflowing */ + free(newattr); + newattr = (char*)NULL; + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } else + errno = ENOMEM; + return (newattr); +} + +/* + * Create a mode_t permission set + * from owner, group and world grants as represented in ACEs + */ + +static int merge_permissions(BOOL isdir, + le32 owner, le32 group, le32 world, le32 special) + +{ + int perm; + + perm = 0; + /* build owner permission */ + if (owner) { + if (isdir) { + /* exec if any of list, traverse */ + if (owner & DIR_GEXEC) + perm |= S_IXUSR; + /* write if any of addfile, adddir, delchild */ + if (owner & DIR_GWRITE) + perm |= S_IWUSR; + /* read if any of list */ + if (owner & DIR_GREAD) + perm |= S_IRUSR; + } else { + /* exec if execute or generic execute */ + if (owner & FILE_GEXEC) + perm |= S_IXUSR; + /* write if any of writedata or generic write */ + if (owner & FILE_GWRITE) + perm |= S_IWUSR; + /* read if any of readdata or generic read */ + if (owner & FILE_GREAD) + perm |= S_IRUSR; + } + } + /* build group permission */ + if (group) { + if (isdir) { + /* exec if any of list, traverse */ + if (group & DIR_GEXEC) + perm |= S_IXGRP; + /* write if any of addfile, adddir, delchild */ + if (group & DIR_GWRITE) + perm |= S_IWGRP; + /* read if any of list */ + if (group & DIR_GREAD) + perm |= S_IRGRP; + } else { + /* exec if execute */ + if (group & FILE_GEXEC) + perm |= S_IXGRP; + /* write if any of writedata, appenddata */ + if (group & FILE_GWRITE) + perm |= S_IWGRP; + /* read if any of readdata */ + if (group & FILE_GREAD) + perm |= S_IRGRP; + } + } + /* build world permission */ + if (world) { + if (isdir) { + /* exec if any of list, traverse */ + if (world & DIR_GEXEC) + perm |= S_IXOTH; + /* write if any of addfile, adddir, delchild */ + if (world & DIR_GWRITE) + perm |= S_IWOTH; + /* read if any of list */ + if (world & DIR_GREAD) + perm |= S_IROTH; + } else { + /* exec if execute */ + if (world & FILE_GEXEC) + perm |= S_IXOTH; + /* write if any of writedata, appenddata */ + if (world & FILE_GWRITE) + perm |= S_IWOTH; + /* read if any of readdata */ + if (world & FILE_GREAD) + perm |= S_IROTH; + } + } + /* build special permission flags */ + if (special) { + if (special & FILE_APPEND_DATA) + perm |= S_ISUID; + if (special & FILE_WRITE_DATA) + perm |= S_ISGID; + if (special & FILE_READ_DATA) + perm |= S_ISVTX; + } + return (perm); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (standard case : different owner, group and administrator) + */ + +static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc, + BOOL groupowns, int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t grantgrps; + mode_t grantwrld; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + tagsset = 0; + /* + * Determine what is granted to some group or world + * Also get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + pxace = posix_desc->acl.ace; + grantgrps = 0; + grantwrld = 0; + denywrld = 0; + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec unless for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } else { + switch (pxace[j].tag) { + case POSIX_ACL_GROUP_OBJ : + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_GROUP : + if (pxace[j].id) + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_OTHER : + grantwrld = pxace[j].perms; + break; + default : + break; + } + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted and what is denied. + * It is important the ACEs have been sorted + */ + j = start; + k = target; + while (j < (start + count)) { + tag = pxace[j].tag; + id = pxace[j].id; + if (pxace[j].perms & POSIX_PERM_DENIAL) { + deny = pxace[j].perms | denywrld; + allow = 0; + } else { + deny = denywrld; + allow = pxace[j].perms; + } + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (pxace[j].perms & POSIX_PERM_DENIAL) + deny |= pxace[j].perms; + else + allow |= pxace[j].perms; + j++; + } + /* + * Build the permissions equivalent to grants and denials + */ + if (groupowns) { + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~deny; + } else + switch (tag) { + case POSIX_ACL_USER_OBJ : + perms = (allow | grantgrps | grantwrld) & ~deny; + break; + case POSIX_ACL_USER : + if (id) + perms = (allow | grantgrps | grantwrld) + & ~deny; + else + perms = allow; + break; + case POSIX_ACL_GROUP_OBJ : + perms = (allow | grantwrld) & ~deny; + break; + case POSIX_ACL_GROUP : + if (id) + perms = (allow | grantwrld) & ~deny; + else + perms = allow; + break; + case POSIX_ACL_MASK : + perms = ~deny; + break; + default : + perms = allow & ~deny; + break; + } + /* + * Store into a Posix ACE + */ + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID)); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (standard case : different owner, group and administrator) + */ + +static int build_std_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL noown; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + noown = TRUE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if (ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) { + noown = FALSE; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && !(pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowgrp |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + /* + * No indication about owner's rights : grant basic rights + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + if (noown) + allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE); + /* + * Add to owner rights granted to group or world + * unless denied personaly, and add to group rights + * granted to world unless denied specifically + */ + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner and group are the same, + * and not administrator) + */ + +static int build_owngrp_permissions(const char *securattr, + const SID *usid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + le32 special; + BOOL grppresent; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + grppresent = FALSE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + } else + if (ntfs_same_sid(usid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + grppresent = TRUE; + } + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + if (!grppresent) + allowgrp = allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (special case : owner or/and group is administrator) + */ + +static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, + int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + pxace = posix_desc->acl.ace; + tagsset = 0; + denywrld = 0; + /* + * Get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec not for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted (denials are ignored) + * It is important the ACEs have been sorted + */ + j = start; + k = target; + deny = 0; + while (j < (start + count)) { + allow = 0; + tag = pxace[j].tag; + id = pxace[j].id; + if (tag == POSIX_ACL_MASK) { + deny = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == POSIX_ACL_MASK)) + j++; + } else { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow |= pxace[j].perms; + j++; + } + } + + /* + * Store the grants into a Posix ACE + */ + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~denywrld; + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= perms & (S_ISVTX | S_ISUID | S_ISGID); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner or/and group is administrator) + */ + + +static int build_ownadmin_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL firstapply; + int isforeign; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + firstapply = TRUE; + isforeign = 3; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE) + && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (((pace->mask & WRITE_OWNER) && firstapply))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowown |= pace->mask; + isforeign &= ~1; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + isforeign &= ~2; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } + firstapply = FALSE; + } else + if (!(pace->flags & INHERIT_ONLY_ACE)) + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + offace += le16_to_cpu(pace->size); + } + if (isforeign) { + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + } + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if OWNERFROMACL + +/* + * Define the owner of a file as the first user allowed + * to change the owner, instead of the user defined as owner. + * + * This produces better approximations for files written by a + * Windows user in an inheritable directory owned by another user, + * as the access rights are inheritable but the ownership is not. + * + * An important case is the directories "Documents and Settings/user" + * which the users must have access to, though Windows considers them + * as owned by administrator. + */ + +const SID *ntfs_acl_owner(const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL found; + + found = FALSE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + nace = 0; + do { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->mask & WRITE_OWNER) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_is_user_sid(&pace->sid)) + found = TRUE; + offace += le16_to_cpu(pace->size); + } while (!found && (++nace < acecnt)); + } + if (found) + usid = &pace->sid; + else + usid = (const SID*)&securattr[le32_to_cpu(phead->owner)]; + return (usid); +} + +#else + +/* + * Special case for files owned by administrator with full + * access granted to a mapped user : consider this user as the tenant + * of the file. + * + * This situation cannot be represented with Linux concepts and can + * only be found for files or directories created by Windows. + * Typical situation : directory "Documents and Settings/user" which + * is on the path to user's files and must be given access to user + * only. + * + * Check file is owned by administrator and no user has rights before + * calling. + * Returns the uid of tenant or zero if none + */ + + +static uid_t find_tenant(struct MAPPING *const mapping[], + const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + uid_t tid; + uid_t xid; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + tid = 0; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->type == ACCESS_ALLOWED_ACE_TYPE) + && (pace->mask & DIR_WRITE)) { + xid = NTFS_FIND_USER(mapping[MAPUSERS], &pace->sid); + if (xid) tid = xid; + } + offace += le16_to_cpu(pace->size); + } + return (tid); +} + +#endif /* OWNERFROMACL */ + +#if POSIXACLS + +/* + * Build Posix permissions from an ACL + * returns a pointer to the requested permissions + * or a null pointer (with errno set) if there is a problem + * + * If the NTFS ACL was created according to our rules, the retrieved + * Posix ACL should be the exact ACL which was set. However if + * the NTFS ACL was built by a different tool, the result could + * be a a poor approximation of what was expected + */ + +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING *const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + struct POSIX_ACE *pxace; + struct { + uid_t prevuid; + gid_t prevgid; + int groupmasks; + s16 tagsset; + BOOL gotowner; + BOOL gotownermask; + BOOL gotgroup; + mode_t permswrld; + } ctx[2], *pctx; + int offdacl; + int offace; + int alloccnt; + int acecnt; + uid_t uid; + gid_t gid; + int i,j; + int k,l; + BOOL ignore; + BOOL adminowns; + BOOL groupowns; + BOOL firstinh; + BOOL genericinh; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + adminowns = FALSE; + groupowns = ntfs_same_sid(gsid,usid); + firstinh = FALSE; + genericinh = FALSE; + /* + * Build a raw posix security descriptor + * by just translating permissions and ids + * Add 2 to the count of ACE to be able to insert + * a group ACE later in access and default ACLs + * and add 2 more to be able to insert ACEs for owner + * and 2 more for other + */ + alloccnt = acecnt + 6; + pxdesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + + alloccnt*sizeof(struct POSIX_ACE)); + k = 0; + l = alloccnt; + for (i=0; i<2; i++) { + pctx = &ctx[i]; + pctx->permswrld = 0; + pctx->prevuid = -1; + pctx->prevgid = -1; + pctx->groupmasks = 0; + pctx->tagsset = 0; + pctx->gotowner = FALSE; + pctx->gotgroup = FALSE; + pctx->gotownermask = FALSE; + } + for (j=0; jflags & INHERIT_ONLY_ACE) { + pxace = &pxdesc->acl.ace[l - 1]; + pctx = &ctx[1]; + } else { + pxace = &pxdesc->acl.ace[k]; + pctx = &ctx[0]; + } + ignore = FALSE; + /* + * grants for root as a designated user or group + */ + if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&pace->sid, adminsid)) { + pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER); + pxace->id = 0; + if ((pace->mask & (GENERIC_ALL | WRITE_OWNER)) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = genericinh = TRUE; + } else + if (ntfs_same_sid(usid, &pace->sid)) { + pxace->id = -1; + /* + * Owner has no write-owner right : + * a group was defined same as owner + * or admin was owner or group : + * denials are meant to owner + * and grants are meant to group + */ + if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) { + if (ntfs_same_sid(gsid,usid)) { + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + } else { + if (ntfs_same_sid(&pace->sid,usid)) + groupowns = TRUE; + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->tag = POSIX_ACL_GROUP; + pxace->id = gid; + pctx->prevgid = gid; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + pxace->tag = POSIX_ACL_USER; + pxace->id = uid; + } else + ignore = TRUE; + } + } + } else { + /* + * when group owns, late denials for owner + * mean group mask + */ + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pctx->gotownermask = TRUE; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotowner = TRUE; + if (pctx->gotownermask && !pctx->gotowner) { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } else + pxace->tag = POSIX_ACL_USER_OBJ; + /* system ignored, and admin */ + /* ignored at first position */ + if (pace->flags & INHERIT_ONLY_ACE) { + if ((firstinh && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (!firstinh) { + firstinh = TRUE; + } + } else { + if ((adminowns && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (ntfs_same_sid(usid,adminsid)) + adminowns = TRUE; + } + } + } + } else if (ntfs_same_sid(gsid, &pace->sid)) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pctx->gotgroup || (pctx->groupmasks > 1)) { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->id = gid; + pxace->tag = POSIX_ACL_GROUP; + pctx->prevgid = gid; + } else + ignore = TRUE; + } else { + pxace->id = -1; + pxace->tag = POSIX_ACL_GROUP_OBJ; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotgroup = TRUE; + } + + if (ntfs_same_sid(gsid,adminsid) + || ntfs_same_sid(gsid,systemsid)) { + if (pace->mask & (WRITE_OWNER | GENERIC_ALL)) + ignore = TRUE; + if (ntfs_same_sid(gsid,adminsid)) + adminowns = TRUE; + else + genericinh = ignore; + } + } + } else if (is_world_sid((const SID*)&pace->sid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_OTHER; + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = TRUE; + } else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_SPECIAL; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevuid != uid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_MASK; + } else { + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } + pctx->prevuid = uid; + } else { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevgid != gid)) { + pxace->tag = POSIX_ACL_MASK; + pctx->groupmasks++; + } else { + pxace->tag = POSIX_ACL_GROUP; + } + pxace->id = gid; + pctx->prevgid = gid; + } else { + /* + * do not grant rights to unknown + * people and do not define root as a + * designated user or group + */ + ignore = TRUE; + } + } + } + if (!ignore) { + pxace->perms = 0; + /* specific decoding for vtx/uid/gid */ + if (pxace->tag == POSIX_ACL_SPECIAL) { + if (pace->mask & FILE_APPEND_DATA) + pxace->perms |= S_ISUID; + if (pace->mask & FILE_WRITE_DATA) + pxace->perms |= S_ISGID; + if (pace->mask & FILE_READ_DATA) + pxace->perms |= S_ISVTX; + } else + if (isdir) { + if (pace->mask & DIR_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & DIR_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & DIR_GREAD) + pxace->perms |= POSIX_PERM_R; + if ((pace->mask & GENERIC_ALL) + && (pace->flags & INHERIT_ONLY_ACE)) + pxace->perms |= POSIX_PERM_X + | POSIX_PERM_W + | POSIX_PERM_R; + } else { + if (pace->mask & FILE_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & FILE_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & FILE_GREAD) + pxace->perms |= POSIX_PERM_R; + } + + if (pace->type != ACCESS_ALLOWED_ACE_TYPE) + pxace->perms |= POSIX_PERM_DENIAL; + else + if (pxace->tag == POSIX_ACL_OTHER) + pctx->permswrld = pxace->perms; + pctx->tagsset |= pxace->tag; + if (pace->flags & INHERIT_ONLY_ACE) { + l--; + } else { + k++; + } + } + offace += le16_to_cpu(pace->size); + } + /* + * Create world perms if none (both lists) + */ + for (i=0; i<2; i++) + if ((genericinh || !i) + && !(ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_OTHER; + pxace->id = -1; + pxace->perms = 0; + ctx[i].tagsset |= POSIX_ACL_OTHER; + ctx[i].permswrld = 0; + } + /* + * Set basic owner perms if none (both lists) + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + for (i=0; i<2; i++) + if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ) + && (ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_USER_OBJ; + pxace->id = -1; + pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X; + ctx[i].tagsset |= POSIX_ACL_USER_OBJ; + } + /* + * Duplicate world perms as group_obj perms if none + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & POSIX_ACL_OTHER) + && !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + pxace->perms = ctx[i].permswrld; + ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ; + } + /* + * Also duplicate world perms as group perms if they + * were converted to mask and not followed by a group entry + */ + if (ctx[0].groupmasks) { + for (j=k-2; j>=0; j--) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[k]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[0].permswrld; + ctx[0].tagsset |= POSIX_ACL_GROUP; + k++; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + if (ctx[1].groupmasks) { + for (j=l; j<(alloccnt-1); j++) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[l - 1]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[1].permswrld; + ctx[1].tagsset |= POSIX_ACL_GROUP; + l--; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + + /* + * Insert default mask if none present and + * there are designated users or groups + * (the space for it has not beed used) + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP)) + && !(ctx[i].tagsset & POSIX_ACL_MASK)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + pxace->perms = POSIX_PERM_DENIAL; + ctx[i].tagsset |= POSIX_ACL_MASK; + } + + if (k > l) { + ntfs_log_error("Posix descriptor is longer than expected\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } else { + pxdesc->acccnt = k; + pxdesc->defcnt = alloccnt - l; + pxdesc->firstdef = l; + pxdesc->tagsset = ctx[0].tagsset; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + ntfs_sort_posix(pxdesc); + if (adminowns) { + k = norm_ownadmin_permissions_posix(pxdesc, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_ownadmin_permissions_posix(pxdesc, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } else { + k = norm_std_permissions_posix(pxdesc,groupowns, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_std_permissions_posix(pxdesc,groupowns, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } + } + if (pxdesc && !ntfs_valid_posix(pxdesc)) { + ntfs_log_error("Invalid Posix descriptor built\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } + return (pxdesc); +} + +#endif /* POSIXACLS */ + +/* + * Build unix-style (mode_t) permissions from an ACL + * returns the requested permissions + * or a negative result (with errno set) if there is a problem + */ + +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + int perm; + BOOL adminowns; + BOOL groupowns; + + adminowns = ntfs_same_sid(usid,adminsid) + || ntfs_same_sid(gsid,adminsid); + groupowns = !adminowns && ntfs_same_sid(gsid,usid); + if (adminowns) + perm = build_ownadmin_permissions(securattr, usid, gsid, isdir); + else + if (groupowns) + perm = build_owngrp_permissions(securattr, usid, isdir); + else + perm = build_std_permissions(securattr, usid, gsid, isdir); + return (perm); +} + +/* + * The following must be in some library... + */ + +static unsigned long atoul(const char *p) +{ /* must be somewhere ! */ + unsigned long v; + + v = 0; + while ((*p >= '0') && (*p <= '9')) + v = v * 10 + (*p++) - '0'; + return (v); +} + +/* + * Build an internal representation of a SID + * Returns a copy in allocated memory if it succeeds + * The SID is checked to be a valid user one. + */ + +static SID *encodesid(const char *sidstr) +{ + SID *sid; + int cnt; + BIGSID bigsid; + SID *bsid; + u32 auth; + const char *p; + + sid = (SID*) NULL; + if (!strncmp(sidstr, "S-1-", 4)) { + bsid = (SID*)&bigsid; + bsid->revision = SID_REVISION; + p = &sidstr[4]; + auth = atoul(p); + bsid->identifier_authority.high_part = const_cpu_to_be16(0); + bsid->identifier_authority.low_part = cpu_to_be32(auth); + cnt = 0; + p = strchr(p, '-'); + while (p && (cnt < 8)) { + p++; + auth = atoul(p); + bsid->sub_authority[cnt] = cpu_to_le32(auth); + p = strchr(p, '-'); + cnt++; + } + bsid->sub_authority_count = cnt; + if ((cnt > 0) && ntfs_valid_sid(bsid) && ntfs_is_user_sid(bsid)) { + sid = (SID*) ntfs_malloc(4 * cnt + 8); + if (sid) + memcpy(sid, bsid, 4 * cnt + 8); + } + } + return (sid); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + */ + +static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *q; + char *pu; + char *pg; + int gotend; + struct MAPLIST *item; + + src = *psrc; + dst = 0; + /* allocate and get a full line */ + item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST)); + if (item) { + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + if (dst < LINESZ) + item->maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + item->maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((item->maptext[0] == '#') || !gotend)); + if (gotend) { + pu = pg = (char*)NULL; + /* decompose into uid, gid and sid */ + item->uidstr = item->maptext; + item->gidstr = strchr(item->uidstr, ':'); + if (item->gidstr) { + pu = item->gidstr++; + item->sidstr = strchr(item->gidstr, ':'); + if (item->sidstr) { + pg = item->sidstr++; + q = strchr(item->sidstr, ':'); + if (q) *q = 0; + } + } + if (pu && pg) + *pu = *pg = '\0'; + else { + ntfs_log_early_error("Bad mapping item \"%s\"\n", + item->maptext); + free(item); + item = (struct MAPLIST*)NULL; + } + } else { + free(item); /* free unused item */ + item = (struct MAPLIST*)NULL; + } + } + *psrc = src; + return (item); +} + +/* + * Read user mapping file and split into their attribute. + * Parameters are kept as text in a chained list until logins + * are converted to uid. + * Returns the head of list, if any + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid) +{ + char buf[BUFSZ]; + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPLIST *lastitem; + int src; + off_t offs; + s64 size; + + firstitem = (struct MAPLIST*)NULL; + lastitem = (struct MAPLIST*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + item->next = (struct MAPLIST*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } while (item); + } + return (firstitem); +} + +/* + * Free memory used to store the user mapping + * The only purpose is to facilitate the detection of memory leaks + */ + +void ntfs_free_mapping(struct MAPPING *mapping[]) +{ + struct MAPPING *user; + struct MAPPING *group; + + /* free user mappings */ + while (mapping[MAPUSERS]) { + user = mapping[MAPUSERS]; + /* do not free SIDs used for group mappings */ + group = mapping[MAPGROUPS]; + while (group && (group->sid != user->sid)) + group = group->next; + if (!group) + free(user->sid); + /* free group list if any */ + if (user->grcnt) + free(user->groups); + /* unchain item and free */ + mapping[MAPUSERS] = user->next; + free(user); + } + /* free group mappings */ + while (mapping[MAPGROUPS]) { + group = mapping[MAPGROUPS]; + free(group->sid); + /* unchain item and free */ + mapping[MAPGROUPS] = group->next; + free(group); + } +} + + +/* + * Build the user mapping list + * user identification may be given in symbolic or numeric format + * + * ! Note ! : does getpwnam() read /etc/passwd or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct passwd *pwd; + SID *sid; + int uid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (item = firstitem; item; item = item->next) { + if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9')) + uid = atoi(item->uidstr); + else { + uid = 0; + if (item->uidstr[0]) { + pwd = getpwnam(item->uidstr); + if (pwd) + uid = pwd->pw_uid; + else + ntfs_log_early_error("Invalid user \"%s\"\n", + item->uidstr); + } + } + /* + * Records with no uid and no gid are inserted + * to define the implicit mapping pattern + */ + if (uid + || (!item->uidstr[0] && !item->gidstr[0])) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + ntfs_log_error("Bad implicit SID pattern %s\n", + item->sidstr); + sid = (SID*)NULL; + } + if (sid) { + mapping = + (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = uid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + return (firstmapping); +} + +/* + * Build the group mapping list + * group identification may be given in symbolic or numeric format + * + * gid not associated to a uid are processed first in order + * to favour real groups + * + * ! Note ! : does getgrnam() read /etc/group or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct group *grp; + BOOL secondstep; + BOOL ok; + int step; + SID *sid; + int gid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (step=1; step<=2; step++) { + for (item = firstitem; item; item = item->next) { + secondstep = (item->uidstr[0] != '\0') + || !item->gidstr[0]; + ok = (step == 1 ? !secondstep : secondstep); + if ((item->gidstr[0] >= '0') + && (item->gidstr[0] <= '9')) + gid = atoi(item->gidstr); + else { + gid = 0; + if (item->gidstr[0]) { + grp = getgrnam(item->gidstr); + if (grp) + gid = grp->gr_gid; + else + ntfs_log_early_error("Invalid group \"%s\"\n", + item->gidstr); + } + } + /* + * Records with no uid and no gid are inserted in the + * second step to define the implicit mapping pattern + */ + if (ok + && (gid + || (!item->uidstr[0] && !item->gidstr[0]))) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + /* error already logged */ + sid = (SID*)NULL; + } + if (sid) { + mapping = (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = gid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + } + return (firstmapping); +} diff --git a/lib/libntfs/src/source/acls.h b/lib/libntfs/src/source/acls.h new file mode 100644 index 0000000..8a83d32 --- /dev/null +++ b/lib/libntfs/src/source/acls.h @@ -0,0 +1,199 @@ +/* + * + * Copyright (c) 2007-2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ACLS_H +#define ACLS_H + +/* + * JPA configuration modes for security.c / acls.c + * should be moved to some config file + */ + +#define BUFSZ 1024 /* buffer size to read mapping file */ +#define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ +#define LINESZ 120 /* maximum useful size of a mapping line */ +#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */ +#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */ + +/* + * JPA The following must be in some library... + * but did not found out where + */ + +#define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8)) +#define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \ + | ((x & 0xff00) << 8) | ((x & 255) << 24)) + +#define cpu_to_be16(x) endian_rev16(cpu_to_le16(x)) +#define cpu_to_be32(x) endian_rev32(cpu_to_le32(x)) + +/* + * Macro definitions needed to share code with secaudit + */ + +#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) +#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) +#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) +#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) + + +/* + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present + */ + + /* flags which are set to mean exec, write or read */ + +#define FILE_READ (FILE_READ_DATA) +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_EXEC (FILE_EXECUTE) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define DIR_EXEC (FILE_TRAVERSE) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ + +#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ) +#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE) +#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE) +#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ) +#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE) +#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE) + + /* standard owner (and administrator) rights */ + +#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) + + /* standard world rights */ + +#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \ + | SYNCHRONIZE) + + /* inheritance flags for files and directories */ + +#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE +#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) + +/* + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. + */ + +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ + +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + +/* + * Struct to hold the input mapping file + * (private to this module) + */ + +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; +}; + +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); + +/* + * Constants defined in acls.c + */ + +extern const SID *adminsid; +extern const SID *worldsid; + +/* + * Functions defined in acls.c + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_valid_sid(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); + +BOOL ntfs_is_user_sid(const SID *usid); + + +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); + +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); + +#if POSIXACLS + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t umask, BOOL isdir); +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt); +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); + +#endif /* POSIXACLS */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); + +#endif /* ACLS_H */ + diff --git a/lib/libntfs/src/source/attrib.c b/lib/libntfs/src/source/attrib.c new file mode 100644 index 0000000..281a620 --- /dev/null +++ b/lib/libntfs/src/source/attrib.c @@ -0,0 +1,6794 @@ +/** + * attrib.c - Attribute handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2010 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2011 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#include "param.h" +#include "compat.h" +#include "attrib.h" +#include "attrlist.h" +#include "device.h" +#include "mft.h" +#include "debug.h" +#include "mst.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "dir.h" +#include "compress.h" +#include "bitmap.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; +ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('S'), + const_cpu_to_le16('\0') }; + +ntfschar TXF_DATA[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('T'), + const_cpu_to_le16('X'), + const_cpu_to_le16('F'), + const_cpu_to_le16('_'), + const_cpu_to_le16('D'), + const_cpu_to_le16('A'), + const_cpu_to_le16('T'), + const_cpu_to_le16('A'), + const_cpu_to_le16('\0') }; + +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; +} + +static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags |= flag; + else + ntfs_log_trace("Denied setting flag %d for not unnamed data " + "attribute\n", flag); +} + +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags &= ~flag; +} + +#define GenNAttrIno(func_name, flag) \ +int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ +void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ +void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) + +/** + * ntfs_get_attribute_value_length - Find the length of an attribute + * @a: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) +{ + if (!a) { + errno = EINVAL; + return 0; + } + errno = 0; + if (a->non_resident) + return sle64_to_cpu(a->data_size); + + return (s64)le32_to_cpu(a->value_length); +} + +/** + * ntfs_get_attribute_value - Get a copy of an attribute + * @vol: + * @a: + * @b: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b) +{ + runlist *rl; + s64 total, r; + int i; + + /* Sanity checks. */ + if (!vol || !a || !b) { + errno = EINVAL; + return 0; + } + /* Complex attribute? */ + /* + * Ignore the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (a->type != AT_ATTRIBUTE_LIST && a->flags) { + ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " + "this yet.\n", le16_to_cpu(a->flags)); + errno = EOPNOTSUPP; + return 0; + } + if (!a->non_resident) { + /* Attribute is resident. */ + + /* Sanity check. */ + if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) + > le32_to_cpu(a->length)) { + return 0; + } + + memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); + errno = 0; + return (s64)le32_to_cpu(a->value_length); + } + + /* Attribute is not resident. */ + + /* If no data, return 0. */ + if (!(a->data_size)) { + errno = 0; + return 0; + } + /* + * FIXME: What about attribute lists?!? (AIA) + */ + /* Decompress the mapping pairs array into a runlist. */ + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (!rl) { + errno = EINVAL; + return 0; + } + /* + * FIXED: We were overflowing here in a nasty fashion when we + * reach the last cluster in the runlist as the buffer will + * only be big enough to hold data_size bytes while we are + * reading in allocated_size bytes which is usually larger + * than data_size, since the actual data is unlikely to have a + * size equal to a multiple of the cluster size! + * FIXED2: We were also overflowing here in the same fashion + * when the data_size was more than one run smaller than the + * allocated size which happens with Windows XP sometimes. + */ + /* Now load all clusters in the runlist into b. */ + for (i = 0, total = 0; rl[i].length; i++) { + if (total + (rl[i].length << vol->cluster_size_bits) >= + sle64_to_cpu(a->data_size)) { + unsigned char *intbuf = NULL; + /* + * We have reached the last run so we were going to + * overflow when executing the ntfs_pread() which is + * BAAAAAAAD! + * Temporary fix: + * Allocate a new buffer with size: + * rl[i].length << vol->cluster_size_bits, do the + * read into our buffer, then memcpy the correct + * amount of data into the caller supplied buffer, + * free our buffer, and continue. + * We have reached the end of data size so we were + * going to overflow in the same fashion. + * Temporary fix: same as above. + */ + intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits); + if (!intbuf) { + free(rl); + return 0; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we + * just memset the user buffer to 0 for the length of + * the run, which should be 16 (= compression unit + * size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily + * size of 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << + vol->cluster_size_bits, rl[i].length << + vol->cluster_size_bits, intbuf); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << + vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + free(intbuf); + return 0; + } + memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - + total); + free(intbuf); + total = sle64_to_cpu(a->data_size); + break; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we just + * memset the user buffer to 0 for the length of the run, which + * should be 16 (= compression unit size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily size of + * 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, + rl[i].length << vol->cluster_size_bits, + b + total); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + return 0; + } + total += r; + } + free(rl); + return total; +} + +/* Already cleaned up code below, but still look for FIXME:... */ + +/** + * __ntfs_attr_init - primary initialization of an ntfs attribute structure + * @na: ntfs attribute to initialize + * @ni: ntfs inode with which to initialize the ntfs attribute + * @type: attribute type + * @name: attribute name in little endian Unicode or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. + */ +static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, + const ATTR_TYPES type, ntfschar *name, const u32 name_len) +{ + na->rl = NULL; + na->ni = ni; + na->type = type; + na->name = name; + if (name) + na->name_len = name_len; + else + na->name_len = 0; +} + +/** + * ntfs_attr_init - initialize an ntfs_attr with data sizes and status + * @na: + * @non_resident: + * @compressed: + * @encrypted: + * @sparse: + * @allocated_size: + * @data_size: + * @initialized_size: + * @compressed_size: + * @compression_unit: + * + * Final initialization for an ntfs attribute. + */ +void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, + const BOOL encrypted, const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit) +{ + if (!NAttrInitialized(na)) { + na->data_flags = data_flags; + if (non_resident) + NAttrSetNonResident(na); + if (data_flags & ATTR_COMPRESSION_MASK) + NAttrSetCompressed(na); + if (encrypted) + NAttrSetEncrypted(na); + if (sparse) + NAttrSetSparse(na); + na->allocated_size = allocated_size; + na->data_size = data_size; + na->initialized_size = initialized_size; + if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) { + ntfs_volume *vol = na->ni->vol; + + na->compressed_size = compressed_size; + na->compression_block_clusters = 1 << compression_unit; + na->compression_block_size = 1 << (compression_unit + + vol->cluster_size_bits); + na->compression_block_size_bits = ffs( + na->compression_block_size) - 1; + } + NAttrSetInitialized(na); + } +} + +/** + * ntfs_attr_open - open an ntfs attribute for access + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Allocate a new ntfs attribute structure, initialize it with @ni, @type, + * @name, and @name_len, then return it. Return NULL on error with + * errno set to the error code. + * + * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you + * do not care whether the attribute is named or not set @name to NULL. In + * both those cases @name_len is not used at all. + */ +ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na = NULL; + ntfschar *newname = NULL; + ATTR_RECORD *a; + le16 cs; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + + if (!ni || !ni->vol || !ni->mrec) { + errno = EINVAL; + goto out; + } + na = ntfs_calloc(sizeof(ntfs_attr)); + if (!na) + goto out; + if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { + name = ntfs_ucsndup(name, name_len); + if (!name) + goto err_out; + newname = name; + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) + goto put_err_out; + + a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + newname = name; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + + /* + * Wipe the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (type == AT_ATTRIBUTE_LIST) + a->flags = 0; + + if ((type == AT_DATA) && !a->initialized_size) { + /* + * Define/redefine the compression state if stream is + * empty, based on the compression mark on parent + * directory (for unnamed data streams) or on current + * inode (for named data streams). The compression mark + * may change any time, the compression state can only + * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled + */ + a->flags &= ~ATTR_COMPRESSION_MASK; + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) + a->flags |= ATTR_IS_COMPRESSED; + } + + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + + /* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */ + if (na->type == AT_DATA && na->name == AT_UNNAMED && + (((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { + errno = EIO; + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); + goto put_err_out; + } + + if (a->non_resident) { + if ((a->flags & ATTR_COMPRESSION_MASK) + && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } + ntfs_attr_init(na, TRUE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, + sle64_to_cpu(a->allocated_size), + sle64_to_cpu(a->data_size), + sle64_to_cpu(a->initialized_size), + cs ? sle64_to_cpu(a->compressed_size) : 0, + cs ? a->compression_unit : 0); + } else { + s64 l = le32_to_cpu(a->value_length); + ntfs_attr_init(na, FALSE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, + cs ? (l + 7) & ~7 : 0, 0); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return na; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(newname); + free(na); + na = NULL; + goto out; +} + +/** + * ntfs_attr_close - free an ntfs attribute structure + * @na: ntfs attribute structure to free + * + * Release all memory associated with the ntfs attribute @na and then release + * @na itself. + */ +void ntfs_attr_close(ntfs_attr *na) +{ + if (!na) + return; + if (NAttrNonResident(na) && na->rl) + free(na->rl); + /* Don't release if using an internal constant. */ + if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30 + && na->name != STREAM_SDS) + free(na->name); + free(na); +} + +/** + * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute + * @na: ntfs attribute for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * + * Map the part of a runlist containing the @vcn of the ntfs attribute @na. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + ntfs_attr_search_ctx *ctx; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + vcn, NULL, 0, ctx)) { + runlist_element *rl; + + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, + na->rl); + if (rl) { + na->rl = rl; + ntfs_attr_put_search_ctx(ctx); + return 0; + } + } + + ntfs_attr_put_search_ctx(ctx); + return -1; +} + +#if PARTIAL_RUNLIST_UPDATING + +/* + * Map the runlist of an attribute from some point to the end + * + * Returns 0 if success, + * -1 if it failed (errno telling why) + */ + +static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + VCN last_vcn; + VCN highest_vcn; + VCN needed; + VCN existing_vcn; + runlist_element *rl; + ATTR_RECORD *a; + BOOL startseen; + ntfs_attr_search_ctx *ctx; + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + existing_vcn = (na->rl ? na->rl->vcn : -1); + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Get the last vcn in the attribute. */ + last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits; + + needed = vcn; + highest_vcn = 0; + startseen = FALSE; + do { + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + needed, NULL, 0, ctx)) { + + a = ctx->attr; + /* Decode and merge the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, a, + na->rl); + if (rl) { + na->rl = rl; + highest_vcn = le64_to_cpu(a->highest_vcn); + /* corruption detection */ + if (((highest_vcn + 1) < last_vcn) + && ((highest_vcn + 1) <= needed)) { + ntfs_log_error("Corrupt attribute list\n"); + rl = (runlist_element*)NULL; + } + needed = highest_vcn + 1; + if (!a->lowest_vcn) + startseen = TRUE; + /* reaching a previously allocated part ? */ + if ((existing_vcn >= 0) + && (needed >= existing_vcn)) { + needed = last_vcn; + } + } + } else + rl = (runlist_element*)NULL; + } while (rl && (needed < last_vcn)); + ntfs_attr_put_search_ctx(ctx); + /* mark fully mapped if we did so */ + if (rl && startseen) + NAttrSetFullyMapped(na); + return (rl ? 0 : -1); +} + +#endif + +/** + * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute + * @na: ntfs attribute for which to map the runlist + * + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling + * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this + * will map the runlist fragments from each of the extents thus giving access + * to the entirety of the disk allocation of an attribute. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_whole_runlist(ntfs_attr *na) +{ + VCN next_vcn, last_vcn, highest_vcn; + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol = na->ni->vol; + ATTR_RECORD *a; + int ret = -1; + + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); + + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto out; + + /* Map all attribute extents one by one. */ + next_vcn = last_vcn = highest_vcn = 0; + a = NULL; + while (1) { + runlist_element *rl; + + int not_mapped = 0; + if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) + not_mapped = 1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) + break; + + a = ctx->attr; + + if (not_mapped) { + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, + a, na->rl); + if (!rl) + goto err_out; + na->rl = rl; + } + + /* Are we in the first extent? */ + if (!next_vcn) { + if (a->lowest_vcn) { + errno = EIO; + ntfs_log_perror("First extent of inode %llu " + "attribute has non-zero lowest_vcn", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + /* Get the last vcn in the attribute. */ + last_vcn = sle64_to_cpu(a->allocated_size) >> + vol->cluster_size_bits; + } + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) { + errno = ENOENT; + break; + } + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + errno = EIO; + ntfs_log_perror("Inode %llu has corrupt attribute list", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + } + if (!a) { + ntfs_log_perror("Couldn't find attribute for runlist mapping"); + goto err_out; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + errno = EIO; + ntfs_log_perror("Failed to load full runlist: inode: %llu " + "highest_vcn: 0x%llx last_vcn: 0x%llx", + (unsigned long long)na->ni->mft_no, + (long long)highest_vcn, (long long)last_vcn); + goto err_out; + } + if (errno == ENOENT) { + NAttrSetFullyMapped(na); + ret = 0; + } +err_out: + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute + * @na: ntfs attribute whose runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @na->rl to map vcns to + * their corresponding lcns. + * + * If the @vcn is not mapped yet, attempt to map the attribute extent + * containing the @vcn and retry the vcn to lcn conversion. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ========================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. + */ +LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) +{ + LCN lcn; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) + return (LCN)LCN_EINVAL; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); +retry: + /* Convert vcn to lcn. If that fails map the runlist and retry once. */ + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0) + return lcn; + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If the attempt to map the runlist failed, or we are getting + * LCN_RL_NOT_MAPPED despite having mapped the attribute extent + * successfully, something is really badly wrong... + */ + if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) + return (LCN)LCN_EIO; + /* lcn contains the appropriate error code. */ + return lcn; +} + +/** + * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute + * @na: ntfs attribute whose runlist to search + * @vcn: vcn to find + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * @na and return the the address of the runlist element containing the @vcn. + * + * Note you need to distinguish between the lcn of the returned runlist + * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes + * on read and allocate clusters on write. You need to update the runlist, the + * attribute itself as well as write the modified mft record to disk. + * + * If there is an error return NULL with errno set to the error code. The + * following error codes are defined: + * EINVAL Input parameter error. + * ENOENT There is no such vcn in the runlist. + * ENOMEM Not enough memory. + * EIO I/O error or corrupt metadata. + */ +runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) +{ + runlist_element *rl; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)vcn); +retry: + rl = na->rl; + if (!rl) + goto map_rl; + if (vcn < rl[0].vcn) + goto map_rl; + while (rl->length) { + if (vcn < rl[1].vcn) { + if (rl->lcn >= (LCN)LCN_HOLE) + return rl; + break; + } + rl++; + } + switch (rl->lcn) { + case (LCN)LCN_RL_NOT_MAPPED: + goto map_rl; + case (LCN)LCN_ENOENT: + errno = ENOENT; + break; + case (LCN)LCN_EINVAL: + errno = EINVAL; + break; + default: + errno = EIO; + break; + } + return NULL; +map_rl: + /* The @vcn is in an unmapped region, map the runlist and retry. */ + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If we already retried or the mapping attempt failed something has + * gone badly wrong. EINVAL and ENOENT coming from a failed mapping + * attempt are equivalent to errors for us as they should not happen + * in our code paths. + */ + if (is_retry || errno == EINVAL || errno == ENOENT) + errno = EIO; + return NULL; +} + +/** + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2, max_read, max_init; + ntfs_volume *vol; + runlist_element *rl; + u16 efs_padding_length; + + /* Sanity checking arguments is done in ntfs_attr_pread(). */ + + if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) { + if ((na->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + return ntfs_compressed_attr_pread(na, pos, count, b); + else { + /* compression mode not supported */ + errno = EOPNOTSUPP; + return -1; + } + } + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option + */ + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + return -1; + } + + if (!count) + return 0; + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + return -1; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) + return 0; + count = max_read - pos; + } + /* If it is a resident attribute, get the value from the mft record. */ + if (!NAttrNonResident(na)) { + ntfs_attr_search_ctx *ctx; + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { +res_err_out: + ntfs_attr_put_search_ctx(ctx); + return -1; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto res_err_out; + } + memcpy(b, val + pos, count); + ntfs_attr_put_search_ctx(ctx); + return count; + } + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > max_init) { + if (pos >= max_init) { + memset(b, 0, count); + return count; + } + total2 = pos + count - max_init; + count -= total2; + memset((u8*)b + count, 0, total2); + } + /* + * for encrypted non-resident attributes with efs_raw set + * the last two bytes aren't read from disk but contain + * the number of padding bytes so original size can be + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + } + + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds read. + * However, we already truncated the read to the data_size, + * so getting this here is an error. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } + return -1; + } + /* + * Gather the requested data into the linear destination buffer. Note, + * a partial final vcn is taken care of by the @count capping of read + * length. + */ + ofs = pos - (rl->vcn << vol->cluster_size_bits); + for (; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #2", + __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + /* It is a hole, just zero the matching @b range. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update progress counters. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it into @dst. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); + br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (br > 0) { + total += br; + count -= br; + b = (u8*)b + br; + } + if (br == to_read) + continue; + /* If the syscall was interrupted, try again. */ + if (br == (s64)-1 && errno == EINTR) + goto retry; + if (total) + return total; + if (!br) + errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); + return -1; + } + /* Finally, return the number of bytes read. */ + return total + total2; +rl_err_out: + if (total) + return total; + errno = EIO; + return -1; +} + +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) +{ + char *buf; + s64 written, size, end = pos + count; + s64 ofsi; + const runlist_element *rli; + ntfs_volume *vol; + int ret = -1; + + ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, + (long long)count); + + if (!na || pos < 0 || count < 0) { + errno = EINVAL; + goto err_out; + } + + buf = ntfs_calloc(NTFS_BUF_SIZE); + if (!buf) + goto err_out; + + rli = na->rl; + ofsi = 0; + vol = na->ni->vol; + while (pos < end) { + while (rli->length && (ofsi + (rli->length << + vol->cluster_size_bits) <= pos)) { + ofsi += (rli->length << vol->cluster_size_bits); + rli++; + } + size = min(end - pos, NTFS_BUF_SIZE); + /* + * If the zeroed block is fully within a hole, + * we need not write anything, so advance as far + * as possible within the hole. + */ + if ((rli->lcn == (LCN)LCN_HOLE) + && (ofsi <= pos) + && (ofsi + (rli->length << vol->cluster_size_bits) + >= (pos + size))) { + size = min(end - pos, ofsi - pos + + (rli->length << vol->cluster_size_bits)); + pos += size; + } else { + written = ntfs_rl_pwrite(vol, rli, ofsi, pos, + size, buf); + if (written <= 0) { + ntfs_log_perror("Failed to zero space"); + goto err_free; + } + pos += written; + } + } + + ret = 0; +err_free: + free(buf); +err_out: + return ret; +} + +static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, + runlist_element **rl, VCN *update_from) +{ + s64 to_write; + s64 need; + ntfs_volume *vol = na->ni->vol; + int eo, ret = -1; + runlist *rlc; + LCN lcn_seek_from = -1; + VCN cur_vcn, from_vcn; + + to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); + + cur_vcn = (*rl)->vcn; + from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); + + ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " + "%lld\n", (long long)count, (long long)cur_vcn, + (long long)from_vcn, (long long)to_write, (long long)*ofs); + + /* Map the runlist to be able to update mapping pairs later. */ +#if PARTIAL_RUNLIST_UPDATING + if ((!na->rl + || !NAttrDataAppending(na))) { + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + } else { + /* make sure the previous non-hole is mapped */ + rlc = *rl; + rlc--; + if (((*rl)->lcn == LCN_HOLE) + && cur_vcn + && (rlc->vcn < 0)) { + if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1)) + goto err_out; + } + } +#else + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; +#endif + + /* Restore @*rl, it probably get lost during runlist mapping. */ + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + ntfs_log_error("Failed to find run after mapping runlist. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + + /* Search backwards to find the best lcn to start seek from. */ + rlc = *rl; + while (rlc->vcn) { + rlc--; + if (rlc->lcn >= 0) { + /* + * avoid fragmenting a compressed file + * Windows does not do that, and that may + * not be desirable for files which can + * be updated + */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + lcn_seek_from = rlc->lcn + rlc->length; + else + lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); + break; + } + } + if (lcn_seek_from == -1) { + /* Backwards search failed, search forwards. */ + rlc = *rl; + while (rlc->length) { + rlc++; + if (rlc->lcn >= 0) { + lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); + if (lcn_seek_from < -1) + lcn_seek_from = -1; + break; + } + } + } + + need = ((*ofs + to_write - 1) >> vol->cluster_size_bits) + + 1 + (*rl)->vcn - from_vcn; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && (need < na->compression_block_clusters)) { + /* + * for a compressed file, be sure to allocate the full + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. + */ + VCN alloc_vcn; + + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, + lcn_seek_from, DATA_ZONE); + } else + rlc = ntfs_cluster_alloc(vol, from_vcn, need, + lcn_seek_from, DATA_ZONE); + if (!rlc) + goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; + + *rl = ntfs_runlists_merge(na->rl, rlc); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } + if (!*rl) { + eo = errno; + ntfs_log_perror("Failed to merge runlists"); + if (ntfs_cluster_free_from_rl(vol, rlc)) { + ntfs_log_perror("Failed to free hot clusters. " + "Please run chkdsk /f"); + } + errno = eo; + goto err_out; + } + na->unused_runs = 2; + na->rl = *rl; + if ((*update_from == -1) || (from_vcn < *update_from)) + *update_from = from_vcn; + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + /* + * It's definitely a BUG, if we failed to find @cur_vcn, because + * we missed it during instantiating of the hole. + */ + ntfs_log_error("Failed to find run after hole instantiation. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + /* If leaved part of the hole go to the next run. */ + if ((*rl)->lcn < 0) + (*rl)++; + /* Now LCN shoudn't be less than 0. */ + if ((*rl)->lcn < 0) { + ntfs_log_error("BUG! LCN is lesser than 0. " + "Please report to the %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + if (*ofs) { + /* Clear non-sparse region from @cur_vcn to @*ofs. */ + if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits, + *ofs)) + goto err_out; + } + if ((*rl)->vcn < cur_vcn) { + /* + * Clusters that replaced hole are merged with + * previous run, so we need to update offset. + */ + *ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits; + } + if ((*rl)->vcn > cur_vcn) { + /* + * We left part of the hole, so we need to update offset + */ + *ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits; + } + + ret = 0; +err_out: + return ret; +} + +static int stuff_hole(ntfs_attr *na, const s64 pos); + +/* + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block + * + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error + */ + +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) +{ + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; + + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ +// impossible ? + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; + + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); +} + +/* + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block + * + * Must always leave two unused entries in the runlist + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error + */ + +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) +{ + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; + + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); + + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; +#if PARTIAL_RUNLIST_UPDATING + VCN prevblock; +#endif + /* + * Map the runlist, unless it has not been created. + * If appending data, a partial mapping from the + * end of previous block will do. + */ + irl = *prl - na->rl; +#if PARTIAL_RUNLIST_UPDATING + prevblock = pos >> cluster_size_bits; + if (prevblock) + prevblock--; + if (!NAttrBeingNonResident(na) + && (NAttrDataAppending(na) + ? ntfs_attr_map_partial_runlist(na,prevblock) + : ntfs_attr_map_whole_runlist(na))) { +#else + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { +#endif + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; + + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; + + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); +} + +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes); + +/** + * ntfs_attr_pwrite - positioned write to an ntfs attribute + * @na: ntfs attribute to write to + * @pos: position in the attribute to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to ntfs attribute + * @na at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also return + * 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) +{ + s64 written, to_write, ofs, old_initialized_size, old_data_size; + s64 total = 0; + VCN update_from = -1; + ntfs_volume *vol; + s64 fullcount; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + s64 hole_end; + int eo; + int compressed_part; + struct { + unsigned int undo_initialized_size : 1; + unsigned int undo_data_size : 1; + } need_to = { 0, 0 }; + BOOL wasnonresident = FALSE; + BOOL compressed; + BOOL updatemap; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + na->unused_runs = 0; /* prepare overflow checks */ + /* + * Encrypted attributes are only supported in raw mode. We return + * access denied, which is what Windows NT4 does, too. + * Moreover a file cannot be both encrypted and compressed. + */ + if ((na->data_flags & ATTR_IS_ENCRYPTED) + && (compressed || !vol->efs_raw)) { + errno = EACCES; + goto errno_set; + } + /* + * Fill the gap, when writing beyond the end of a compressed + * file. This will make recursive calls + */ + if (compressed + && (na->type == AT_DATA) + && (pos > na->initialized_size) + && stuff_hole(na,pos)) + goto errno_set; + /* If this is a compressed attribute it needs special treatment. */ + wasnonresident = NAttrNonResident(na) != 0; + /* + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. + */ + if (compressed + && ((na->type != AT_DATA) + || ((na->data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED))) { + errno = EOPNOTSUPP; + goto errno_set; + } + + if (!count) + goto out; + /* for a compressed file, get prepared to reserve a full block */ + fullcount = count; + /* If the write reaches beyond the end, extend the attribute. */ + old_data_size = na->data_size; + /* identify whether this is appending to a non resident data attribute */ + if ((na->type == AT_DATA) && (pos >= old_data_size) + && NAttrNonResident(na)) + NAttrSetDataAppending(na); + if (pos + count > na->data_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending data, the attribute is first extended + * before being filled with data. This may cause the + * attribute to be made temporarily sparse, which + * implies reformating the inode and reorganizing the + * full runlist. To avoid unnecessary reorganization, + * we avoid sparse testing until the data is filled in. + */ + if (ntfs_attr_truncate_i(na, pos + count, + (NAttrDataAppending(na) ? + HOLES_DELAY : HOLES_OK))) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#else + if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#endif + /* resizing may change the compression mode */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + need_to.undo_data_size = 1; + } + /* + * For compressed data, a single full block was allocated + * to deal with compression, possibly in a previous call. + * We are not able to process several blocks because + * some clusters are freed after compression and + * new allocations have to be done before proceeding, + * so truncate the requested count if needed (big buffers). + */ + if (compressed) { + fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; + if (count > fullcount) + count = fullcount; + } + old_initialized_size = na->initialized_size; + /* If it is a resident attribute, write the data to the mft record. */ + if (!NAttrNonResident(na)) { + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: lookup failed", __FUNCTION__); + goto err_out; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto err_out; + } + memcpy(val + pos, b, count); + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * NOTE: We are in a bad state at this moment. We have + * dirtied the mft record but we failed to commit it to + * disk. Since we have read the mft record ok before, + * it is unlikely to fail writing it, so is ok to just + * return error here... (AIA) + */ + ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + total = count; + goto out; + } + + /* Handle writes beyond initialized_size. */ + + if (pos + count > na->initialized_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending, we only need to map the end of the runlist, + * starting at the last previously allocated run, so that + * we are able a new one to it. + * However, for compressed file, we need the full compression + * block, which may be split in several extents. + */ + if (NAttrDataAppending(na)) { + VCN block_begin = pos >> vol->cluster_size_bits; + + if (compressed) + block_begin &= -na->compression_block_clusters; + if (block_begin) + block_begin--; + if (ntfs_attr_map_partial_runlist(na, block_begin)) + goto err_out; + if ((update_from == -1) || (block_begin < update_from)) + update_from = block_begin; + } else +#endif + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + /* + * For a compressed attribute, we must be sure there is an + * available entry, and, when reopening a compressed file, + * we may need to split a hole. So reserve the entries + * before it gets too late. + */ + if (compressed) { + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + } + /* Set initialized_size to @pos + @count. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) + goto err_out; + + /* If write starts beyond initialized_size, zero the gap. */ + if (pos > na->initialized_size) + if (ntfs_attr_fill_zero(na, na->initialized_size, + pos - na->initialized_size)) + goto err_out; + + ctx->attr->initialized_size = cpu_to_sle64(pos + count); + /* fix data_size for compressed files */ + if (compressed) { + na->data_size = pos + count; + ctx->attr->data_size = ctx->attr->initialized_size; + } + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * Undo the change in the in-memory copy and send it + * back for writing. + */ + ctx->attr->initialized_size = + cpu_to_sle64(old_initialized_size); + ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec); + goto err_out; + } + na->initialized_size = pos + count; +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* + * NOTE: At this point the initialized_size in the mft record + * has been updated BUT there is random data on disk thus if + * we decide to abort, we MUST change the initialized_size + * again. + */ + need_to.undo_initialized_size = 1; + } + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we already extended the size of the attribute, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__); + } + goto err_out; + } + /* + * Determine if there is compressed data in the current + * compression block (when appending to an existing file). + * If so, decompression will be needed, and the full block + * must be allocated to be identified as uncompressed. + * This comes in two variants, depending on whether + * compression has saved at least one cluster. + * The compressed size can never be over full size by + * more than 485 (maximum for 15 compression blocks + * compressed to 4098 and the last 3640 bytes compressed + * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485) + * This is less than the smallest cluster, so the hole is + * is never beyond the cluster next to the position of + * the first uncompressed byte to write. + */ + compressed_part = 0; + if (compressed) { + if ((rl->lcn == (LCN)LCN_HOLE) + && wasnonresident) { + if (rl->length < na->compression_block_clusters) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ + compressed_part + = na->compression_block_clusters + - rl->length; + else { + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); + } + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } + + if (compressed_part < 0) + goto err_out; + + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; + } + ofs = pos - (rl->vcn << vol->cluster_size_bits); + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + for (hole_end = 0; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #4", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + hole_end = rl->vcn + rl->length; + + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl, + &update_from)) + goto err_out; + } + if (compressed) { + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + } + + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); +retry: + ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " + "%lld.\n", (long long)to_write, (long long)rl->vcn, + (long long)rl->lcn, (long long)ofs); + if (!NVolReadOnly(vol)) { + + s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; + s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; + u32 bsize = vol->cluster_size; + /* Byte size needed to zero fill a cluster */ + s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + /** + * Zero fill to cluster boundary if we're writing at the + * end of the attribute or into an ex-sparse cluster. + * This will cause the kernel not to seek and read disk + * blocks during write(2) to fill the end of the buffer + * which increases write speed by 2-10 fold typically. + * + * This is done even for compressed files, because + * data is generally first written uncompressed. + */ + if (rounding && ((wend == na->initialized_size) || + (wend < (hole_end << vol->cluster_size_bits)))){ + + char *cb; + + rounding += to_write; + + cb = ntfs_malloc(rounding); + if (!cb) + goto err_out; + + memcpy(cb, b, to_write); + memset(cb + to_write, 0, rounding - to_write); + + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + rounding, cb, compressed_part, + &update_from); + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounding, cb); + if (written == rounding) + written = to_write; + } + + free(cb); + } else { + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + to_write, b, compressed_part, + &update_from); + } else + written = ntfs_pwrite(vol->dev, wpos, + to_write, b); + } + } else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + fullcount -= written; + b = (const u8*)b + written; + } + if (written != to_write) { + /* Partial write cannot be dealt with, stop there */ + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (!written) + errno = EIO; + goto rl_err_out; + } + compressed_part = 0; + } +done: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ +#if PARTIAL_RUNLIST_UPDATING + updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na); +#else + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); +#endif + if (updatemap) { + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + total = -1; + goto out; + } + if (!wasnonresident) + NAttrClearBeingNonResident(na); + NAttrClearDataAppending(na); + } +out: + ntfs_log_leave("\n"); + return total; +rl_err_out: + eo = errno; + if (total) { + if (need_to.undo_initialized_size) { + if (pos + total > na->initialized_size) + goto done; + /* + * TODO: Need to try to change initialized_size. If it + * succeeds goto done, otherwise goto err_out. (AIA) + */ + goto err_out; + } + goto done; + } + errno = eo; +err_out: + eo = errno; + if (need_to.undo_initialized_size) { + int err; + + err = 0; + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + err = 1; + } else + ntfs_attr_reinit_search_ctx(ctx); + if (!err) { + err = ntfs_attr_lookup(na->type, na->name, + na->name_len, 0, 0, NULL, 0, ctx); + if (!err) { + na->initialized_size = old_initialized_size; + ctx->attr->initialized_size = cpu_to_sle64( + old_initialized_size); + err = ntfs_mft_record_write(vol, + ctx->ntfs_ino->mft_no, + ctx->mrec); + } + } + if (err) { + /* + * FIXME: At this stage could try to recover by filling + * old_initialized_size -> new_initialized_size with + * data or at least zeroes. (AIA) + */ + ntfs_log_error("Eeek! Failed to recover from error. " + "Leaving metadata in inconsistent " + "state! Run chkdsk!\n"); + } + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + ntfs_attr_update_mapping_pairs(na, 0); + /* Restore original data_size if needed. */ + if (need_to.undo_data_size + && ntfs_attr_truncate_i(na, old_data_size, HOLES_OK)) + ntfs_log_perror("Failed to restore data_size"); + errno = eo; +errno_set: + total = -1; + goto out; +} + +int ntfs_attr_pclose(ntfs_attr *na) +{ + s64 ofs; + int failed; + BOOL ok = TRUE; + VCN update_from = -1; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + int eo; + int compressed_part; + BOOL compressed; + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", + na->ni->mft_no, na->type); + + if (!na || !na->ni || !na->ni->vol) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + na->unused_runs = 0; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + goto errno_set; + } + /* If this is not a compressed attribute get out */ + /* same if it is resident */ + if (!compressed || !NAttrNonResident(na)) + goto out; + + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + /* Find the runlist element containing the terminal vcn. */ + rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we have already written the last byte uncompressed, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__); + } + goto err_out; + } + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + compressed_part = 0; + if (rl->lcn >= 0) { + runlist_element *xrl; + + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else + if (rl->lcn == (LCN)LCN_HOLE) { + if (rl->length < na->compression_block_clusters) + compressed_part + = na->compression_block_clusters + - rl->length; + else + compressed_part + = na->compression_block_clusters; + } + /* done, if the last block set was compressed */ + if (compressed_part) + goto out; + + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #6", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + + if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from)) + goto err_out; + } + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + +retry: + failed = 0; + if (update_from < 0) update_from = 0; + if (!NVolReadOnly(vol)) { + failed = ntfs_compressed_close(na, rl, ofs, &update_from); +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + } + if (failed) { + /* If the syscall was interrupted, try again. */ + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + ok = FALSE; + goto out; + } +out: + NAttrClearComprClosing(na); + ntfs_log_leave("\n"); + return (!ok); +rl_err_out: + /* + * need not restore old sizes, only compressed_size + * can have changed. It has been set according to + * the current runlist while updating the mapping pairs, + * and must be kept consistent with the runlists. + */ +err_out: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); + errno = eo; +errno_set: + ok = FALSE; + goto out; +} + +/** + * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read + * @na: multi sector transfer protected ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @bk_cnt: number of mst protected blocks to read + * @bk_size: size of each mst protected block in bytes + * @dst: output data buffer + * + * This function will read @bk_cnt blocks of size @bk_size bytes each starting + * at offset @pos from the ntfs attribute @na into the data buffer @b. + * + * On success, the multi sector transfer fixups are applied and the number of + * read blocks is returned. If this number is lower than @bk_cnt this means + * that the read has either reached end of attribute or that an error was + * encountered during the read so that the read is partial. 0 means end of + * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer is detected the magic is + * changed to BAAD but no error is returned, i.e. it is possible that any of + * the returned blocks have multi sector transfer errors. This should be + * detected by the caller by checking each block with is_baad_recordp(&block). + * The reasoning is that we want to fixup as many blocks as possible and we + * want to return even bad ones to the caller so, e.g. in case of ntfsck, the + * errors can be repaired. + */ +s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + const u32 bk_size, void *dst) +{ + s64 br; + u8 *end; + BOOL warn; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); + if (br <= 0) + return br; + br /= bk_size; + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); + for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + + bk_size) + ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); + /* Finally, return the number of blocks read. */ + return br; +} + +/** + * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write + * @na: multi sector transfer protected ntfs attribute to write to + * @pos: position in the attribute to write to + * @bk_cnt: number of mst protected blocks to write + * @bk_size: size of each mst protected block in bytes + * @src: data buffer to write to disk + * + * This function will write @bk_cnt blocks of size @bk_size bytes each from + * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na + * at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @bk_cnt this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also + * return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case + * of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, + const u32 bk_size, void *src) +{ + s64 written, i; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!bk_cnt) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < bk_cnt; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)src + i * bk_size), bk_size); + if (err < 0) { + /* Abort write at this position. */ + ntfs_log_perror("%s #1", __FUNCTION__); + if (!i) + return err; + bk_cnt = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); + if (written <= 0) { + ntfs_log_perror("%s: written=%lld", __FUNCTION__, + (long long)written); + } + /* Quickly deprotect the data again. */ + for (i = 0; i < bk_cnt; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * + bk_size)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bk_size; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfs_volume *vol; + ntfschar *upcase; + u32 upcase_len; + + ntfs_log_trace("attribute type 0x%x.\n", type); + + if (ctx->ntfs_ino) { + vol = ctx->ntfs_ino->vol; + upcase = vol->upcase; + upcase_len = vol->upcase_len; + } else { + if (name && name != AT_UNNAMED) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + vol = NULL; + upcase = NULL; + upcase_len = 0; + } + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else { + register int rc; + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) + return 0; + /* @val is present; compare values. */ + else { + register int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + register u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + errno = EIO; + ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, + ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); + return -1; +} + +void ntfs_attr_name_free(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) +{ + char *name = NULL; + int name_len; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + ntfs_log_perror("ntfs_ucstombs"); + return NULL; + + } else if (name_len > 0) + return name; + + ntfs_attr_name_free(&name); + return NULL; +} + +/** + * ntfs_external_attr_find - find an attribute in the attribute list of an inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * Find an attribute by searching the attribute list for the corresponding + * attribute list entry. Having found the entry, map the mft record for read + * if the attribute is in a different mft record/inode, find the attribute in + * there and return it. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to + * ENOENT to indicate that there are no more entries. During the enumeration, + * each successful call of ntfs_external_attr_find() will return the next + * attribute described by the attribute list of the base mft record described + * by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * On first search @ctx->ntfs_ino must be the inode of the base mft record and + * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). + * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too + * (@ctx->base_ntfs_ino is then the base inode). + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ntfs_volume *vol; + ATTR_LIST_ENTRY *al_entry, *next_al_entry; + u8 *al_start, *al_end; + ATTR_RECORD *a; + ntfschar *al_name; + u32 al_name_len; + BOOL is_first_search = FALSE; + + ni = ctx->ntfs_ino; + base_ni = ctx->base_ntfs_ino; + ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", + (unsigned long long)ni->mft_no, type); + if (!base_ni) { + /* First call happens with the base mft record. */ + base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; + ctx->base_mrec = ctx->mrec; + } + if (ni == base_ni) + ctx->base_attr = ctx->attr; + if (type == AT_END) + goto not_found; + vol = base_ni->vol; + al_start = base_ni->attr_list; + al_end = al_start + base_ni->attr_list_size; + if (!ctx->al_entry) { + ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; + is_first_search = TRUE; + } + /* + * Iterate over entries in attribute list starting at @ctx->al_entry, + * or the entry following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + al_entry = ctx->al_entry; + ctx->is_first = FALSE; + /* + * If an enumeration and the first attribute is higher than + * the attribute list itself, need to return the attribute list + * attribute. + */ + if ((type == AT_UNUSED) && is_first_search && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) + goto find_attr_list_attr; + } else { + al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + /* + * If this is an enumeration and the attribute list attribute + * is the next one in the enumeration sequence, just return the + * attribute list attribute from the base mft record as it is + * not listed in the attribute list itself. + */ + if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < + le32_to_cpu(AT_ATTRIBUTE_LIST) && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) { + int rc; +find_attr_list_attr: + + /* Check for bogus calls. */ + if (name || name_len || val || val_len || lowest_vcn) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + /* We want the base record. */ + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + + /* Find the attribute list attribute. */ + rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, + IGNORE_CASE, NULL, 0, ctx); + + /* + * Setup the search context so the correct + * attribute is returned next time round. + */ + ctx->al_entry = al_entry; + ctx->is_first = TRUE; + + /* Got it. Done. */ + if (!rc) + return 0; + + /* Error! If other than not found return it. */ + if (errno != ENOENT) + return rc; + + /* Not found?!? Absurd! */ + errno = EIO; + ntfs_log_error("Attribute list wasn't found"); + return -1; + } + } + for (;; al_entry = next_al_entry) { + /* Out of bounds check. */ + if ((u8*)al_entry < base_ni->attr_list || + (u8*)al_entry > al_end) + break; /* Inode is corrupt. */ + ctx->al_entry = al_entry; + /* Catch the end of the attribute list. */ + if ((u8*)al_entry == al_end) + goto not_found; + if (!al_entry->length) + break; + if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + + le16_to_cpu(al_entry->length) > al_end) + break; + next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + + le16_to_cpu(al_entry->length)); + if (type != AT_UNUSED) { + if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) + goto not_found; + if (type != al_entry->type) + continue; + } + al_name_len = al_entry->name_length; + al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); + /* + * If !@type we want the attribute represented by this + * attribute list entry. + */ + if (type == AT_UNUSED) + goto is_enumeration; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + if (al_name_len) + goto not_found; + } else { + int rc; + + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { + + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. Now check @lowest_vcn. Continue search if the + * next attribute list entry still fits @lowest_vcn. Otherwise + * we have reached the right one or the search has failed. + */ + if (lowest_vcn && (u8*)next_al_entry >= al_start && + (u8*)next_al_entry + 6 < al_end && + (u8*)next_al_entry + le16_to_cpu( + next_al_entry->length) <= al_end && + sle64_to_cpu(next_al_entry->lowest_vcn) <= + lowest_vcn && + next_al_entry->type == al_entry->type && + next_al_entry->name_length == al_name_len && + ntfs_names_are_equal((ntfschar*)((char*) + next_al_entry + + next_al_entry->name_offset), + next_al_entry->name_length, + al_name, al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + continue; +is_enumeration: + if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { + if (MSEQNO_LE(al_entry->mft_reference) != + le16_to_cpu( + ni->mrec->sequence_number)) { + ntfs_log_error("Found stale mft reference in " + "attribute list!\n"); + break; + } + } else { /* Mft references do not match. */ + /* Do we want the base record back? */ + if (MREF_LE(al_entry->mft_reference) == + base_ni->mft_no) { + ni = ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + } else { + /* We want an extent record. */ + ni = ntfs_extent_inode_open(base_ni, + al_entry->mft_reference); + if (!ni) + break; + ctx->ntfs_ino = ni; + ctx->mrec = ni->mrec; + } + } + a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the + * mft record containing the attribute represented by the + * current al_entry. + * + * We could call into ntfs_attr_find() to find the right + * attribute in this mft record but this would be less + * efficient and not quite accurate as ntfs_attr_find() ignores + * the attribute instance numbers for example which become + * important when one plays with attribute lists. Also, because + * a proper match has been found in the attribute list entry + * above, the comparison can now be optimized. So it is worth + * re-implementing a simplified ntfs_attr_find() here. + * + * Use a manual loop so we can still use break and continue + * with the same meanings as above. + */ +do_next_attr_loop: + if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + if (a->type == AT_END) + continue; + if (!a->length) + break; + if (al_entry->instance != a->instance) + goto do_next_attr; + /* + * If the type and/or the name are/is mismatched between the + * attribute list entry and the attribute record, there is + * corruption so we break and return error EIO. + */ + if (al_entry->type != a->type) + break; + if (!ntfs_names_are_equal((ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, al_name, + al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + break; + ctx->attr = a; + /* + * If no @val specified or @val specified and it matches, we + * have found it! Also, if !@type, it is an enumeration, so we + * want the current attribute. + */ + if ((type == AT_UNUSED) || !val || (!a->non_resident && + le32_to_cpu(a->value_length) == val_len && + !memcmp((char*)a + le16_to_cpu(a->value_offset), + val, val_len))) { + return 0; + } +do_next_attr: + /* Proceed to the next attribute in the current mft record. */ + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + goto do_next_attr_loop; + } + if (ni != base_ni) { + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->attr = ctx->base_attr; + } + errno = EIO; + ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); + return -1; +not_found: + /* + * If we were looking for AT_END or we were enumerating and reached the + * end, we reset the search context @ctx and use ntfs_attr_find() to + * seek to the end of the base mft record. + */ + if (type == AT_UNUSED || type == AT_END) { + ntfs_attr_reinit_search_ctx(ctx); + return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, + ctx); + } + /* + * The attribute wasn't found. Before we return, we want to ensure + * @ctx->mrec and @ctx->attr indicate the position at which the + * attribute should be inserted in the base mft record. Since we also + * want to preserve @ctx->al_entry we cannot reinitialize the search + * context using ntfs_attr_reinit_search_ctx() as this would set + * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see + * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve + * @ctx->al_entry as the remaining fields (base_*) are identical to + * their non base_ counterparts and we cannot set @ctx->base_attr + * correctly yet as we do not know what @ctx->attr will be set to by + * the call to ntfs_attr_find() below. + */ + ctx->mrec = ctx->base_mrec; + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ctx->base_ntfs_ino; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; + /* + * In case there are multiple matches in the base mft record, need to + * keep enumerating until we get an attribute not found response (or + * another error), otherwise we would keep returning the same attribute + * over and over again and all programs using us for enumeration would + * lock up in a tight loop. + */ + { + int ret; + + do { + ret = ntfs_attr_find(type, name, name_len, ic, val, + val_len, ctx); + } while (!ret); + return ret; + } +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol; + ntfs_inode *base_ni; + int ret = -1; + + ntfs_log_enter("Entering for attribute type 0x%x\n", type); + + if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && + (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || + !vol->upcase || !vol->upcase_len))) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto out; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); + else + ret = ntfs_external_attr_find(type, name, name_len, ic, + lowest_vcn, val, val_len, ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_position - find given or next attribute type in an ntfs inode + * @type: attribute type to start lookup + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute type in an ntfs inode or the next attribute which is not + * the AT_END attribute. Please see more details at ntfs_attr_lookup. + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * The following error codes are defined: + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + * ENOSPC No attribute was found after 'type', only AT_END. + */ +int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + return -1; + if (ctx->attr->type == AT_END) { + errno = ENOSPC; + return -1; + } + } + return 0; +} + +/** + * ntfs_attr_init_search_ctx - initialize an attribute search context + * @ctx: attribute search context to initialize + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Initialize the attribute search context @ctx with @ni and @mrec. + */ +static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, + ntfs_inode *ni, MFT_RECORD *mrec) +{ + if (!mrec) + mrec = ni->mrec; + ctx->mrec = mrec; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ni; + ctx->al_entry = NULL; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; +} + +/** + * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context + * @ctx: attribute search context to reinitialize + * + * Reinitialize the attribute search context @ctx. + * + * This is used when a search for a new attribute is being started to reset + * the search context to the beginning. + */ +void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) +{ + if (!ctx->base_ntfs_ino) { + /* No attribute list. */ + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * This needs resetting due to ntfs_external_attr_find() which + * can leave it set despite having zeroed ctx->base_ntfs_ino. + */ + ctx->al_entry = NULL; + return; + } /* Attribute list. */ + ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); + return; +} + +/** + * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Allocate a new attribute search context, initialize it with @ni and @mrec, + * and return it. Return NULL on error with errno set. + * + * @mrec can be NULL, in which case the mft record is taken from @ni. + * + * Note: For low level utilities which know what they are doing we allow @ni to + * be NULL and @mrec to be set. Do NOT do this unless you understand the + * implications!!! For example it is no longer safe to call ntfs_attr_lookup(). + */ +ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) +{ + ntfs_attr_search_ctx *ctx; + + if (!ni && !mrec) { + errno = EINVAL; + ntfs_log_perror("NULL arguments"); + return NULL; + } + ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx)); + if (ctx) + ntfs_attr_init_search_ctx(ctx, ni, mrec); + return ctx; +} + +/** + * ntfs_attr_put_search_ctx - release an attribute search context + * @ctx: attribute search context to free + * + * Release the attribute search context @ctx. + */ +void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) +{ + // NOTE: save errno if it could change and function stays void! + free(ctx); +} + +/** + * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to find + * + * Search for the attribute definition record corresponding to the attribute + * @type in the $AttrDef system file. + * + * Return the attribute type definition record if found and NULL if not found + * or an error occurred. On error the error code is stored in errno. The + * following error codes are defined: + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type) +{ + ATTR_DEF *ad; + + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; + } + for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < + vol->attrdef_len && ad->type; ++ad) { + /* We haven't found it yet, carry on searching. */ + if (le32_to_cpu(ad->type) < le32_to_cpu(type)) + continue; + /* We found the attribute; return it. */ + if (ad->type == type) + return ad; + /* We have gone too far already. No point in continuing. */ + break; + } + errno = ENOENT; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; +} + +/** + * ntfs_attr_size_bounds_check - check a size of an attribute type for validity + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * @size: size which to check + * + * Check whether the @size in bytes is valid for an attribute of @type on the + * ntfs volume @vol. This information is obtained from $AttrDef system file. + * + * Return 0 if valid and -1 if not valid or an error occurred. On error the + * error code is stored in errno. The following error codes are defined: + * ERANGE - @size is not valid for the attribute @type. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). + */ +int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, + const s64 size) +{ + ATTR_DEF *ad; + s64 min_size, max_size; + + if (size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: size=%lld", __FUNCTION__, + (long long)size); + return -1; + } + + /* + * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise + * Windows would crash. This is not listed in the AttrDef. + */ + if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { + errno = ERANGE; + ntfs_log_perror("Too large attrlist (%lld)", (long long)size); + return -1; + } + + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + + min_size = sle64_to_cpu(ad->min_size); + max_size = sle64_to_cpu(ad->max_size); + + /* The $AttrDef generated by Windows specifies 2 as min_size for the + * volume name attribute, but in reality Windows sets it to 0 when + * clearing the volume name. If we want to be able to clear the volume + * name we must also accept 0 as min_size, despite the $AttrDef + * definition. */ + if(type == AT_VOLUME_NAME) + min_size = 0; + + if ((min_size && (size < min_size)) || + ((max_size > 0) && (size > max_size))) { + errno = ERANGE; + ntfs_log_perror("Attr type %d size check failed (min,size,max=" + "%lld,%lld,%lld)", type, (long long)min_size, + (long long)size, (long long)max_size); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type to check + * @name: attribute name to check + * @name_len: attribute name length + * + * Check whether the attribute of @type and @name with name length @name_len on + * the ntfs volume @vol is allowed to be non-resident. This information is + * obtained from $AttrDef system file and is augmented by rules imposed by + * Microsoft (e.g. see http://support.microsoft.com/kb/974729/). + * + * Return 0 if the attribute is allowed to be non-resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +static int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type, + const ntfschar *name, int name_len) +{ + ATTR_DEF *ad; + BOOL allowed; + + /* + * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a + * name of $TXF_DATA must be resident despite the entry for + * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. + * Failure to obey this on the root directory mft record of a volume + * causes Windows Vista and later to see the volume as a RAW volume and + * thus cannot mount it at all. + */ + if ((type == AT_LOGGED_UTILITY_STREAM) + && name + && ntfs_names_are_equal(TXF_DATA, 9, name, name_len, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) + allowed = FALSE; + else { + /* Find the attribute definition record in $AttrDef. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + /* Check the flags and return the result. */ + allowed = !(ad->flags & ATTR_DEF_RESIDENT); + } + if (!allowed) { + errno = EPERM; + ntfs_log_trace("Attribute can't be non-resident\n"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_resident - check if an attribute can be resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be resident. This information is derived from our ntfs knowledge and may + * not be completely accurate, especially when user defined attributes are + * present. Basically we allow everything to be resident except for index + * allocation and extended attribute attributes. + * + * Return 0 if the attribute is allowed to be resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be resident. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + * + * Warning: In the system file $MFT the attribute $Bitmap must be non-resident + * otherwise windows will not boot (blue screen of death)! We cannot + * check for this here as we don't know which inode's $Bitmap is being + * asked about so the caller needs to special case this. + */ +int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) +{ + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + return -1; + } + if (type != AT_INDEX_ALLOCATION) + return 0; + + ntfs_log_trace("Attribute can't be resident\n"); + errno = EPERM; + return -1; +} + +/** + * ntfs_make_room_for_attr - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * ENOSPC - There is not enough space available to complete operation. The + * caller has to make space before calling this. + * EINVAL - Input parameters were faulty. + */ +int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) +{ + u32 biu; + + ntfs_log_trace("Entering for pos 0x%d, size %u.\n", + (int)(pos - (u8*)m), (unsigned) size); + + /* Make size 8-byte alignment. */ + size = (size + 7) & ~7; + + /* Rigorous consistency checks. */ + if (!m || !pos || pos < (u8*)m) { + errno = EINVAL; + ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); + return -1; + } + /* The -8 is for the attribute terminator. */ + if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { + errno = EINVAL; + return -1; + } + /* Nothing to do. */ + if (!size) + return 0; + + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("No enough space in the MFT record\n"); + return -1; + } + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (u8*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_resident_attr_record_add - add resident attribute to inode + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute + * @name: name of the new attribute + * @name_len: name length of the new attribute + * @val: value of the new attribute + * @size: size of new attribute (length of @val, if @val != NULL) + * @flags: flags of the new attribute + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type and with same name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, u32 size, + ATTR_FLAGS data_flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + int err, offset; + ntfs_inode *base_ni; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, (unsigned) data_flags); + + if (!ni || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_resident(ni->vol, type)) { + if (errno == EPERM) + ntfs_log_trace("Attribute can't be resident.\n"); + else + ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, + ctx)) { + err = EEXIST; + ntfs_log_trace("Attribute already present.\n"); + goto put_err_out; + } + if (errno != ENOENT) { + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + length = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_trace("Failed to make room for attribute.\n"); + goto put_err_out; + } + + /* Setup record fields. */ + offset = ((u8*)a - (u8*)m); + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 0; + a->name_length = name_len; + a->name_offset = (name_len + ? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) + : const_cpu_to_le16(0)); + a->flags = data_flags; + a->instance = m->next_attr_instance; + a->value_length = cpu_to_le32(size); + a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); + if (val) + memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); + else + memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); + if (type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_attr_record_resize(m, a, 0); + ntfs_log_trace("Failed add attribute entry to " + "ATTRIBUTE_LIST.\n"); + goto put_err_out; + } + } + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); + } + ntfs_inode_mark_dirty(ni); + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_record_add - add extent of non-resident attribute + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute extent + * @name: name of the new attribute extent + * @name_len: name length of the new attribute extent + * @lowest_vcn: lowest vcn of the new attribute extent + * @dataruns_size: dataruns size of the new attribute extent + * @flags: flags of the new attribute extent + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type, with same lowest vcn and with same + * name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + ATTR_FLAGS flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + ntfs_inode *base_ni; + int err, offset; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " + "dataruns_size %d, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, + (long long) lowest_vcn, dataruns_size, (unsigned) flags); + + if (!ni || dataruns_size <= 0 || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno == EPERM) + ntfs_log_perror("Attribute can't be non resident"); + else + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, + ctx)) { + err = EEXIST; + ntfs_log_perror("Attribute 0x%x already present", type); + goto put_err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("ntfs_attr_find failed"); + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + dataruns_size = (dataruns_size + 7) & ~7; + length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * + name_len + 7) & ~7) + dataruns_size + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_perror("Failed to make room for attribute"); + goto put_err_out; + } + + /* Setup record fields. */ + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0)); + a->flags = flags; + a->instance = m->next_attr_instance; + a->lowest_vcn = cpu_to_sle64(lowest_vcn); + a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); + a->compression_unit = (flags & ATTR_IS_COMPRESSED) + ? STANDARD_COMPRESSION_UNIT : 0; + /* If @lowest_vcn == 0, than setup empty attribute. */ + if (!lowest_vcn) { + a->highest_vcn = cpu_to_sle64(-1); + a->allocated_size = 0; + a->data_size = 0; + a->initialized_size = 0; + /* Set empty mapping pairs. */ + *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; + } + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_log_perror("Failed add attr entry to attrlist"); + ntfs_attr_record_resize(m, a, 0); + goto put_err_out; + } + } + ntfs_inode_mark_dirty(ni); + /* + * Locate offset from start of the MFT record where new attribute is + * placed. We need relookup it, because record maybe moved during + * update of attribute list. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, + lowest_vcn, NULL, 0, ctx)) { + ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); + ntfs_attr_put_search_ctx(ctx); + return -1; + + } + offset = (u8*)ctx->attr - (u8*)ctx->mrec; + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_rm - remove attribute extent + * @ctx: search context describing the attribute which should be removed + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ATTR_TYPES type; + + if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->attr->type)); + type = ctx->attr->type; + ni = ctx->ntfs_ino; + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + /* Remove attribute itself. */ + if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { + ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " + "record.\n"); + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) + if (ntfs_attrlist_entry_add(ni, ctx->attr)) + ntfs_log_trace("Rollback failed. Leaving inconstant " + "metadata.\n"); + errno = EIO; + return -1; + } + ntfs_inode_mark_dirty(ni); + + /* + * Remove record from $ATTRIBUTE_LIST if present and we don't want + * delete $ATTRIBUTE_LIST itself. + */ + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { + if (ntfs_attrlist_entry_rm(ctx)) { + ntfs_log_trace("Couldn't delete record from " + "$ATTRIBUTE_LIST.\n"); + return -1; + } + } + + /* Post $ATTRIBUTE_LIST delete setup. */ + if (type == AT_ATTRIBUTE_LIST) { + if (NInoAttrList(base_ni) && base_ni->attr_list) + free(base_ni->attr_list); + base_ni->attr_list = NULL; + NInoClearAttrList(base_ni); + NInoAttrListClearDirty(base_ni); + } + + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - + le16_to_cpu(ctx->mrec->attrs_offset) == 8) { + if (ntfs_mft_record_free(ni->vol, ni)) { + // FIXME: We need rollback here. + ntfs_log_trace("Couldn't free MFT record.\n"); + errno = EIO; + return -1; + } + /* Remove done if we freed base inode. */ + if (ni == base_ni) + return 0; + } + + if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) + return 0; + + /* Remove attribute list if we don't need it any more. */ + if (!ntfs_attrlist_need(base_ni)) { + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* + * FIXME: Should we succeed here? Definitely something + * goes wrong because NInoAttrList(base_ni) returned + * that we have got attribute list. + */ + ntfs_log_trace("Couldn't find attribute list. Succeed " + "anyway.\n"); + return 0; + } + /* Deallocate clusters. */ + if (ctx->attr->non_resident) { + runlist *al_rl; + + al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, + ctx->attr, NULL); + if (!al_rl) { + ntfs_log_trace("Couldn't decompress attribute list " + "runlist. Succeed anyway.\n"); + return 0; + } + if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { + ntfs_log_trace("Leaking clusters! Run chkdsk. " + "Couldn't free clusters from " + "attribute list runlist.\n"); + } + free(al_rl); + } + /* Remove attribute record itself. */ + if (ntfs_attr_record_rm(ctx)) { + /* + * FIXME: Should we succeed here? BTW, chkdsk doesn't + * complain if it find MFT record with attribute list, + * but without extents. + */ + ntfs_log_trace("Couldn't remove attribute list. Succeed " + "anyway.\n"); + return 0; + } + } + return 0; +} + +/** + * ntfs_attr_add - add attribute to inode + * @ni: opened ntfs inode to which add attribute + * @type: type of the new attribute + * @name: name in unicode of the new attribute + * @name_len: name length in unicode characters of the new attribute + * @val: value of new attribute + * @size: size of the new attribute / length of @val (if specified) + * + * @val should always be specified for always resident attributes (eg. FILE_NAME + * attribute), for attributes that can become non-resident @val can be NULL + * (eg. DATA attribute). @size can be specified even if @val is NULL, in this + * case data size will be equal to @size and initialized size will be equal + * to 0. + * + * If inode haven't got enough space to add attribute, add attribute to one of + * it extents, if no extents present or no one of them have enough space, than + * allocate new extent and add attribute to it. + * + * If on one of this steps attribute list is needed but not present, than it is + * added transparently to caller. So, this function should not be called with + * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call + * ntfs_inode_add_attrlist instead. + * + * On success return 0. On error return -1 with errno set to the error code. + */ +int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, s64 size) +{ + u32 attr_rec_size; + int err, i, offset; + BOOL is_resident; + BOOL can_be_non_resident = FALSE; + ntfs_inode *attr_ni; + ntfs_attr *na; + ATTR_FLAGS data_flags; + + if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, + (long long)size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", + (long long)ni->mft_no, type, (long long)size); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + /* Check the attribute type and the size. */ + if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* Sanity checks for always resident attributes. */ + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + goto err_out; + } + /* @val is mandatory. */ + if (!val) { + errno = EINVAL; + ntfs_log_perror("val is mandatory for always resident " + "attributes"); + return -1; + } + if (size > ni->vol->mft_record_size) { + errno = ERANGE; + ntfs_log_perror("Attribute is too big"); + return -1; + } + } else + can_be_non_resident = TRUE; + + /* + * Determine resident or not will be new attribute. We add 8 to size in + * non resident case for mapping pairs. + */ + if (!ntfs_attr_can_be_resident(ni->vol, type)) { + is_resident = TRUE; + } else { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_resident failed"); + goto err_out; + } + is_resident = FALSE; + } + /* Calculate attribute record size. */ + if (is_resident) + attr_rec_size = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + else + attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; + + /* + * If we have enough free space for the new attribute in the base MFT + * record, then add attribute to it. + */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { + attr_ni = ni; + goto add_attr_record; + } + + /* Try to add to extent inodes. */ + if (ntfs_inode_attach_all_extents(ni)) { + err = errno; + ntfs_log_perror("Failed to attach all extents to inode"); + goto err_out; + } + for (i = 0; i < ni->nr_extents; i++) { + attr_ni = ni->extent_nis[i]; + if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - + le32_to_cpu(attr_ni->mrec->bytes_in_use) >= + attr_rec_size) + goto add_attr_record; + } + + /* There is no extent that contain enough space for new attribute. */ + if (!NInoAttrList(ni)) { + /* Add attribute list not present, add it and retry. */ + if (ntfs_inode_add_attrlist(ni)) { + err = errno; + ntfs_log_perror("Failed to add attribute list"); + goto err_out; + } + return ntfs_attr_add(ni, type, name, name_len, val, size); + } + /* Allocate new extent. */ + attr_ni = ntfs_mft_record_alloc(ni->vol, ni); + if (!attr_ni) { + err = errno; + ntfs_log_perror("Failed to allocate extent record"); + goto err_out; + } + +add_attr_record: + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && ((type == AT_DATA) + || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) + data_flags = ATTR_IS_COMPRESSED; + else + data_flags = const_cpu_to_le16(0); + if (is_resident) { + /* Add resident attribute. */ + offset = ntfs_resident_attr_record_add(attr_ni, type, name, + name_len, val, size, data_flags); + if (offset < 0) { + if (errno == ENOSPC && can_be_non_resident) + goto add_non_resident; + err = errno; + ntfs_log_perror("Failed to add resident attribute"); + goto free_err_out; + } + return 0; + } + +add_non_resident: + /* Add non resident attribute. */ + offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, + name_len, 0, 8, data_flags); + if (offset < 0) { + err = errno; + ntfs_log_perror("Failed to add non resident attribute"); + goto free_err_out; + } + + /* If @size == 0, we are done. */ + if (!size) + return 0; + + /* Open new attribute and resize it. */ + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added attribute"); + goto rm_attr_err_out; + } + /* Resize and set attribute value. */ + if (ntfs_attr_truncate_i(na, size, HOLES_OK) || + (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { + err = errno; + ntfs_log_perror("Failed to initialize just added attribute"); + if (ntfs_attr_rm(na)) + ntfs_log_perror("Failed to remove just added attribute"); + ntfs_attr_close(na); + goto err_out; + } + ntfs_attr_close(na); + return 0; + +rm_attr_err_out: + /* Remove just added attribute. */ + if (ntfs_attr_record_resize(attr_ni->mrec, + (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) + ntfs_log_perror("Failed to remove just added attribute #2"); +free_err_out: + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - + le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) + ntfs_log_perror("Failed to free MFT record"); +err_out: + errno = err; + return -1; +} + +/* + * Change an attribute flag + */ + +int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) +{ + ntfs_attr_search_ctx *ctx; + int res; + + res = -1; + /* Search for designated attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(type, name, name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* do the requested change (all small endian le16) */ + ctx->attr->flags = (ctx->attr->flags & ~mask) + | (flags & mask); + NInoSetDirty(ni); + res = 0; + } + ntfs_attr_put_search_ctx(ctx); + } + return (res); +} + + +/** + * ntfs_attr_rm - remove attribute from ntfs inode + * @na: opened ntfs attribute to delete + * + * Remove attribute and all it's extents from ntfs inode. If attribute was non + * resident also free all clusters allocated by attribute. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_attr_rm(ntfs_attr *na) +{ + ntfs_attr_search_ctx *ctx; + int ret = 0; + + if (!na) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) na->ni->mft_no, na->type); + + /* Free cluster allocation. */ + if (NAttrNonResident(na)) { + if (ntfs_attr_map_whole_runlist(na)) + return -1; + if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { + ntfs_log_trace("Failed to free cluster allocation. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + } + + /* Search for attribute extents and remove them all. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_trace("Failed to remove attribute extent. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + ntfs_attr_reinit_search_ctx(ctx); + } + ntfs_attr_put_search_ctx(ctx); + if (errno != ENOENT) { + ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " + "metadata.\n"); + ret = -1; + } + + return ret; +} + +/** + * ntfs_attr_record_resize - resize an attribute record + * @m: mft record containing attribute record + * @a: attribute record to resize + * @new_size: new size in bytes to which to resize the attribute record @a + * + * Resize the attribute record @a, i.e. the resident part of the attribute, in + * the mft record @m to @new_size bytes. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + * + * Warning: If you make a record smaller without having copied all the data you + * are interested in the data may be overwritten! + */ +int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) +{ + u32 old_size, alloc_size, attr_size; + + old_size = le32_to_cpu(m->bytes_in_use); + alloc_size = le32_to_cpu(m->bytes_allocated); + attr_size = le32_to_cpu(a->length); + + ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", + (unsigned)old_size, (unsigned)alloc_size, + (unsigned)attr_size, (unsigned)new_size); + + /* Align to 8 bytes, just in case the caller hasn't. */ + new_size = (new_size + 7) & ~7; + + /* If the actual attribute length has changed, move things around. */ + if (new_size != attr_size) { + + u32 new_muse = old_size - attr_size + new_size; + + /* Not enough space in this mft record. */ + if (new_muse > alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Not enough space in the MFT record " + "(%u > %u)\n", new_muse, alloc_size); + return -1; + } + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", + new_muse, alloc_size); + return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + } + + /* Move attributes following @a to their new location. */ + memmove((u8 *)a + new_size, (u8 *)a + attr_size, + old_size - ((u8 *)a - (u8 *)m) - attr_size); + + /* Adjust @m to reflect the change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + + /* Adjust @a to reflect the new size. */ + if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) + a->length = cpu_to_le32(new_size); + } + return 0; +} + +/** + * ntfs_resident_attr_value_resize - resize the value of a resident attribute + * @m: mft record containing attribute record + * @a: attribute record whose value to resize + * @new_size: new size in bytes to which to resize the attribute value of @a + * + * Resize the value of the attribute @a in the mft record @m to @new_size bytes. + * If the value is made bigger, the newly "allocated" space is cleared. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + */ +int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size) +{ + int ret; + + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); + + /* Resize the resident part of the attribute record. */ + if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + + new_size + 7) & ~7)) < 0) + return ret; + /* + * If we made the attribute value bigger, clear the area between the + * old size and @new_size. + */ + if (new_size > le32_to_cpu(a->value_length)) + memset((u8*)a + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length), 0, new_size - + le32_to_cpu(a->value_length)); + /* Finally update the length of the attribute value. */ + a->value_length = cpu_to_le32(new_size); + return 0; +} + +/** + * ntfs_attr_record_move_to - move attribute record to target inode + * @ctx: attribute search context describing the attribute record + * @ni: opened ntfs inode to which move attribute record + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) +{ + ntfs_attr_search_ctx *nctx; + ATTR_RECORD *a; + int err; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " + "0x%llx, ni->mft_no 0x%llx.\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (long long) ctx->ntfs_ino->mft_no, + (long long) ni->mft_no); + + if (ctx->ntfs_ino == ni) + return 0; + + if (!ctx->al_entry) { + ntfs_log_trace("Inode should contain attribute list to use this " + "function.\n"); + errno = EINVAL; + return -1; + } + + /* Find place in MFT record where attribute will be moved. */ + a = ctx->attr; + nctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!nctx) + return -1; + + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, + 0, nctx)) { + ntfs_log_trace("Attribute of such type, with same name already " + "present in this MFT record.\n"); + err = EEXIST; + goto put_err_out; + } + if (errno != ENOENT) { + err = errno; + ntfs_log_debug("Attribute lookup failed.\n"); + goto put_err_out; + } + + /* Make space and move attribute. */ + if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, + le32_to_cpu(a->length))) { + err = errno; + ntfs_log_trace("Couldn't make space for attribute.\n"); + goto put_err_out; + } + memcpy(nctx->attr, a, le32_to_cpu(a->length)); + nctx->attr->instance = nctx->mrec->next_attr_instance; + nctx->mrec->next_attr_instance = cpu_to_le16( + (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); + ntfs_attr_record_resize(ctx->mrec, a, 0); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_inode_mark_dirty(ni); + + /* Update attribute list. */ + ctx->al_entry->mft_reference = + MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + ctx->al_entry->instance = nctx->attr->instance; + ntfs_attrlist_mark_dirty(ni); + + ntfs_attr_put_search_ctx(nctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(nctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_move_away - move away attribute record from it's mft record + * @ctx: attribute search context describing the attribute record + * @extra: minimum amount of free space in the new holder of record + * + * New attribute record holder must have free @extra bytes after moving + * attribute record to it. + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) +{ + ntfs_inode *base_ni, *ni; + MFT_RECORD *m; + int i; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, + ctx, ctx ? ctx->attr : NULL, extra); + return -1; + } + + ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (unsigned long long)ctx->ntfs_ino->mft_no); + + if (ctx->ntfs_ino->nr_extents == -1) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + if (!NInoAttrList(base_ni)) { + errno = EINVAL; + ntfs_log_perror("Inode %llu has no attrlist", + (unsigned long long)base_ni->mft_no); + return -1; + } + + if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { + ntfs_log_perror("Couldn't attach extents, inode=%llu", + (unsigned long long)base_ni->mft_no); + return -1; + } + + /* Walk through all extents and try to move attribute to them. */ + for (i = 0; i < base_ni->nr_extents; i++) { + ni = base_ni->extent_nis[i]; + m = ni->mrec; + + if (ctx->ntfs_ino->mft_no == ni->mft_no) + continue; + + if (le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) < + le32_to_cpu(ctx->attr->length) + extra) + continue; + + /* + * ntfs_attr_record_move_to can fail if extent with other lowest + * VCN already present in inode we trying move record to. So, + * do not return error. + */ + if (!ntfs_attr_record_move_to(ctx, ni)) + return 0; + } + + /* + * Failed to move attribute to one of the current extents, so allocate + * new extent and move attribute to it. + */ + ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Couldn't allocate MFT record"); + return -1; + } + if (ntfs_attr_record_move_to(ctx, ni)) { + ntfs_log_perror("Couldn't move attribute to MFT record"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute + * @na: open ntfs attribute to make non-resident + * @ctx: ntfs search context describing the attribute + * + * Convert a resident ntfs attribute to a non-resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * TODO: others... + * + * NOTE to self: No changes in the attribute list are required to move from + * a resident to a non-resident attribute. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx) +{ + s64 new_allocated_size, bw; + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + runlist *rl; + int mp_size, mp_ofs, name_ofs, arec_size, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Some preliminary sanity checking. */ + if (NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make non-resident attribute " + "non-resident. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Check that the attribute is allowed to be non-resident. */ + if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len)) + return -1; + + new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size + - 1) & ~(vol->cluster_size - 1); + + if (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } + /* Start by allocating clusters to hold the attribute value. */ + rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> + vol->cluster_size_bits, -1, DATA_ZONE); + if (!rl) + return -1; + } else + rl = NULL; + /* + * Setup the in-memory attribute structure to be non-resident so that + * we can use ntfs_attr_pwrite(). + */ + NAttrSetNonResident(na); + NAttrSetBeingNonResident(na); + na->rl = rl; + na->allocated_size = new_allocated_size; + na->data_size = na->initialized_size = le32_to_cpu(a->value_length); + /* + * FIXME: For now just clear all of these as we don't support them when + * writing. + */ + NAttrClearSparse(na); + NAttrClearEncrypted(na); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* set compression writing parameters */ + na->compression_block_size + = 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits); + na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT; + } + + if (rl) { + /* Now copy the attribute value to the allocated cluster(s). */ + bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), + (u8*)a + le16_to_cpu(a->value_offset)); + if (bw != le32_to_cpu(a->value_length)) { + err = errno; + ntfs_log_debug("Eeek! Failed to write out attribute value " + "(bw = %lli, errno = %i). " + "Aborting...\n", (long long)bw, err); + if (bw >= 0) + err = EIO; + goto cluster_free_err_out; + } + } + /* Determine the size of the mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX); + if (mp_size < 0) { + err = errno; + ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " + "Aborting...\n"); + goto cluster_free_err_out; + } + /* Calculate new offsets for the name and the mapping pairs array. */ + if (na->ni->flags & FILE_ATTR_COMPRESSED) + name_ofs = (sizeof(ATTR_REC) + 7) & ~7; + else + name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; + mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + /* + * Determine the size of the resident part of the non-resident + * attribute record. (Not compressed thus no compressed_size element + * present.) + */ + arec_size = (mp_ofs + mp_size + 7) & ~7; + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + err = errno; + goto cluster_free_err_out; + } + + /* + * Convert the resident part of the attribute record to describe a + * non-resident attribute. + */ + a->non_resident = 1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + a->name_offset = cpu_to_le16(name_ofs); + + /* Setup the fields specific to non-resident attributes. */ + a->lowest_vcn = cpu_to_sle64(0); + a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> + vol->cluster_size_bits); + + a->mapping_pairs_offset = cpu_to_le16(mp_ofs); + + /* + * Update the flags to match the in-memory ones. + * However cannot change the compression state if we had + * a fuse_file_info open with a mark for release. + * The decisions about compression can only be made when + * creating/recreating the stream, not when making non resident. + */ + a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* support only ATTR_IS_COMPRESSED compression mode */ + a->compression_unit = STANDARD_COMPRESSION_UNIT; + a->compressed_size = const_cpu_to_le64(0); + } else { + a->compression_unit = 0; + a->flags &= ~ATTR_COMPRESSION_MASK; + na->data_flags = a->flags; + } + + memset(&a->reserved1, 0, sizeof(a->reserved1)); + + a->allocated_size = cpu_to_sle64(new_allocated_size); + a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); + + /* Generate the mapping pairs array in the attribute record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, + rl, 0, NULL) < 0) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " + "corrupt attribute record on disk. In memory " + "runlist is still intact! Error code is %i. " + "FIXME: Need to rollback instead!\n", errno); + return -1; + } + + /* Done! */ + return 0; + +cluster_free_err_out: + if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) + ntfs_log_trace("Eeek! Failed to release allocated clusters in error " + "code path. Leaving inconsistent metadata...\n"); + NAttrClearNonResident(na); + NAttrClearFullyMapped(na); + na->allocated_size = na->data_size; + na->rl = NULL; + free(rl); + errno = err; + return -1; +} + + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); + +/** + * ntfs_resident_attr_resize - resize a resident, open ntfs attribute + * @na: resident ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * + * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + * Can also be used to force an attribute non-resident. In this case, the + * size cannot be changed. + * + * On success return 0 + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + BOOL force_non_resident) +{ + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol; + ntfs_inode *ni; + int err, ret = STATUS_ERROR; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + /* Get the attribute record that needs modification. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, + ctx)) { + err = errno; + ntfs_log_perror("ntfs_attr_lookup failed"); + goto put_err_out; + } + vol = na->ni->vol; + /* + * Check the attribute type and the corresponding minimum and maximum + * sizes against @newsize and fail if @newsize is out of bounds. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + goto put_err_out; + } + /* + * If @newsize is bigger than the mft record we need to make the + * attribute non-resident if the attribute type supports it. If it is + * smaller we can go ahead and attempt the resize. + */ + if ((newsize < vol->mft_record_size) && !force_non_resident) { + /* Perform the resize of the attribute record. */ + if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize))) { + /* Update attribute size everywhere. */ + na->data_size = na->initialized_size = newsize; + na->allocated_size = (newsize + 7) & ~7; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + na->compressed_size = na->allocated_size; + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); + } + goto resize_done; + } + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + err = errno; + goto put_err_out; + } + } + /* There is not enough space in the mft record to perform the resize. */ + + /* Make the attribute non-resident if possible. */ + if (!ntfs_attr_make_non_resident(na, ctx)) { + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (force_non_resident) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } + /* Resize non-resident attribute */ + return ntfs_attr_truncate_i(na, newsize, HOLES_OK); + } else if (errno != ENOSPC && errno != EPERM) { + err = errno; + ntfs_log_perror("Failed to make attribute non-resident"); + goto put_err_out; + } + + /* Try to make other attributes non-resident and retry each time. */ + ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + ntfs_attr *tna; + ATTR_RECORD *a; + + a = ctx->attr; + if (a->non_resident) + continue; + + /* + * Check out whether convert is reasonable. Assume that mapping + * pairs will take 8 bytes. + */ + if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, + compressed_size) + ((a->name_length * + sizeof(ntfschar) + 7) & ~7) + 8) + continue; + + tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + + le16_to_cpu(a->name_offset)), a->name_length); + if (!tna) { + err = errno; + ntfs_log_perror("Couldn't open attribute"); + goto put_err_out; + } + if (ntfs_attr_make_non_resident(tna, ctx)) { + ntfs_attr_close(tna); + continue; + } + if ((tna->type == AT_DATA) && !tna->name_len) { + /* + * If we had to make the unnamed data attribute + * non-resident, propagate its new allocated size + * to all name attributes and directory indexes + */ + tna->ni->allocated_size = tna->allocated_size; + NInoFileNameSetDirty(tna->ni); + } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } + ntfs_inode_mark_dirty(tna->ni); + ntfs_attr_close(tna); + ntfs_attr_put_search_ctx(ctx); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); + goto put_err_out; + } + + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, + non_resident_end) + 8)) { + ntfs_log_perror("Could not free space in MFT record"); + return -1; + } + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + + /* + * Move the attribute to a new mft record, creating an attribute list + * attribute or modifying it if it is already present. + */ + + /* Point search context back to attribute which we need resize. */ + ntfs_attr_init_search_ctx(ctx, na->ni, NULL); + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); + err = errno; + goto put_err_out; + } + + /* + * Check whether attribute is already single in this MFT record. + * 8 added for the attribute terminator. + */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) == + le16_to_cpu(ctx->mrec->attrs_offset) + + le32_to_cpu(ctx->attr->length) + 8) { + err = ENOSPC; + ntfs_log_trace("MFT record is filled with one attribute\n"); + ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + goto put_err_out; + } + + /* Add attribute list if not present. */ + if (na->ni->nr_extents == -1) + ni = na->ni->base_ni; + else + ni = na->ni; + if (!NInoAttrList(ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(ni)) + return -1; + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(vol, ni); + if (!ni) { + err = errno; + ntfs_log_perror("Couldn't allocate new MFT record"); + goto put_err_out; + } + /* Move attribute to it. */ + if (ntfs_attr_record_move_to(ctx, ni)) { + err = errno; + ntfs_log_perror("Couldn't move attribute to new MFT record"); + goto put_err_out; + } + /* Update ntfs attribute. */ + if (na->ni->nr_extents == -1) + na->ni = ni; + + ntfs_attr_put_search_ctx(ctx); + /* Try to perform resize once again. */ + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + +resize_done: + /* + * Set the inode (and its base inode if it exists) dirty so it is + * written out later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); + ntfs_log_leave("\n"); + return ret; +} + +/* + * Force an attribute to be made non-resident without + * changing its size. + * + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. + * + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. + * + * returns 0 if successful, + * < 0 if failed, with errno telling why + */ + +int ntfs_attr_force_non_resident(ntfs_attr *na) +{ + int res; + + res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); +} + +/** + * ntfs_attr_make_resident - convert a non-resident to a resident attribute + * @na: open ntfs attribute to make resident + * @ctx: ntfs search context describing the attribute + * + * Convert a non-resident ntfs attribute to a resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed. + * EPERM - The attribute is not allowed to be resident. + * EIO - I/O error, damaged inode or bug. + * ENOSPC - There is no enough space to perform conversion. + * EOPNOTSUPP - Requested conversion is not supported yet. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + int name_ofs, val_ofs, err = EIO; + s64 arec_size, bytes_read; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Should be called for the first extent of the attribute. */ + if (sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_trace("Eeek! Should be called for the first extent of the " + "attribute. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Some preliminary sanity checking. */ + if (!NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make resident attribute resident. " + "Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ + if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { + errno = EPERM; + return -1; + } + + /* Check that the attribute is allowed to be resident. */ + if (ntfs_attr_can_be_resident(vol, na->type)) + return -1; + + if (na->data_flags & ATTR_IS_ENCRYPTED) { + ntfs_log_trace("Making encrypted streams resident is not " + "implemented yet.\n"); + errno = EOPNOTSUPP; + return -1; + } + + /* Work out offsets into and size of the resident attribute. */ + name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ + val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + arec_size = (val_ofs + na->data_size + 7) & ~7; + + /* Sanity check the size before we start modifying the attribute. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + + arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("Not enough space to make attribute resident\n"); + return -1; + } + + /* Read and cache the whole runlist if not already done. */ + if (ntfs_attr_map_whole_runlist(na)) + return -1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) { + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + } + a->name_offset = cpu_to_le16(name_ofs); + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + /* + * Bug, because ntfs_attr_record_resize should not fail (we + * already checked that attribute fits MFT record). + */ + ntfs_log_error("BUG! Failed to resize attribute record. " + "Please report to the %s. Aborting...\n", + NTFS_DEV_LIST); + errno = EIO; + return -1; + } + + /* Convert the attribute record to describe a resident attribute. */ + a->non_resident = 0; + a->flags = 0; + a->value_length = cpu_to_le32(na->data_size); + a->value_offset = cpu_to_le16(val_ofs); + /* + * If a data stream was wiped out, adjust the compression mode + * to current state of compression flag + */ + if (!na->data_size + && (na->type == AT_DATA) + && (na->ni->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (na->ni->flags & FILE_ATTR_COMPRESSED)) { + a->flags |= ATTR_IS_COMPRESSED; + na->data_flags = a->flags; + } + /* + * File names cannot be non-resident so we would never see this here + * but at least it serves as a reminder that there may be attributes + * for which we do need to set this flag. (AIA) + */ + if (a->type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + a->reservedR = 0; + + /* Sanity fixup... Shouldn't really happen. (AIA) */ + if (na->initialized_size > na->data_size) + na->initialized_size = na->data_size; + + /* Copy data from run list to resident attribute value. */ + bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, + (u8*)a + val_ofs); + if (bytes_read != na->initialized_size) { + if (bytes_read < 0) + err = errno; + ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " + "inconstant metadata. Run chkdsk. " + "Aborting...\n"); + errno = err; + return -1; + } + + /* Clear memory in gap between initialized_size and data_size. */ + if (na->initialized_size < na->data_size) + memset((u8*)a + val_ofs + na->initialized_size, 0, + na->data_size - na->initialized_size); + + /* + * Deallocate clusters from the runlist. + * + * NOTE: We can use ntfs_cluster_free() because we have already mapped + * the whole run list and thus it doesn't matter that the attribute + * record is in a transiently corrupted state at this moment in time. + */ + if (ntfs_cluster_free(vol, na, 0, -1) < 0) { + ntfs_log_perror("Eeek! Failed to release allocated clusters"); + ntfs_log_trace("Ignoring error and leaving behind wasted " + "clusters.\n"); + } + + /* Throw away the now unused runlist. */ + free(na->rl); + na->rl = NULL; + + /* Update in-memory struct ntfs_attr. */ + NAttrClearNonResident(na); + NAttrClearFullyMapped(na); + NAttrClearSparse(na); + NAttrClearEncrypted(na); + na->initialized_size = na->data_size; + na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; + na->compression_block_size = 0; + na->compression_block_size_bits = na->compression_block_clusters = 0; + return 0; +} + +/* + * If we are in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ +static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, + hole_type holes, ntfs_attr_search_ctx *ctx) +{ + int sparse, ret = 0; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (a->lowest_vcn) + goto out; + + a->allocated_size = cpu_to_sle64(na->allocated_size); + + /* Update sparse bit, unless this is an intermediate state */ + if (holes == HOLES_DELAY) + sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); + else { + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } + } + + /* Check whether attribute becomes sparse, unless check is delayed. */ + if ((holes != HOLES_DELAY) + && sparse + && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* + * Move attribute to another mft record, if attribute is too + * small to add compressed_size field to it and we have no + * free space in the current mft record. + */ + if ((le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset) == 8) + && !(le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use))) { + + if (!NInoAttrList(na->ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(na->ni)) + goto leave; + goto retry; + } + if (ntfs_attr_record_move_away(ctx, 8)) { + ntfs_log_perror("Failed to move attribute"); + goto error; + } + ntfs_attr_put_search_ctx(ctx); + goto retry; + } + if (!(le32_to_cpu(a->length) - le16_to_cpu( + a->mapping_pairs_offset))) { + errno = EIO; + ntfs_log_perror("Mapping pairs space is 0"); + goto error; + } + + NAttrSetSparse(na); + a->flags |= ATTR_IS_SPARSE; + na->data_flags = a->flags; + a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows + set it so, even if attribute is not actually compressed. */ + + memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); + } + + /* Attribute no longer sparse. */ + if (!sparse && (a->flags & ATTR_IS_SPARSE) && + !(a->flags & ATTR_IS_COMPRESSED)) { + + NAttrClearSparse(na); + a->flags &= ~ATTR_IS_SPARSE; + na->data_flags = a->flags; + a->compression_unit = 0; + + memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + if (le16_to_cpu(a->name_offset) >= 8) + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); + } + + /* Update compressed size if required. */ + if (NAttrFullyMapped(na) + && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); + if (new_compr_size == -1) + goto error; + + na->compressed_size = new_compr_size; + a->compressed_size = cpu_to_sle64(new_compr_size); + } + /* + * Set FILE_NAME dirty flag, to update sparse bit and + * allocated size in the index. + */ + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + NInoFileNameSetDirty(na->ni); + } +out: + return ret; +leave: ret = -1; goto out; /* return -1 */ +retry: ret = -2; goto out; +error: ret = -3; goto out; +} + +#define NTFS_VCN_DELETE_MARK -2 +/** + * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs + */ +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn, + hole_type holes) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *ni, *base_ni; + MFT_RECORD *m; + ATTR_RECORD *a; + VCN stop_vcn; + const runlist_element *stop_rl; + int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; + BOOL finished_build; + BOOL first_updated = FALSE; + +retry: + if (!na || !na->rl) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p", __FUNCTION__, na); + return -1; + } + + ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (!NAttrNonResident(na)) { + errno = EINVAL; + ntfs_log_perror("%s: resident attribute", __FUNCTION__); + return -1; + } + +#if PARTIAL_RUNLIST_UPDATING + /* + * For a file just been made sparse, we will have + * to reformat the first extent, so be sure the + * runlist is fully mapped and fully processed. + * Same if the file was sparse and is not any more. + * Note : not needed if the full runlist is to be processed + */ + if ((holes != HOLES_DELAY) + && (!NAttrFullyMapped(na) || from_vcn) + && !(na->data_flags & ATTR_IS_COMPRESSED)) { + BOOL changed; + + if (!(na->data_flags & ATTR_IS_SPARSE)) { + int sparse; + runlist_element *xrl; + + /* + * If attribute was not sparse, we only + * have to check whether there is a hole + * in the updated region. + */ + xrl = na->rl; + if (xrl->lcn == LCN_RL_NOT_MAPPED) + xrl++; + sparse = ntfs_rl_sparse(xrl); + if (sparse < 0) { + ntfs_log_error("Could not check whether sparse\n"); + errno = EIO; + return (-1); + } + changed = sparse > 0; + } else { + /* + * If attribute was sparse, the compressed + * size has been maintained, and it gives + * and easy way to check whether the + * attribute is still sparse. + */ + changed = (((na->data_size - 1) + | (na->ni->vol->cluster_size - 1)) + 1) + == na->compressed_size; + } + if (changed) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_error("Could not map whole for sparse change\n"); + errno = EIO; + return (-1); + } + from_vcn = 0; + } + } +#endif + if (na->ni->nr_extents == -1) + base_ni = na->ni->base_ni; + else + base_ni = na->ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) + return -1; + + /* Fill attribute records with new mapping pairs. */ + stop_vcn = 0; + stop_rl = na->rl; + finished_build = FALSE; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { + a = ctx->attr; + m = ctx->mrec; + if (!a->lowest_vcn) + first_updated = TRUE; + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * contain @from_vcn. Also we do not need @from_vcn anymore, + * set it to 0 to make ntfs_attr_lookup enumerate attributes. + */ + if (from_vcn) { + LCN first_lcn; + + stop_vcn = sle64_to_cpu(a->lowest_vcn); + from_vcn = 0; + /* + * Check whether the first run we need to update is + * the last run in runlist, if so, then deallocate + * all attrubute extents starting this one. + */ + first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); + if (first_lcn == LCN_EINVAL) { + errno = EIO; + ntfs_log_perror("Bad runlist"); + goto put_err_out; + } + if (first_lcn == LCN_ENOENT || + first_lcn == LCN_RL_NOT_MAPPED) + finished_build = TRUE; + } + + /* + * Check whether we finished mapping pairs build, if so mark + * extent as need to delete (by setting highest vcn to + * NTFS_VCN_DELETE_MARK (-2), we shall check it later and + * delete extent) and continue search. + */ + if (finished_build) { + ntfs_log_trace("Mark attr 0x%x for delete in inode " + "%lld.\n", (unsigned)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + continue; + } + + switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) { + case -1: return -1; + case -2: goto retry; + case -3: goto put_err_out; + } + + /* + * Determine maximum possible length of mapping pairs, + * if we shall *not* expand space for mapping pairs. + */ + cur_max_mp_size = le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset); + /* + * Determine maximum possible length of mapping pairs in the + * current mft record, if we shall expand space for mapping + * pairs. + */ + exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; + /* Get the size for the rest of mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl, + stop_vcn, exp_max_mp_size); + if (mp_size <= 0) { + ntfs_log_perror("%s: get MP size failed", __FUNCTION__); + goto put_err_out; + } + /* Test mapping pairs for fitting in the current mft record. */ + if (mp_size > exp_max_mp_size) { + /* + * Mapping pairs of $ATTRIBUTE_LIST attribute must fit + * in the base mft record. Try to move out other + * attributes and try again. + */ + if (na->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, mp_size - + cur_max_mp_size)) { + ntfs_log_perror("Attribute list is too " + "big. Defragment the " + "volume\n"); + return -1; + } + goto retry; + } + + /* Add attribute list if it isn't present, and retry. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(base_ni)) { + ntfs_log_perror("Can not add attrlist"); + return -1; + } + goto retry; + } + + /* + * Set mapping pairs size to maximum possible for this + * mft record. We shall write the rest of mapping pairs + * to another MFT records. + */ + mp_size = exp_max_mp_size; + } + + /* Change space for mapping pairs if we need it. */ + if (((mp_size + 7) & ~7) != cur_max_mp_size) { + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->mapping_pairs_offset) + + mp_size)) { + errno = EIO; + ntfs_log_perror("Failed to resize attribute"); + goto put_err_out; + } + } + + /* Update lowest vcn. */ + a->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + if ((ctx->ntfs_ino->nr_extents == -1 || + NInoAttrList(ctx->ntfs_ino)) && + ctx->attr->type != AT_ATTRIBUTE_LIST) { + ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_attrlist_mark_dirty(ctx->ntfs_ino); + } + + /* + * Generate the new mapping pairs array directly into the + * correct destination, i.e. the attribute record itself. + */ + if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( + a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl)) + finished_build = TRUE; + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (!finished_build && errno != ENOSPC) { + ntfs_log_perror("Failed to build mapping pairs"); + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); + goto put_err_out; + } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } + + /* Deallocate not used attribute extents and return with success. */ + if (finished_build) { + ntfs_attr_reinit_search_ctx(ctx); + ntfs_log_trace("Deallocate marked extents.\n"); + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (sle64_to_cpu(ctx->attr->highest_vcn) != + NTFS_VCN_DELETE_MARK) + continue; + /* Remove unused attribute record. */ + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_perror("Could not remove unused attr"); + goto put_err_out; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (errno != ENOENT) { + ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); + goto put_err_out; + } + ntfs_log_trace("Deallocate done.\n"); + ntfs_attr_put_search_ctx(ctx); + goto ok; + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Allocate new MFT records for the rest of mapping pairs. */ + while (1) { + /* Calculate size of rest mapping pairs. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, + na->rl, stop_vcn, INT_MAX); + if (mp_size <= 0) { + ntfs_log_perror("%s: get mp size failed", __FUNCTION__); + goto put_err_out; + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Could not allocate new MFT record"); + goto put_err_out; + } + m = ni->mrec; + /* + * If mapping size exceed available space, set them to + * possible maximum. + */ + cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) - + (offsetof(ATTR_RECORD, compressed_size) + + (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) ? + sizeof(a->compressed_size) : 0)) - + ((sizeof(ntfschar) * na->name_len + 7) & ~7); + if (mp_size > cur_max_mp_size) + mp_size = cur_max_mp_size; + /* Add attribute extent to new record. */ + err = ntfs_non_resident_attr_record_add(ni, na->type, + na->name, na->name_len, stop_vcn, mp_size, + na->data_flags); + if (err == -1) { + err = errno; + ntfs_log_perror("Could not add attribute extent"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Could not free MFT record"); + errno = err; + goto put_err_out; + } + a = (ATTR_RECORD*)((u8*)m + err); + + err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl); + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (err < 0 && errno != ENOSPC) { + err = errno; + ntfs_log_perror("Failed to build MP"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Couldn't free MFT record"); + errno = err; + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + ntfs_inode_mark_dirty(ni); + /* All mapping pairs has been written. */ + if (!err) + break; + } +ok: + ret = 0; +out: + return ret; +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + goto out; +} +#undef NTFS_VCN_DELETE_MARK + +/** + * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute + * @na: non-resident ntfs open attribute for which we need update + * @from_vcn: update runlist starting this VCN + * + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + * + * FIXME: This function does not update sparse bit and compressed size correctly + * if called with @from_vcn != 0. + * + * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments passed. + * ENOMEM - Not enough memory to complete operation. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST + * or there is no free MFT records left to allocate. + */ +int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + */ +static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) +{ + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + VCN first_free_vcn; + s64 nr_freed_clusters; + int err; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long) + na->ni->mft_no, na->type, (long long)newsize); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding minimum size + * against @newsize and fail if @newsize is too small. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ERANGE) { + ntfs_log_trace("Eeek! Size bounds check failed. " + "Aborting...\n"); + } else if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* The first cluster outside the new allocation. */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only deallocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " + "failed.\n"); + return -1; + } + /* Deallocate all clusters starting with the first free one. */ + nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, + -1); + if (nr_freed_clusters < 0) { + ntfs_log_trace("Eeek! Freeing of clusters failed. " + "Aborting...\n"); + return -1; + } + + /* Truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { + /* + * Failed to truncate the runlist, so just throw it + * away, it will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_trace("Eeek! Run list truncation failed.\n"); + return -1; + } + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; + /* Write mapping pairs for new runlist. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { + ntfs_log_trace("Eeek! Mapping pairs update failed. " + "Leaving inconstant metadata. " + "Run chkdsk.\n"); + return -1; + } + } + + /* Get the first attribute record. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " + "Leaving inconstant metadata.\n"); + goto put_err_out; + } + + /* Update data and initialized size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + if (newsize < na->initialized_size) { + na->initialized_size = newsize; + ctx->attr->initialized_size = cpu_to_sle64(newsize); + } + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + + /* If the attribute now has zero size, make it resident. */ + if (!newsize) { + if (ntfs_attr_make_resident(na, ctx)) { + /* If couldn't make resident, just continue. */ + if (errno != EPERM) + ntfs_log_error("Failed to make attribute " + "resident. Leaving as is...\n"); + } + } + + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to expand + * @newsize: new size (in bytes) to which to expand the attribute + * + * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, + * by allocating new clusters. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + LCN lcn_seek_from; + VCN first_free_vcn; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + runlist *rl, *rln; + s64 org_alloc_size; + int err; + + ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize, (long long)na->data_size); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding maximum size + * against @newsize and fail if @newsize is too big. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ENOENT) + errno = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + return -1; + } + + if (na->type == AT_DATA) + NAttrSetDataAppending(na); + /* Save for future use. */ + org_alloc_size = na->allocated_size; + /* The first cluster outside the new allocation. */ + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only allocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { +#if PARTIAL_RUNLIST_UPDATING + s64 start_update; + + /* + * Update from the last previously allocated run, + * as we may have to expand an existing hole. + */ + start_update = na->allocated_size >> vol->cluster_size_bits; + if (start_update) + start_update--; + if (ntfs_attr_map_partial_runlist(na, start_update)) { + ntfs_log_perror("failed to map partial runlist"); + return -1; + } +#else + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); + return -1; + } +#endif + + /* + * If we extend $DATA attribute on NTFS 3+ volume, we can add + * sparse runs instead of real allocation of clusters. + */ + if ((na->type == AT_DATA) && (vol->major_ver >= 3) + && (holes != HOLES_NO)) { + rl = ntfs_malloc(0x1000); + if (!rl) + return -1; + + rl[0].vcn = (na->allocated_size >> + vol->cluster_size_bits); + rl[0].lcn = LCN_HOLE; + rl[0].length = first_free_vcn - + (na->allocated_size >> vol->cluster_size_bits); + rl[1].vcn = first_free_vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + } else { + /* + * Determine first after last LCN of attribute. + * We will start seek clusters from this LCN to avoid + * fragmentation. If there are no valid LCNs in the + * attribute let the cluster allocator choose the + * starting LCN. + */ + lcn_seek_from = -1; + if (na->rl->length) { + /* Seek to the last run list element. */ + for (rl = na->rl; (rl + 1)->length; rl++) + ; + /* + * If the last LCN is a hole or similar seek + * back to last valid LCN. + */ + while (rl->lcn < 0 && rl != na->rl) + rl--; + /* + * Only set lcn_seek_from it the LCN is valid. + */ + if (rl->lcn >= 0) + lcn_seek_from = rl->lcn + rl->length; + } + + rl = ntfs_cluster_alloc(vol, na->allocated_size >> + vol->cluster_size_bits, first_free_vcn - + (na->allocated_size >> + vol->cluster_size_bits), lcn_seek_from, + DATA_ZONE); + if (!rl) { + ntfs_log_perror("Cluster allocation failed " + "(%lld)", + (long long)first_free_vcn - + ((long long)na->allocated_size >> + vol->cluster_size_bits)); + return -1; + } + } + + /* Append new clusters to attribute runlist. */ + rln = ntfs_runlists_merge(na->rl, rl); + if (!rln) { + /* Failed, free just allocated clusters. */ + err = errno; + ntfs_log_perror("Run list merge failed"); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + errno = err; + return -1; + } + na->rl = rln; + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; + /* Write mapping pairs for new runlist. */ +#if PARTIAL_RUNLIST_UPDATING + if (ntfs_attr_update_mapping_pairs_i(na, start_update, holes)) { +#else + if (ntfs_attr_update_mapping_pairs(na, 0)) { +#endif + err = errno; + ntfs_log_perror("Mapping pairs update failed"); + goto rollback; + } + } + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + err = errno; + if (na->allocated_size == org_alloc_size) { + errno = err; + return -1; + } else + goto rollback; + } + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + ntfs_log_perror("Lookup of first attribute extent failed"); + if (err == ENOENT) + err = EIO; + if (na->allocated_size != org_alloc_size) { + ntfs_attr_put_search_ctx(ctx); + goto rollback; + } else + goto put_err_out; + } + + /* Update data size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +rollback: + /* Free allocated clusters. */ + if (ntfs_cluster_free(vol, na, org_alloc_size >> + vol->cluster_size_bits, -1) < 0) { + err = EIO; + ntfs_log_perror("Leaking clusters"); + } + /* Now, truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, org_alloc_size >> + vol->cluster_size_bits)) { + /* + * Failed to truncate the runlist, so just throw it away, it + * will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); + } else { + /* Prepare to mapping pairs update. */ + na->allocated_size = org_alloc_size; + /* Restore mapping pairs. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> + vol->cluster_size_bits*/)) { + ntfs_log_perror("Failed to restore old mapping pairs"); + } + } + errno = err; + return -1; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + + +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_non_resident_attr_expand_i(na, newsize, holes); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_truncate - resize an ntfs attribute + * @na: open ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * @holes: how to create a hole if expanding + * + * Change the size of an open ntfs attribute @na to @newsize bytes. If the + * attribute is made bigger and the attribute is resident the newly + * "allocated" space is cleared and if the attribute is non-resident the + * newly allocated space is marked as not initialised and no real allocation + * on disk is performed. + * + * On success return 0. + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EOPNOTSUPP - The desired resize is not implemented yet. + * EACCES - Encrypted attribute. + */ +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret = STATUS_ERROR; + s64 fullsize; + BOOL compressed; + + if (!na || newsize < 0 || + (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return STATUS_ERROR; + } + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + if (na->data_size == newsize) { + ntfs_log_trace("Size is already ok\n"); + ret = STATUS_OK; + goto out; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (na->data_flags & ATTR_IS_ENCRYPTED) { + errno = EACCES; + ntfs_log_trace("Cannot truncate encrypted attribute\n"); + goto out; + } + /* + * TODO: Implement making handling of compressed attributes. + * Currently we can only expand the attribute or delete it, + * and only for ATTR_IS_COMPRESSED. This is however possible + * for resident attributes when there is no open fuse context + * (important case : $INDEX_ROOT:$I30) + */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + if (compressed + && NAttrNonResident(na) + && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { + errno = EOPNOTSUPP; + ntfs_log_perror("Failed to truncate compressed attribute"); + goto out; + } + if (NAttrNonResident(na)) { + /* + * For compressed data, the last block must be fully + * allocated, and we do not know the size of compression + * block until the attribute has been made non-resident. + * Moreover we can only process a single compression + * block at a time (from where we are about to write), + * so we silently do not allocate more. + * + * Note : do not request upsizing of compressed files + * unless being able to face the consequences ! + */ + if (compressed && newsize && (newsize > na->data_size)) + fullsize = (na->initialized_size + | (na->compression_block_size - 1)) + 1; + else + fullsize = newsize; + if (fullsize > na->data_size) + ret = ntfs_non_resident_attr_expand(na, fullsize, + holes); + else + ret = ntfs_non_resident_attr_shrink(na, fullsize); + } else + ret = ntfs_resident_attr_resize(na, newsize); +out: + ntfs_log_leave("Return status %d\n", ret); + return ret; +} + +/* + * Resize an attribute, creating a hole if relevant + */ + +int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +{ + int r; + + r = ntfs_attr_truncate_i(na, newsize, HOLES_OK); + NAttrClearDataAppending(na); + NAttrClearBeingNonResident(na); + return (r); +} + +/* + * Resize an attribute, avoiding hole creation + */ + +int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_NO)); +} + +/* + * Stuff a hole in a compressed file + * + * An unallocated hole must be aligned on compression block size. + * If needed current block and target block are stuffed with zeroes. + * + * Returns 0 if succeeded, + * -1 if it failed (as explained in errno) + */ + +static int stuff_hole(ntfs_attr *na, const s64 pos) +{ + s64 size; + s64 begin_size; + s64 end_size; + char *buf; + int ret; + + ret = 0; + /* + * If the attribute is resident, the compression block size + * is not defined yet and we can make no decision. + * So we first try resizing to the target and if the + * attribute is still resident, we're done + */ + if (!NAttrNonResident(na)) { + ret = ntfs_resident_attr_resize(na, pos); + if (!ret && !NAttrNonResident(na)) + na->initialized_size = na->data_size = pos; + } + if (!ret && NAttrNonResident(na)) { + /* does the hole span over several compression block ? */ + if ((pos ^ na->initialized_size) + & ~(na->compression_block_size - 1)) { + begin_size = ((na->initialized_size - 1) + | (na->compression_block_size - 1)) + + 1 - na->initialized_size; + end_size = pos & (na->compression_block_size - 1); + size = (begin_size > end_size ? begin_size : end_size); + } else { + /* short stuffing in a single compression block */ + begin_size = size = pos - na->initialized_size; + end_size = 0; + } + if (size) + buf = (char*)ntfs_malloc(size); + else + buf = (char*)NULL; + if (buf || !size) { + memset(buf,0,size); + /* stuff into current block */ + if (begin_size + && (ntfs_attr_pwrite(na, + na->initialized_size, begin_size, buf) + != begin_size)) + ret = -1; + /* create an unstuffed hole */ + if (!ret + && ((na->initialized_size + end_size) < pos) + && ntfs_non_resident_attr_expand(na, + pos - end_size, HOLES_OK)) + ret = -1; + else + na->initialized_size + = na->data_size = pos - end_size; + /* stuff into the target block */ + if (!ret && end_size + && (ntfs_attr_pwrite(na, + na->initialized_size, end_size, buf) + != end_size)) + ret = -1; + if (buf) + free(buf); + } else + ret = -1; + } + /* make absolutely sure we have reached the target */ + if (!ret && (na->initialized_size != pos)) { + ntfs_log_error("Failed to stuff a compressed file" + "target %lld reached %lld\n", + (long long)pos, (long long)na->initialized_size); + errno = EIO; + ret = -1; + } + return (ret); +} + +/** + * ntfs_attr_readall - read the entire data from an ntfs attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * @data_size: if non-NULL then store here the data size + * + * This function will read the entire content of an ntfs attribute. + * If @name is AT_UNNAMED then look specifically for an unnamed attribute. + * If @name is NULL then the attribute could be either named or not. + * In both those cases @name_len is not used at all. + * + * On success a buffer is allocated with the content of the attribute + * and which needs to be freed when it's not needed anymore. If the + * @data_size parameter is non-NULL then the data size is set there. + * + * On error NULL is returned with errno set to the error code. + */ +void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size) +{ + ntfs_attr *na; + void *data, *ret = NULL; + s64 size; + + ntfs_log_enter("Entering\n"); + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + ntfs_log_perror("ntfs_attr_open failed"); + goto err_exit; + } + data = ntfs_malloc(na->data_size); + if (!data) + goto out; + + size = ntfs_attr_pread(na, 0, na->data_size, data); + if (size != na->data_size) { + ntfs_log_perror("ntfs_attr_pread failed"); + free(data); + goto out; + } + ret = data; + if (data_size) + *data_size = size; +out: + ntfs_attr_close(na); +err_exit: + ntfs_log_leave("\n"); + return ret; +} + +/* + * Read some data from a data attribute + * + * Returns the amount of data read, negative if there was an error + */ + +int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)offset < (size_t)na->data_size) { + if (offset + size > (size_t)na->data_size) + size = na->data_size - offset; + while (size) { + res = ntfs_attr_pread(na, offset, size, buf + total); + if ((off_t)res < (off_t)size) + ntfs_log_perror("ntfs_attr_pread partial read " + "(%lld : %lld <> %d)", + (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +/* + * Write some data into a data attribute + * + * Returns the amount of data written, negative if there was an error + */ + +int ntfs_attr_data_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + res = ntfs_attr_pwrite(na, offset, size, buf + total); + if (res < (s64)size) + ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " + "%lld <> %d)", (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + int ret; + + ntfs_log_trace("Entering\n"); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return 0; + + ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, + ctx); + + ntfs_attr_put_search_ctx(ctx); + + return !ret; +} + +int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + ntfs_log_error("%s: NULL inode pointer", __FUNCTION__); + errno = EINVAL; + return -1; + } + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + /* do not log removal of non-existent stream */ + if (type != AT_DATA) { + ntfs_log_perror("Failed to open attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + } + return -1; + } + + ret = ntfs_attr_rm(na); + if (ret) + ntfs_log_perror("Failed to remove attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + ntfs_attr_close(na); + + return ret; +} + +/* Below macros are 32-bit ready. */ +#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) +#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) + +static u8 *ntfs_init_lut256(void) +{ + int i; + u8 *lut; + + lut = ntfs_malloc(256); + if (lut) + for(i = 0; i < 256; i++) + *(lut + i) = 8 - BITCOUNT(i); + return lut; +} + +s64 ntfs_attr_get_free_bits(ntfs_attr *na) +{ + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; + s64 nr_free = 0; + + lut = ntfs_init_lut256(); + if (!lut) + return -1; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; + + while (1) { + u32 *p; + br = ntfs_attr_pread(na, total, 65536, buf); + if (br <= 0) + break; + total += br; + p = (u32 *)buf + br / 4 - 1; + for (; (u8 *)p >= buf; p--) { + nr_free += lut[ *p & 255] + + lut[(*p >> 8) & 255] + + lut[(*p >> 16) & 255] + + lut[(*p >> 24) ]; + } + switch (br % 4) { + case 3: nr_free += lut[*(buf + br - 3)]; + case 2: nr_free += lut[*(buf + br - 2)]; + case 1: nr_free += lut[*(buf + br - 1)]; + } + } + free(buf); +out: + free(lut); + if (!total || br < 0) + return -1; + return nr_free; +} diff --git a/lib/libntfs/src/source/attrib.h b/lib/libntfs/src/source/attrib.h new file mode 100644 index 0000000..67fb957 --- /dev/null +++ b/lib/libntfs/src/source/attrib.h @@ -0,0 +1,394 @@ +/* + * attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006-2007 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRIB_H +#define _NTFS_ATTRIB_H + +/* Forward declarations */ +typedef struct _ntfs_attr ntfs_attr; +typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; + +#include "types.h" +#include "inode.h" +#include "unistr.h" +#include "runlist.h" +#include "volume.h" +#include "debug.h" +#include "logging.h" + +extern ntfschar AT_UNNAMED[]; +extern ntfschar STREAM_SDS[]; + +/* The little endian Unicode string $TXF_DATA as a global constant. */ +extern ntfschar TXF_DATA[10]; + +/** + * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn() + * + * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn(). + * + * TODO: Describe them. + */ +typedef enum { + LCN_HOLE = -1, /* Keep this as highest value or die! */ + LCN_RL_NOT_MAPPED = -2, + LCN_ENOENT = -3, + LCN_EINVAL = -4, + LCN_EIO = -5, +} ntfs_lcn_special_values; + +typedef enum { /* ways of processing holes when expanding */ + HOLES_NO, + HOLES_OK, + HOLES_DELAY +} hole_type; + +/** + * struct ntfs_attr_search_ctx - search context used in attribute search functions + * @mrec: buffer containing mft record to search + * @attr: attribute record in @mrec where to begin/continue search + * @is_first: if true lookup_attr() begins search with @attr, else after @attr + * + * Structure must be initialized to zero before the first call to one of the + * attribute search functions. Initialize @mrec to point to the mft record to + * search, and @attr to point to the first attribute within @mrec (not necessary + * if calling the _first() functions), and set @is_first to TRUE (not necessary + * if calling the _first() functions). + * + * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE, + * the search begins after @attr. This is so that, after the first call to one + * of the search attribute functions, we can call the function again, without + * any modification of the search context, to automagically get the next + * matching attribute. + */ +struct _ntfs_attr_search_ctx { + MFT_RECORD *mrec; + ATTR_RECORD *attr; + BOOL is_first; + ntfs_inode *ntfs_ino; + ATTR_LIST_ENTRY *al_entry; + ntfs_inode *base_ntfs_ino; + MFT_RECORD *base_mrec; + ATTR_RECORD *base_attr; +}; + +extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); +extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, + MFT_RECORD *mrec); +extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); + +extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type); + +/** + * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode + * @ctx: initialised attribute search context + * + * Syntactic sugar for walking attributes in an inode. + * + * Return 0 on success and -1 on error with errno set to the error code from + * ntfs_attr_lookup(). + * + * Example: When you want to enumerate all attributes in an open ntfs inode + * @ni, you can simply do: + * + * int err; + * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); + * if (!ctx) + * // Error code is in errno. Handle this case. + * while (!(err = ntfs_attrs_walk(ctx))) { + * ATTR_RECORD *attr = ctx->attr; + * // attr now contains the next attribute. Do whatever you want + * // with it and then just continue with the while loop. + * } + * if (err && errno != ENOENT) + * // Ooops. An error occurred! You should handle this case. + * // Now finished with all attributes in the inode. + */ +static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) +{ + return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx); +} + +/** + * struct ntfs_attr - ntfs in memory non-resident attribute structure + * @rl: if not NULL, the decompressed runlist + * @ni: base ntfs inode to which this attribute belongs + * @type: attribute type + * @name: Unicode name of the attribute + * @name_len: length of @name in Unicode characters + * @state: NTFS attribute specific flags describing this attribute + * @allocated_size: copy from the attribute record + * @data_size: copy from the attribute record + * @initialized_size: copy from the attribute record + * @compressed_size: copy from the attribute record + * @compression_block_size: size of a compression block (cb) + * @compression_block_size_bits: log2 of the size of a cb + * @compression_block_clusters: number of clusters per cb + * + * This structure exists purely to provide a mechanism of caching the runlist + * of an attribute. If you want to operate on a particular attribute extent, + * you should not be using this structure at all. If you want to work with a + * resident attribute, you should not be using this structure at all. As a + * fail-safe check make sure to test NAttrNonResident() and if it is false, you + * know you shouldn't be using this structure. + * + * If you want to work on a resident attribute or on a specific attribute + * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent) + * record, edit that, and then write back the mft record (or set the + * corresponding ntfs inode dirty for delayed write back). + * + * @rl is the decompressed runlist of the attribute described by this + * structure. Obviously this only makes sense if the attribute is not resident, + * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet + * @rl is NULL, so be prepared to cope with @rl == NULL. + * + * @ni is the base ntfs inode of the attribute described by this structure. + * + * @type is the attribute type (see layout.h for the definition of ATTR_TYPES), + * @name and @name_len are the little endian Unicode name and the name length + * in Unicode characters of the attribute, respectively. + * + * @state contains NTFS attribute specific flags describing this attribute + * structure. See ntfs_attr_state_bits above. + */ +struct _ntfs_attr { + runlist_element *rl; + ntfs_inode *ni; + ATTR_TYPES type; + ATTR_FLAGS data_flags; + ntfschar *name; + u32 name_len; + unsigned long state; + s64 allocated_size; + s64 data_size; + s64 initialized_size; + s64 compressed_size; + u32 compression_block_size; + u8 compression_block_size_bits; + u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ +}; + +/** + * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr + * structure + */ +typedef enum { + NA_Initialized, /* 1: structure is initialized. */ + NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_DataAppending, /* 1: Attribute is being appended to */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ +} ntfs_attr_state_bits; + +#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) +#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) +#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) + +#define NAttrInitialized(na) test_nattr_flag(na, Initialized) +#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) +#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) + +#define NAttrNonResident(na) test_nattr_flag(na, NonResident) +#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) +#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) + +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) + +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) + +#define NAttrDataAppending(na) test_nattr_flag(na, DataAppending) +#define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending) +#define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending) + +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) + +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ +extern void NAttrClear##func_name(ntfs_attr *na); + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) +#undef GenNAttrIno + +/** + * union attr_val - Union of all known attribute values + * + * For convenience. Used in the attr structure. + */ +typedef union { + u8 _default; /* Unnamed u8 to serve as default when just using + a_val without specifying any of the below. */ + STANDARD_INFORMATION std_inf; + ATTR_LIST_ENTRY al_entry; + FILE_NAME_ATTR filename; + OBJECT_ID_ATTR obj_id; + SECURITY_DESCRIPTOR_ATTR sec_desc; + VOLUME_NAME vol_name; + VOLUME_INFORMATION vol_inf; + DATA_ATTR data; + INDEX_ROOT index_root; + INDEX_BLOCK index_blk; + BITMAP_ATTR bmp; + REPARSE_POINT reparse; + EA_INFORMATION ea_inf; + EA_ATTR ea; + PROPERTY_SET property_set; + LOGGED_UTILITY_STREAM logged_util_stream; + EFS_ATTR_HEADER efs; +} attr_val; + +extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, const BOOL encrypted, + const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit); + + /* warning : in the following "name" has to be freeable */ + /* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */ +extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern void ntfs_attr_close(ntfs_attr *na); + +extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b); +extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, + const void *b); +extern int ntfs_attr_pclose(ntfs_attr *na); + +extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size); + +extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, + const s64 bk_cnt, const u32 bk_size, void *dst); +extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, + s64 bk_cnt, const u32 bk_size, void *src); + +extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); +extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); + +extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); +extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); + +extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, + const ATTR_TYPES type, const s64 size); +extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, + const ATTR_TYPES type); +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); +extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); + +extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, u32 size, + ATTR_FLAGS flags); +extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + ATTR_FLAGS flags); +extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, u8 *val, s64 size); +extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask); +extern int ntfs_attr_rm(ntfs_attr *na); + +extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); + +extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size); + +extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); +extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); + +extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); + +extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); +extern int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize); + +/** + * get_attribute_value_length - return the length of the value of an attribute + * @a: pointer to a buffer containing the attribute record + * + * Return the byte size of the attribute value of the attribute @a (as it + * would be after eventual decompression and filling in of holes if sparse). + * If we return 0, check errno. If errno is 0 the actual length was 0, + * otherwise errno describes the error. + * + * FIXME: Describe possible errnos. + */ +extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); + +/** + * get_attribute_value - return the attribute value of an attribute + * @vol: volume on which the attribute is present + * @a: attribute to get the value of + * @b: destination buffer for the attribute value + * + * Make a copy of the attribute value of the attribute @a into the destination + * buffer @b. Note, that the size of @b has to be at least equal to the value + * returned by get_attribute_value_length(@a). + * + * Return number of bytes copied. If this is zero check errno. If errno is 0 + * then nothing was read due to a zero-length attribute value, otherwise + * errno describes the error. + */ +extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b); + +extern void ntfs_attr_name_free(char **name); +extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); +extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); +extern int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset); +extern int ntfs_attr_data_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset); + +#endif /* defined _NTFS_ATTRIB_H */ + diff --git a/lib/libntfs/src/source/attrib_frag.c b/lib/libntfs/src/source/attrib_frag.c new file mode 100644 index 0000000..649986f --- /dev/null +++ b/lib/libntfs/src/source/attrib_frag.c @@ -0,0 +1,369 @@ +/** + * attrib.c - Attribute handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "compat.h" +#include "attrib.h" +#include "attrlist.h" +#include "device.h" +#include "mft.h" +#include "debug.h" +#include "mst.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "dir.h" +#include "compress.h" +#include "bitmap.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" +#include "ntfs.h" +#include "ntfs_frag.h" + +static inline u8 size_to_shift(u32 size) +{ + u8 ret = 0; + while (size) + { + ret++; + size >>= 1; + } + return ret - 1; +} + +/** + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 offset, + _ntfs_frag_append_t append_fragment, void *callback_data) +{ + u64 b = offset; + s64 br, to_read, ofs, total, total2, max_read, max_init; + ntfs_volume *vol; + runlist_element *rl; + //u16 efs_padding_length; + + /* Sanity checking arguments is done in ntfs_attr_pread(). */ + + if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) + { + //return -1; // no compressed files + return -31; + /* + if ((na->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + return ntfs_compressed_attr_pread(na, pos, count, b); + else { + // compression mode not supported + errno = EOPNOTSUPP; + return -1; + } + */ + } + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option + */ + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + //return -1; + return -32; + } + + if (!count) + return 0; + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + //return -1; + return -33; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) + return 0; + count = max_read - pos; + } + /* If it is a resident attribute, get the value from the mft record. */ + if (!NAttrNonResident(na)) + { + return -34; // No resident files + /* + ntfs_attr_search_ctx *ctx; + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { +res_err_out: + ntfs_attr_put_search_ctx(ctx); + return -1; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto res_err_out; + } + memcpy(b, val + pos, count); + ntfs_attr_put_search_ctx(ctx); + return count; + */ + } + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > max_init) { + if (pos >= max_init) { + //memset(b, 0, count); + return count; + } + total2 = pos + count - max_init; + count -= total2; + //memset((u8*)b + count, 0, total2); + } + /* + * for encrypted non-resident attributes with efs_raw set + * the last two bytes aren't read from disk but contain + * the number of padding bytes so original size can be + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) + { + return -35; //No encrypted files + /* + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + */ + } + + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds read. + * However, we already truncated the read to the data_size, + * so getting this here is an error. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } + //return -1; + return -36; + } + /* + * Gather the requested data into the linear destination buffer. Note, + * a partial final vcn is taken care of by the @count capping of read + * length. + */ + ofs = pos - (rl->vcn << vol->cluster_size_bits); + for (; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #2", + __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + /* It is a hole, just zero the matching @b range. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + //memset(b, 0, to_read); + /* Update progress counters. */ + total += to_read; + count -= to_read; + b = b + to_read; + continue; + } + /* It is a real lcn, read it into @dst. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); + /* + br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + + ofs, to_read, b); + */ + br = to_read; + // convert to sectors unit + u32 shift = size_to_shift(na->ni->vol->sector_size); + u32 off_sec = b >> shift; + u32 sector = ((rl->lcn << vol->cluster_size_bits) + ofs) >> shift; + u32 count_sec = to_read >> shift; + int ret; + ret = append_fragment(callback_data, off_sec, sector, count_sec); + if (ret) { + if (ret < 0) return ret; + return -50; + } + /* If everything ok, update progress counters and continue. */ + if (br > 0) { + total += br; + count -= br; + b = b + br; + } + if (br == to_read) + continue; + /* If the syscall was interrupted, try again. */ + if (br == (s64)-1 && errno == EINTR) + goto retry; + if (total) + return total; + if (!br) + errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); + //return -1; + return -38; + } + /* Finally, return the number of bytes read. */ + return total + total2; +rl_err_out: + if (total) + return total; + errno = EIO; + //return -1; + return -39; +} + + +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_getfragments(ntfs_attr *na, const s64 pos, s64 count, u64 offset, + _ntfs_frag_append_t append_fragment, void *callback_data) +{ + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !callback_data || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, callback_data, (long long)pos, + (long long)count); + //return -1; + return -21; + } + + /* + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + */ + + ret = ntfs_attr_getfragments_i(na, pos, count, offset, + append_fragment, callback_data); + + //ntfs_log_leave("\n"); + return ret; +} + diff --git a/lib/libntfs/src/source/attrlist.c b/lib/libntfs/src/source/attrlist.c new file mode 100644 index 0000000..9c62f31 --- /dev/null +++ b/lib/libntfs/src/source/attrlist.c @@ -0,0 +1,314 @@ +/** + * attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "attrlist.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_attrlist_need - check whether inode need attribute list + * @ni: opened ntfs inode for which perform check + * + * Check whether all are attributes belong to one MFT record, in that case + * attribute list is not needed. + * + * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set + * to the error code. If function succeed errno set to 0. The following error + * codes are defined: + * EINVAL - Invalid arguments passed to function or attribute haven't got + * attribute list. + */ +int ntfs_attrlist_need(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Inode haven't got attribute list.\n"); + errno = EINVAL; + return -1; + } + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_attrlist_entry_add - add an attribute list attribute entry + * @ni: opened ntfs inode, which contains that attribute + * @attr: attribute record to add to attribute list + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed to function. + * ENOMEM - Not enough memory to allocate necessary buffers. + * EIO - I/O error occurred or damaged filesystem. + * EEXIST - Such attribute already present in attribute list. + */ +int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ATTR_LIST_ENTRY *ale; + MFT_REF mref; + ntfs_attr *na = NULL; + ntfs_attr_search_ctx *ctx; + u8 *new_al; + int entry_len, entry_offset, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, + (unsigned) le32_to_cpu(attr->type)); + + if (!ni || !attr) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Determine size and allocate memory for new attribute list. */ + entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + attr->name_length + 7) & ~7; + new_al = ntfs_calloc(ni->attr_list_size + entry_len); + if (!new_al) + return -1; + + /* Find place for the new entry. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) + ((u8*)attr + le16_to_cpu(attr->name_offset)) : + AT_UNNAMED, attr->name_length, CASE_SENSITIVE, + (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : + 0, (attr->non_resident) ? NULL : ((u8*)attr + + le16_to_cpu(attr->value_offset)), (attr->non_resident) ? + 0 : le32_to_cpu(attr->value_length), ctx)) { + /* Found some extent, check it to be before new extent. */ + if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { + err = EEXIST; + ntfs_log_trace("Such attribute already present in the " + "attribute list.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* Add new entry after this extent. */ + ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + } else { + /* Check for real errors. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* No previous extents found. */ + ale = ctx->al_entry; + } + /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ + ntfs_attr_put_search_ctx(ctx); + + /* Determine new entry offset. */ + entry_offset = ((u8 *)ale - ni->attr_list); + /* Set pointer to new entry. */ + ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); + /* Zero it to fix valgrind warning. */ + memset(ale, 0, entry_len); + /* Form new entry. */ + ale->type = attr->type; + ale->length = cpu_to_le16(entry_len); + ale->name_length = attr->name_length; + ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); + if (attr->non_resident) + ale->lowest_vcn = attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = mref; + ale->instance = attr->instance; + memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), + attr->name_length * sizeof(ntfschar)); + + /* Resize $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, ni->attr_list, entry_offset); + memcpy(new_al + entry_offset + entry_len, ni->attr_list + + entry_offset, ni->attr_list_size - entry_offset); + + /* Set new runlist. */ + free(ni->attr_list); + ni->attr_list = new_al; + ni->attr_list_size = ni->attr_list_size + entry_len; + NInoAttrListSetDirty(ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} + +/** + * ntfs_attrlist_entry_rm - remove an attribute list attribute entry + * @ctx: attribute search context describing the attribute list entry + * + * Remove the attribute list entry @ctx->al_entry from the attribute list. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) +{ + u8 *new_al; + int new_al_len; + ntfs_inode *base_ni; + ntfs_attr *na; + ATTR_LIST_ENTRY *ale; + int err; + + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + ale = ctx->al_entry; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->al_entry->type), + (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); + + if (!NInoAttrList(base_ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Allocate memory for new attribute list. */ + new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); + new_al = ntfs_calloc(new_al_len); + if (!new_al) + return -1; + + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); + memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( + ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); + + /* Set new runlist. */ + free(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(base_ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} diff --git a/lib/libntfs/src/source/attrlist.h b/lib/libntfs/src/source/attrlist.h new file mode 100644 index 0000000..2952e48 --- /dev/null +++ b/lib/libntfs/src/source/attrlist.h @@ -0,0 +1,51 @@ +/* + * attrlist.h - Exports for attribute list attribute handling. + * Originated from Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRLIST_H +#define _NTFS_ATTRLIST_H + +#include "attrib.h" + +extern int ntfs_attrlist_need(ntfs_inode *ni); + +extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); +extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); + +/** + * ntfs_attrlist_mark_dirty - set the attribute list dirty + * @ni: ntfs inode which base inode contain dirty attribute list + * + * Set the attribute list dirty so it is written out later (at the latest at + * ntfs_inode_close() time). + * + * This function cannot fail. + */ +static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + NInoAttrListSetDirty(ni->base_ni); + else + NInoAttrListSetDirty(ni); +} + +#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/lib/libntfs/src/source/bit_ops.h b/lib/libntfs/src/source/bit_ops.h new file mode 100644 index 0000000..762be0b --- /dev/null +++ b/lib/libntfs/src/source/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/lib/libntfs/src/source/bitmap.c b/lib/libntfs/src/source/bitmap.c new file mode 100644 index 0000000..65162a2 --- /dev/null +++ b/lib/libntfs/src/source/bitmap.c @@ -0,0 +1,300 @@ +/** + * bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2006 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_bit_set - set a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to set + * @new_value: value to set bit to (0 or 1) + * + * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. + */ +void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + if (!bitmap || new_value > 1) + return; + if (!new_value) + bitmap[bit >> 3] &= ~(1 << (bit & 7)); + else + bitmap[bit >> 3] |= (1 << (bit & 7)); +} + +/** + * ntfs_bit_get - get value of a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to get + * + * Get and return the value of the bit @bit in @bitmap (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get(const u8 *bitmap, const u64 bit) +{ + if (!bitmap) + return -1; + return (bitmap[bit >> 3] >> (bit & 7)) & 1; +} + +/** + * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it + * @bitmap: field of bits + * @bit: bit to get/set + * @new_value: value to set bit to (0 or 1) + * + * Return the value of the bit @bit and set it to @new_value (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + register u8 old_bit, shift; + + if (!bitmap || new_value > 1) + return -1; + shift = bit & 7; + old_bit = (bitmap[bit >> 3] >> shift) & 1; + if (new_value != old_bit) + bitmap[bit >> 3] ^= 1 << shift; + return old_bit; +} + +/** + * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na to @value, where @value is either 0 or 1. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, + s64 count, int value) +{ + s64 bufsize, br; + u8 *buf, *lastbyte_buf; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; + + if (!na || start_bit < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); + return -1; + } + + bit = start_bit & 7; + if (bit) + firstbyte = 1; + else + firstbyte = 0; + + /* Calculate the required buffer size in bytes, capping it at 8kiB. */ + bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; + if (bufsize > 8192) + bufsize = 8192; + + buf = ntfs_malloc(bufsize); + if (!buf) + return -1; + + /* Depending on @value, zero or set all bits in the allocated buffer. */ + memset(buf, value ? 0xff : 0, bufsize); + + /* If there is a first partial byte... */ + if (bit) { + /* read it in... */ + br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); + if (br != 1) { + if (br >= 0) + errno = EIO; + goto free_err_out; + } + /* and set or clear the appropriate bits in it. */ + while ((bit & 7) && count--) { + if (value) + *buf |= 1 << bit++; + else + *buf &= ~(1 << bit++); + } + /* Update @start_bit to the new position. */ + start_bit = (start_bit + 7) & ~7; + } + + /* Loop until @count reaches zero. */ + lastbyte = 0; + lastbyte_buf = NULL; + bit = count & 7; + do { + /* If there is a last partial byte... */ + if (count > 0 && bit) { + lastbyte_pos = ((count + 7) >> 3) + firstbyte; + if (!lastbyte_pos) { + // FIXME: Eeek! BUG! + ntfs_log_error("Lastbyte is zero. Leaving " + "inconsistent metadata.\n"); + errno = EIO; + goto free_err_out; + } + /* and it is in the currently loaded bitmap window... */ + if (lastbyte_pos <= bufsize) { + lastbyte_buf = buf + lastbyte_pos - 1; + + /* read the byte in... */ + br = ntfs_attr_pread(na, (start_bit + count) >> + 3, 1, lastbyte_buf); + if (br != 1) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); + goto free_err_out; + } + /* and set/clear the appropriate bits in it. */ + while (bit && count--) { + if (value) + *lastbyte_buf |= 1 << --bit; + else + *lastbyte_buf &= ~(1 << --bit); + } + /* We don't want to come back here... */ + bit = 0; + /* We have a last byte that we have handled. */ + lastbyte = 1; + } + } + + /* Write the prepared buffer to disk. */ + tmp = (start_bit >> 3) - firstbyte; + br = ntfs_attr_pwrite(na, tmp, bufsize, buf); + if (br != bufsize) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); + goto free_err_out; + } + + /* Update counters. */ + tmp = (bufsize - firstbyte - lastbyte) << 3; + if (firstbyte) { + firstbyte = 0; + /* + * Re-set the partial first byte so a subsequent write + * of the buffer does not have stale, incorrect bits. + */ + *buf = value ? 0xff : 0; + } + start_bit += tmp; + count -= tmp; + if (bufsize > (tmp = (count + 7) >> 3)) + bufsize = tmp; + + if (lastbyte && count != 0) { + // FIXME: Eeek! BUG! + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); + errno = EIO; + goto free_err_out; + } + } while (count > 0); + + ret = 0; + +free_err_out: + free(buf); + return ret; +} + +/** + * ntfs_bitmap_set_run - set a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_bitmap_clear_run - clear a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to clear + * @count: number of bits to clear + * + * Clear @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; +} + diff --git a/lib/libntfs/src/source/bitmap.h b/lib/libntfs/src/source/bitmap.h new file mode 100644 index 0000000..10b5f6c --- /dev/null +++ b/lib/libntfs/src/source/bitmap.h @@ -0,0 +1,96 @@ +/* + * bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BITMAP_H +#define _NTFS_BITMAP_H + +#include "types.h" +#include "attrib.h" + +/* + * NOTES: + * + * - Operations are 8-bit only to ensure the functions work both on little + * and big endian machines! So don't make them 32-bit ops! + * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1. + * - _Caller_ has to make sure that the bit to operate on is less than the + * size of the bitmap. + */ + +extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern char ntfs_bit_get(const u8 *bitmap, const u64 bit); +extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); +extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); + +/** + * ntfs_bitmap_set_bit - set a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to set + * + * Set the @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_set_run(na, bit, 1); +} + +/** + * ntfs_bitmap_clear_bit - clear a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to clear + * + * Clear @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_clear_run(na, bit, 1); +} + +/* + * rol32 - rotate a 32-bit value left + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/* + * ror32 - rotate a 32-bit value right + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +#endif /* defined _NTFS_BITMAP_H */ + diff --git a/lib/libntfs/src/source/bootsect.c b/lib/libntfs/src/source/bootsect.c new file mode 100644 index 0000000..e9bea37 --- /dev/null +++ b/lib/libntfs/src/source/bootsect.c @@ -0,0 +1,285 @@ +/** + * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2003-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "compat.h" +#include "bootsect.h" +#include "debug.h" +#include "logging.h" + +/** + * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector + * @b: buffer containing putative boot sector to analyze + * @silent: if zero, output progress messages to stderr + * + * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b + * must be at least 512 bytes in size. + * + * If @silent is zero, output progress messages to stderr. Otherwise, do not + * output any messages (except when configured with --enable-debug in which + * case warning/debug messages may be displayed). + * + * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. + */ +BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) +{ + u32 i; + BOOL ret = FALSE; + + ntfs_log_debug("Beginning bootsector check.\n"); + + ntfs_log_debug("Checking OEMid, NTFS signature.\n"); + if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ + ntfs_log_error("NTFS signature is missing.\n"); + goto not_ntfs; + } + + ntfs_log_debug("Checking bytes per sector.\n"); + if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || + le16_to_cpu(b->bpb.bytes_per_sector) > 4096) { + ntfs_log_error("Unexpected bytes per sector value (%d).\n", + le16_to_cpu(b->bpb.bytes_per_sector)); + goto not_ntfs; + } + + ntfs_log_debug("Checking sectors per cluster.\n"); + switch (b->bpb.sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + break; + default: + ntfs_log_error("Unexpected sectors per cluster value (%d).\n", + b->bpb.sectors_per_cluster); + goto not_ntfs; + } + + ntfs_log_debug("Checking cluster size.\n"); + i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * + b->bpb.sectors_per_cluster; + if (i > 65536) { + ntfs_log_error("Unexpected cluster size (%d).\n", i); + goto not_ntfs; + } + + ntfs_log_debug("Checking reserved fields are zero.\n"); + if (le16_to_cpu(b->bpb.reserved_sectors) || + le16_to_cpu(b->bpb.root_entries) || + le16_to_cpu(b->bpb.sectors) || + le16_to_cpu(b->bpb.sectors_per_fat) || + le32_to_cpu(b->bpb.large_sectors) || + b->bpb.fats) { + ntfs_log_error("Reserved fields aren't zero " + "(%d, %d, %d, %d, %d, %d).\n", + le16_to_cpu(b->bpb.reserved_sectors), + le16_to_cpu(b->bpb.root_entries), + le16_to_cpu(b->bpb.sectors), + le16_to_cpu(b->bpb.sectors_per_fat), + le32_to_cpu(b->bpb.large_sectors), + b->bpb.fats); + goto not_ntfs; + } + + ntfs_log_debug("Checking clusters per mft record.\n"); + if ((u8)b->clusters_per_mft_record < 0xe1 || + (u8)b->clusters_per_mft_record > 0xf7) { + switch (b->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per mft record " + "(%d).\n", b->clusters_per_mft_record); + goto not_ntfs; + } + } + + ntfs_log_debug("Checking clusters per index block.\n"); + if ((u8)b->clusters_per_index_record < 0xe1 || + (u8)b->clusters_per_index_record > 0xf7) { + switch (b->clusters_per_index_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per index record " + "(%d).\n", b->clusters_per_index_record); + goto not_ntfs; + } + } + + if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) + ntfs_log_debug("Warning: Bootsector has invalid end of sector " + "marker.\n"); + + ntfs_log_debug("Bootsector check completed successfully.\n"); + + ret = TRUE; +not_ntfs: + return ret; +} + +static const char *last_sector_error = +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; + +/** + * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector + * @vol: ntfs_volume to setup + * @bs: buffer containing ntfs boot sector to parse + * + * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the + * obtained values. + * + * Return 0 on success or -1 on error with errno set to the error code EINVAL. + */ +int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) +{ + s64 sectors; + u8 sectors_per_cluster; + s8 c; + + /* We return -1 with errno = EINVAL on error. */ + errno = EINVAL; + + vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); + vol->sector_size_bits = ffs(vol->sector_size) - 1; + ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); + ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); + /* + * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being + * below or equal the number_of_clusters) really belong in the + * ntfs_boot_sector_is_ntfs but in this way we can just do this once. + */ + sectors_per_cluster = bs->bpb.sectors_per_cluster; + ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); + if (sectors_per_cluster & (sectors_per_cluster - 1)) { + ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." + "\n", sectors_per_cluster); + return -1; + } + + sectors = sle64_to_cpu(bs->number_of_sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); + if (!sectors) { + ntfs_log_error("Volume size is set to zero.\n"); + return -1; + } + if (vol->dev->d_ops->seek(vol->dev, + (sectors - 1) << vol->sector_size_bits, + SEEK_SET) == -1) { + ntfs_log_perror("Failed to read last sector (%lld)", + (long long)sectors); + ntfs_log_error("%s", last_sector_error); + return -1; + } + + vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1); + + vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); + vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); + if (vol->mft_lcn > vol->nr_clusters || + vol->mftmirr_lcn > vol->nr_clusters) { + ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " + "greater than the number of clusters (%lld).\n", + (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, + (long long)vol->nr_clusters); + return -1; + } + + vol->cluster_size = sectors_per_cluster * vol->sector_size; + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("cluster_size (%d) is not a power of 2.\n", + vol->cluster_size); + return -1; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + /* + * Need to get the clusters per mft record and handle it if it is + * negative. Then calculate the mft_record_size. A value of 0x80 is + * illegal, thus signed char is actually ok! + */ + c = bs->clusters_per_mft_record; + ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); + ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); + ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); + /* + * When clusters_per_mft_record is negative, it means that it is to + * be taken to be the negative base 2 logarithm of the mft_record_size + * min bytes. Then: + * mft_record_size = 2^(-clusters_per_mft_record) bytes. + */ + if (c < 0) + vol->mft_record_size = 1 << -c; + else + vol->mft_record_size = c << vol->cluster_size_bits; + if (vol->mft_record_size & (vol->mft_record_size - 1)) { + ntfs_log_error("mft_record_size (%d) is not a power of 2.\n", + vol->mft_record_size); + return -1; + } + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); + ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); + /* Same as above for INDX record. */ + c = bs->clusters_per_index_record; + ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); + if (c < 0) + vol->indx_record_size = 1 << -c; + else + vol->indx_record_size = c << vol->cluster_size_bits; + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); + ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); + /* + * Work out the size of the MFT mirror in number of mft records. If the + * cluster size is less than or equal to the size taken by four mft + * records, the mft mirror stores the first four mft records. If the + * cluster size is bigger than the size taken by four mft records, the + * mft mirror contains as many mft records as will fit into one + * cluster. + */ + if (vol->cluster_size <= 4 * vol->mft_record_size) + vol->mftmirr_size = 4; + else + vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; + return 0; +} + diff --git a/lib/libntfs/src/source/bootsect.h b/lib/libntfs/src/source/bootsect.h new file mode 100644 index 0000000..a299e82 --- /dev/null +++ b/lib/libntfs/src/source/bootsect.h @@ -0,0 +1,42 @@ +/* + * bootsect.h - Exports for bootsector record handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BOOTSECT_H +#define _NTFS_BOOTSECT_H + +#include "types.h" +#include "volume.h" +#include "layout.h" + +/** + * ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume + * @b: buffer containing the boot sector + * + * This function checks the boot sector in @b for describing a valid ntfs + * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. + */ +extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b); +extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs); + +#endif /* defined _NTFS_BOOTSECT_H */ + diff --git a/lib/libntfs/src/source/cache.c b/lib/libntfs/src/source/cache.c new file mode 100644 index 0000000..2ad8d35 --- /dev/null +++ b/lib/libntfs/src/source/cache.c @@ -0,0 +1,606 @@ +/** + * cache.c : deal with LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "types.h" +#include "security.h" +#include "cache.h" +#include "misc.h" +#include "logging.h" + +/* + * General functions to deal with LRU caches + * + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. + * + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. + * + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. + */ + +/* + * Enter a new hash index, after a new record has been inserted + * + * Do not call when a record has been modified (with no key change) + */ + +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) +{ + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; + + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; + } else { + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Drop a hash index when a record is about to be deleted + */ + +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) +{ + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; + + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Fetch an entry from cache + * + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed + */ + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + } + } + cache->reads++; + } + return (current); +} + +/* + * Enter an inode number into cache + * returns the cache entry or NULL if not possible + */ + +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } + + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ + + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->payload, item->payload, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); +} + +/* + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion + */ + +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) +{ + struct CACHED_GENERIC *previous; + + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } + + +/* + * Invalidate entries in cache + * + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate + * + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. + */ + +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; + + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + current = current->next; + } + } + } + } + return (count); +} + +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) +{ + int count; + + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); +} + +/* + * Free memory allocated to a cache + */ + +static void ntfs_free_cache(struct CACHE_HEADER *cache) +{ + struct CACHED_GENERIC *entry; + + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); + } +} + +/* + * Create a cache + * + * Returns the cache header, or NULL if the cache could not be created + */ + +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) +{ + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; + + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; + + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; ifree_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); +} + +/* + * Create all LRU caches + * + * No error return, if creation is not possible, cacheing will + * just be not available + */ + +void ntfs_create_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); +#endif +#if CACHE_NIDATA_SIZE + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); +#endif +#if CACHE_LOOKUP_SIZE + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); +#endif + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); +#if CACHE_LEGACY_SIZE + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); +#endif +} + +/* + * Free all LRU caches + */ + +void ntfs_free_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + ntfs_free_cache(vol->xinode_cache); +#endif +#if CACHE_NIDATA_SIZE + ntfs_free_cache(vol->nidata_cache); +#endif +#if CACHE_LOOKUP_SIZE + ntfs_free_cache(vol->lookup_cache); +#endif + ntfs_free_cache(vol->securid_cache); +#if CACHE_LEGACY_SIZE + ntfs_free_cache(vol->legacy_cache); +#endif +} diff --git a/lib/libntfs/src/source/cache.h b/lib/libntfs/src/source/cache.h new file mode 100644 index 0000000..be63b1a --- /dev/null +++ b/lib/libntfs/src/source/cache.h @@ -0,0 +1,118 @@ +/* + * cache.h : deal with indexed LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_CACHE_H_ +#define _NTFS_CACHE_H_ + +#include "volume.h" + +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; +} ; + +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; +} ; + +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; +} ; + +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; +} ; + +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 +} ; + +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); + +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; +} ; + +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; +} ; + + /* cast to generic, avoiding gcc warnings */ +#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); + +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); + +#endif /* _NTFS_CACHE_H_ */ + diff --git a/lib/libntfs/src/source/cache2.c b/lib/libntfs/src/source/cache2.c new file mode 100644 index 0000000..872f1a5 --- /dev/null +++ b/lib/libntfs/src/source/cache2.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "cache2.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + NTFS_CACHE* cache; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + ntfs_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _NTFS_cache_destructor (NTFS_CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + _NTFS_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + ntfs_free (cache->cacheEntries[i].cache); + } + ntfs_free (cache->cacheEntries); + ntfs_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +{ + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + NTFS_CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + NTFS_CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + NTFS_CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = _NTFS_cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _NTFS_cache_flush (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _NTFS_cache_invalidate (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + _NTFS_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/lib/libntfs/src/source/cache2.h b/lib/libntfs/src/source/cache2.h new file mode 100644 index 0000000..21daca7 --- /dev/null +++ b/lib/libntfs/src/source/cache2.h @@ -0,0 +1,135 @@ +/* + NTFS_CACHE.h + The NTFS_CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This NTFS_CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the NTFS_CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _CACHE2_H +#define _CACHE2_H + +//#include "common.h" +//#include "disc.h" + +#include +#include +#include +#include +#include + +typedef struct { + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} NTFS_CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + NTFS_CACHE_ENTRY* cacheEntries; +} NTFS_CACHE; + +/* +Read data from a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE, zeroing the sector first +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the NTFS_CACHE +*/ +bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { +// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +/* +Write a full sector to the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { +// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE +*/ +bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); + +/* +Clear out the contents of the NTFS_CACHE without writing any dirty sectors first +*/ +void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); + +#endif // _CACHE_H + diff --git a/lib/libntfs/src/source/collate.c b/lib/libntfs/src/source/collate.c new file mode 100644 index 0000000..5f7a015 --- /dev/null +++ b/lib/libntfs/src/source/collate.c @@ -0,0 +1,271 @@ +/** + * collate.c - NTFS collation handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "index.h" +#include "collate.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" + +/** + * ntfs_collate_binary - Which of two binary objects should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + + ntfs_log_trace("Entering.\n"); + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 4) { + ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); + return NTFS_COLLATION_ERROR; + } + d1 = le32_to_cpup(data1); + d2 = le32_to_cpup(data2); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ + +static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + int len; + const le32 *p1, *p2; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) { + ntfs_log_error("data1_len or data2_len not valid\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while ((d1 == d2) && ((len -= 4) > 0)); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_security_hash - Which of two security descriptors + * should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * JPA compare two security hash keys made of two unsigned le32 + * + * Returns: -1, 0 or 1 depending of how the keys compare + */ +static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + const le32 *p1, *p2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 8) { + ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else { + p1++; + p2++; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else + rc = 0; + } + } + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_file_name - Which of two filenames should be listed first + * @vol: + * @data1: + * @data1_len: unused + * @data2: + * @data2_len: unused + * + * Description... + * + * Returns: + */ +static int ntfs_collate_file_name(ntfs_volume *vol, + const void *data1, const int data1_len __attribute__((unused)), + const void *data2, const int data2_len __attribute__((unused))) +{ + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; + int rc; + + ntfs_log_trace("Entering.\n"); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/* + * Get a pointer to appropriate collation function. + * + * Returns NULL if the needed function is not implemented + */ + +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) +{ + COLLATE collate; + + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; + } + return (collate); +} diff --git a/lib/libntfs/src/source/collate.h b/lib/libntfs/src/source/collate.h new file mode 100644 index 0000000..fe38383 --- /dev/null +++ b/lib/libntfs/src/source/collate.h @@ -0,0 +1,34 @@ +/* + * collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COLLATE_H +#define _NTFS_COLLATE_H + +#include "types.h" +#include "volume.h" + +#define NTFS_COLLATION_ERROR -2 + +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); + +#endif /* _NTFS_COLLATE_H */ diff --git a/lib/libntfs/src/source/compat.c b/lib/libntfs/src/source/compat.c new file mode 100644 index 0000000..63114a4 --- /dev/null +++ b/lib/libntfs/src/source/compat.c @@ -0,0 +1,250 @@ +/** + * compat.c - Tweaks for Windows compatibility + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compat.h" + +#ifndef HAVE_FFS +/** + * ffs - Find the first set bit in an int + * @x: + * + * Description... + * + * Returns: + */ +int ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; +} +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/daemon.c + */ +/* + * Copyright (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +int daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); +} +/* + * End: src/lib/libresolv2/common/bsd/daemon.c + *************************************************************/ +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/strsep.c + */ +/* + * Copyright (c) 1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +/* + * End: src/lib/libresolv2/common/bsd/strsep.c + *************************************************************/ +#endif /* HAVE_STRSEP */ + diff --git a/lib/libntfs/src/source/compat.h b/lib/libntfs/src/source/compat.h new file mode 100644 index 0000000..957752a --- /dev/null +++ b/lib/libntfs/src/source/compat.h @@ -0,0 +1,86 @@ +/* + * compat.h - Tweaks for Windows compatibility. + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2008-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPAT_H +#define _NTFS_COMPAT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifndef HAVE_FFS +extern int ffs(int i); +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif /* HAVE_STRSEP */ + +#ifdef WINDOWS + +#define HAVE_STDIO_H /* mimic config.h */ +#define HAVE_STDARG_H + +#define atoll _atoi64 +#define fdatasync commit +#define __inline__ inline +#define __attribute__(X) /*nothing*/ + +#else /* !defined WINDOWS */ + +#ifndef O_BINARY +#define O_BINARY 0 /* unix is binary by default */ +#endif + +#ifdef GEKKO + +#include "mem_allocate.h" + +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define major(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define minor(dev) ((unsigned int) ((dev) & MINORMASK)) +#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi)) +#define random rand + +#endif /* defined GEKKO */ + +#endif /* defined WINDOWS */ + +#endif /* defined _NTFS_COMPAT_H */ + diff --git a/lib/libntfs/src/source/compress.c b/lib/libntfs/src/source/compress.c new file mode 100644 index 0000000..aeb082d --- /dev/null +++ b/lib/libntfs/src/source/compress.c @@ -0,0 +1,1790 @@ +/** + * compress.c - Compressed attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2009-2011 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * A part of the compression algorithm is based on lzhuf.c whose header + * describes the roles of the original authors (with no apparent copyright + * notice, and according to http://home.earthlink.net/~neilbawd/pall.html + * this was put into public domain in 1988 by Haruhiko OKUMURA). + * + * LZHUF.C English version 1.0 + * Based on Japanese version 29-NOV-1988 + * LZSS coded by Haruhiko OKUMURA + * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI + * Edited and translated to English by Kenji RIKITAKE + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "runlist.h" +#include "compress.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +#undef le16_to_cpup +/* the standard le16_to_cpup() crashes for unaligned data on some processors */ +#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8)) + +/** + * enum ntfs_compression_constants - constants used in the compression code + */ +typedef enum { + /* Token types and access mask. */ + NTFS_SYMBOL_TOKEN = 0, + NTFS_PHRASE_TOKEN = 1, + NTFS_TOKEN_MASK = 1, + + /* Compression sub-block constants. */ + NTFS_SB_SIZE_MASK = 0x0fff, + NTFS_SB_SIZE = 0x1000, + NTFS_SB_IS_COMPRESSED = 0x8000, +} ntfs_compression_constants; + +struct COMPRESS_CONTEXT { + const unsigned char *inbuf; + int bufsize; + int size; + int rel; + int mxsz; + s16 head[256]; + s16 lson[NTFS_SB_SIZE]; + s16 rson[NTFS_SB_SIZE]; +} ; + +/* + * Search for the longest sequence matching current position + * + * A binary tree is maintained to locate all previously met sequences, + * and this function has to be called for all of them. + * + * This function is heavily used, it has to be optimized carefully + * + * Returns the size of the longest match, + * zero if no match is found. + */ + +static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i) +{ + s16 *prev; + int node; + register long j; + long maxpos; + long startj; + long bestj; + int bufsize; + int bestnode; + register const unsigned char *p1,*p2; + + p1 = pctx->inbuf; + node = pctx->head[p1[i] & 255]; + if (node >= 0) { + /* search the best match at current position */ + bestnode = node; + bufsize = pctx->bufsize; + /* restrict matches to the longest allowed sequence */ + maxpos = bufsize; + if ((i + pctx->mxsz) < maxpos) + maxpos = i + pctx->mxsz; + startj = i + 1 - maxpos; + bestj = startj; + /* make indexes relative to end of allowed position */ + p1 = &p1[maxpos]; + if (startj < 0) { + do { + /* indexes are negative */ + p2 = &p1[node - i]; + /* no need to compare the first byte */ + j = startj; + /* the second byte cannot lead to useful compression */ + if (p1[j] == p2[j]) { + j++; + if (j < 0) { + do { + } while ((p1[j] == p2[j]) + && (++j < 0)); + } + /* remember the match, if better */ + if (j > bestj) { + bestj = j; + bestnode = node; + } + } + /* walk in the tree in the right direction */ + if ((j < 0) && (p1[j] < p2[j])) + prev = &pctx->lson[node]; + else + prev = &pctx->rson[node]; + node = *prev; + /* stop if reaching a leaf or maximum length */ + } while ((node >= 0) && (j < 0)); + /* put the node into the tree if we reached a leaf */ + if (node < 0) + *prev = i; + } + /* done, return the best match */ + pctx->size = bestj + maxpos - i; + pctx->rel = bestnode - i; + } else { + pctx->head[p1[i] & 255] = i; + pctx->size = 0; + pctx->rel = 0; + } + return (pctx->size); +} + +/* + * Compress a 4096-byte block + * + * Returns a header of two bytes followed by the compressed data. + * If compression is not effective, the header and an uncompressed + * block is returned. + * + * Note : two bytes may be output before output buffer overflow + * is detected, so a 4100-bytes output buffer must be reserved. + * + * Returns the size of the compressed block, including the + * header (minimal size is 2, maximum size is 4098) + * 0 if an error has been met. + */ + +static unsigned int ntfs_compress_block(const char *inbuf, int bufsize, + char *outbuf) +{ + struct COMPRESS_CONTEXT *pctx; + int i; /* current position */ + int j; /* end of best match from current position */ + int k; /* end of best match from next position */ + int offs; /* offset to best match */ + int n; + int bp; /* bits to store offset */ + int mxoff; /* max match offset : 1 << bp */ + int mxsz2; + unsigned int xout; + unsigned int q; /* aggregated offset and size */ + int done; + char *ptag; /* location reserved for a tag */ + int tag; /* current value of tag */ + int ntag; /* count of bits still undefined in tag */ + + pctx = (struct COMPRESS_CONTEXT*)ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); + if (pctx) { + for (n=0; nlson[n] = pctx->rson[n] = -1; + for (n=0; n<256; n++) + pctx->head[n] = -1; + pctx->inbuf = (const unsigned char*)inbuf; + pctx->bufsize = bufsize; + xout = 2; + n = 0; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + tag = 0; + done = -1; + ntag = 8; + ptag = &outbuf[xout++]; + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* adjust the longest match we can output */ + while (mxoff < i) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + /* search the best match at current position */ + if (done < i) + do { + ntfs_best_match(pctx,++done); + } while (done < i); + j = i + pctx->size; + if ((j - i) > pctx->mxsz) + j = i + pctx->mxsz; + + if ((j - i) > 2) { + offs = pctx->rel; + /* check whether there is a better run at i+1 */ + ntfs_best_match(pctx,i+1); + done = i+1; + k = i + 1 + pctx->size; + mxsz2 = pctx->mxsz; + if (mxoff <= i) + mxsz2 = (pctx->mxsz + 2) >> 1; + if ((k - i) > mxsz2) + k = i + mxsz2; + if (k > (j + 1)) { + /* issue a single byte */ + outbuf[xout++] = inbuf[i]; + i++; + } else { + q = (~offs << (16 - bp)) + + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + i = j; + } + } else { + outbuf[xout++] = inbuf[i]; + i++; + } + /* store the tag if fully used */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; + } + } + /* store the last tag, if partially used */ + if (ntag == 8) + xout--; + else + *ptag = tag; + /* uncompressed must be full size, accept if better */ + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + memcpy(&outbuf[2],inbuf,bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize+2], 0, + NTFS_SB_SIZE - bufsize); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + free(pctx); + } else { + xout = 0; + errno = ENOMEM; + } + return (xout); +} + +/** + * ntfs_decompress - decompress a compression block into an array of pages + * @dest: buffer to which to write the decompressed data + * @dest_size: size of buffer @dest in bytes + * @cb_start: compression block to decompress + * @cb_size: size of compression block @cb_start in bytes + * + * This decompresses the compression block @cb_start into the destination + * buffer @dest. + * + * @cb_start is a pointer to the compression block which needs decompressing + * and @cb_size is the size of @cb_start in bytes (8-64kiB). + * + * Return 0 if success or -EOVERFLOW on error in the compressed stream. + */ +static int ntfs_decompress(u8 *dest, const u32 dest_size, + u8 *const cb_start, const u32 cb_size) +{ + /* + * Pointers into the compressed data, i.e. the compression block (cb), + * and the therein contained sub-blocks (sb). + */ + u8 *cb_end = cb_start + cb_size; /* End of cb. */ + u8 *cb = cb_start; /* Current position in cb. */ + u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ + u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ + /* Variables for uncompressed data / destination. */ + u8 *dest_end = dest + dest_size; /* End of dest buffer. */ + u8 *dest_sb_start; /* Start of current sub-block in dest. */ + u8 *dest_sb_end; /* End of current sb in dest. */ + /* Variables for tag and token parsing. */ + u8 tag; /* Current tag. */ + int token; /* Loop counter for the eight tokens in tag. */ + + ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); +do_next_sb: + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); + /* + * Have we reached the end of the compression block or the end of the + * decompressed data? The latter can happen for example if the current + * position in the compression block is one byte before its end so the + * first two checks do not detect it. + */ + if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) { + ntfs_log_debug("Completed. Returning success (0).\n"); + return 0; + } + /* Setup offset for the current sub-block destination. */ + dest_sb_start = dest; + dest_sb_end = dest + NTFS_SB_SIZE; + /* Check that we are still within allowed boundaries. */ + if (dest_sb_end > dest_end) + goto return_overflow; + /* Does the minimum size of a compressed sb overflow valid range? */ + if (cb + 6 > cb_end) + goto return_overflow; + /* Setup the current sub-block source pointers and validate range. */ + cb_sb_start = cb; + cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK) + + 3; + if (cb_sb_end > cb_end) + goto return_overflow; + /* Now, we are ready to process the current sub-block (sb). */ + if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) { + ntfs_log_debug("Found uncompressed sub-block.\n"); + /* This sb is not compressed, just copy it into destination. */ + /* Advance source position to first data byte. */ + cb += 2; + /* An uncompressed sb must be full size. */ + if (cb_sb_end - cb != NTFS_SB_SIZE) + goto return_overflow; + /* Copy the block and advance the source position. */ + memcpy(dest, cb, NTFS_SB_SIZE); + cb += NTFS_SB_SIZE; + /* Advance destination position to next sub-block. */ + dest += NTFS_SB_SIZE; + goto do_next_sb; + } + ntfs_log_debug("Found compressed sub-block.\n"); + /* This sb is compressed, decompress it into destination. */ + /* Forward to the first tag in the sub-block. */ + cb += 2; +do_next_tag: + if (cb == cb_sb_end) { + /* Check if the decompressed sub-block was not full-length. */ + if (dest < dest_sb_end) { + int nr_bytes = dest_sb_end - dest; + + ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); + /* Zero remainder and update destination position. */ + memset(dest, 0, nr_bytes); + dest += nr_bytes; + } + /* We have finished the current sub-block. */ + goto do_next_sb; + } + /* Check we are still in range. */ + if (cb > cb_sb_end || dest > dest_sb_end) + goto return_overflow; + /* Get the next tag and advance to first token. */ + tag = *cb++; + /* Parse the eight tokens described by the tag. */ + for (token = 0; token < 8; token++, tag >>= 1) { + u16 lg, pt, length, max_non_overlap; + register u16 i; + u8 *dest_back_addr; + + /* Check if we are done / still in range. */ + if (cb >= cb_sb_end || dest > dest_sb_end) + break; + /* Determine token type and parse appropriately.*/ + if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { + /* + * We have a symbol token, copy the symbol across, and + * advance the source and destination positions. + */ + *dest++ = *cb++; + /* Continue with the next token. */ + continue; + } + /* + * We have a phrase token. Make sure it is not the first tag in + * the sb as this is illegal and would confuse the code below. + */ + if (dest == dest_sb_start) + goto return_overflow; + /* + * Determine the number of bytes to go back (p) and the number + * of bytes to copy (l). We use an optimized algorithm in which + * we first calculate log2(current destination position in sb), + * which allows determination of l and p in O(1) rather than + * O(n). We just need an arch-optimized log2() function now. + */ + lg = 0; + for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) + lg++; + /* Get the phrase token into i. */ + pt = le16_to_cpup((le16*)cb); + /* + * Calculate starting position of the byte sequence in + * the destination using the fact that p = (pt >> (12 - lg)) + 1 + * and make sure we don't go too far back. + */ + dest_back_addr = dest - (pt >> (12 - lg)) - 1; + if (dest_back_addr < dest_sb_start) + goto return_overflow; + /* Now calculate the length of the byte sequence. */ + length = (pt & (0xfff >> lg)) + 3; + /* Verify destination is in range. */ + if (dest + length > dest_sb_end) + goto return_overflow; + /* The number of non-overlapping bytes. */ + max_non_overlap = dest - dest_back_addr; + if (length <= max_non_overlap) { + /* The byte sequence doesn't overlap, just copy it. */ + memcpy(dest, dest_back_addr, length); + /* Advance destination pointer. */ + dest += length; + } else { + /* + * The byte sequence does overlap, copy non-overlapping + * part and then do a slow byte by byte copy for the + * overlapping part. Also, advance the destination + * pointer. + */ + memcpy(dest, dest_back_addr, max_non_overlap); + dest += max_non_overlap; + dest_back_addr += max_non_overlap; + length -= max_non_overlap; + while (length--) + *dest++ = *dest_back_addr++; + } + /* Advance source position and continue with the next token. */ + cb += 2; + } + /* No tokens left in the current tag. Continue with the next tag. */ + goto do_next_tag; +return_overflow: + errno = EOVERFLOW; + ntfs_log_perror("Failed to decompress file"); + return -1; +} + +/** + * ntfs_is_cb_compressed - internal function, do not use + * + * This is a very specialised function determining if a cb is compressed or + * uncompressed. It is assumed that checking for a sparse cb has already been + * performed and that the cb is not sparse. It makes all sorts of other + * assumptions as well and hence it is not useful anywhere other than where it + * is used at the moment. Please, do not make this function available for use + * outside of compress.c as it is bound to confuse people and not do what they + * want. + * + * Return TRUE on errors so that the error will be detected later on in the + * code. Might be a bit confusing to debug but there really should never be + * errors coming from here. + */ +static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl, + VCN cb_start_vcn, int cb_clusters) +{ + /* + * The simplest case: the run starting at @cb_start_vcn contains + * @cb_clusters clusters which are all not sparse, thus the cb is not + * compressed. + */ +restart: + cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); + while (cb_clusters > 0) { + /* Go to the next run. */ + rl++; + /* Map the next runlist fragment if it is not mapped. */ + if (rl->lcn < LCN_HOLE || !rl->length) { + cb_start_vcn = rl->vcn; + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl || rl->lcn < LCN_HOLE || !rl->length) + return TRUE; + /* + * If the runs were merged need to deal with the + * resulting partial run so simply restart. + */ + if (rl->vcn < cb_start_vcn) + goto restart; + } + /* If the current run is sparse, the cb is compressed. */ + if (rl->lcn == LCN_HOLE) + return TRUE; + /* If the whole cb is not sparse, it is not compressed. */ + if (rl->length >= cb_clusters) + return FALSE; + cb_clusters -= rl->length; + }; + /* All cb_clusters were not sparse thus the cb is not compressed. */ + return FALSE; +} + +/** + * ntfs_compressed_attr_pread - read from a compressed attribute + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead. + * + * This function will read @count bytes starting at offset @pos from the + * compressed ntfs attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number + * is lower than @count this means that the read reached end of file or that + * an error was encountered during the read so that the read is partial. + * 0 means end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2; + u64 cb_size_mask; + VCN start_vcn, vcn, end_vcn; + ntfs_volume *vol; + runlist_element *rl; + u8 *dest, *cb, *cb_pos, *cb_end; + u32 cb_size; + int err; + ATTR_FLAGS data_flags; + FILE_ATTR_FLAGS compression; + unsigned int nr_cbs, cb_clusters; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + data_flags = na->data_flags; + compression = na->ni->flags & FILE_ATTR_COMPRESSED; + if (!na || !na->ni || !na->ni->vol || !b + || ((data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED) + || pos < 0 || count < 0) { + errno = EINVAL; + return -1; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na)) { + errno = EACCES; + return -1; + } + if (!count) + return 0; + /* Truncate reads beyond end of attribute. */ + if (pos + count > na->data_size) { + if (pos >= na->data_size) { + return 0; + } + count = na->data_size - pos; + } + /* If it is a resident attribute, simply use ntfs_attr_pread(). */ + if (!NAttrNonResident(na)) + return ntfs_attr_pread(na, pos, count, b); + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > na->initialized_size) { + if (pos >= na->initialized_size) { + memset(b, 0, count); + return count; + } + total2 = pos + count - na->initialized_size; + count -= total2; + memset((u8*)b + count, 0, total2); + } + vol = na->ni->vol; + cb_size = na->compression_block_size; + cb_size_mask = cb_size - 1UL; + cb_clusters = na->compression_block_clusters; + + /* Need a temporary buffer for each loaded compression block. */ + cb = (u8*)ntfs_malloc(cb_size); + if (!cb) + return -1; + + /* Need a temporary buffer for each uncompressed block. */ + dest = (u8*)ntfs_malloc(cb_size); + if (!dest) { + free(cb); + return -1; + } + /* + * The first vcn in the first compression block (cb) which we need to + * decompress. + */ + start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; + /* Offset in the uncompressed cb at which to start reading data. */ + ofs = pos & cb_size_mask; + /* + * The first vcn in the cb after the last cb which we need to + * decompress. + */ + end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> + vol->cluster_size_bits; + /* Number of compression blocks (cbs) in the wanted vcn range. */ + nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> + na->compression_block_size_bits; + cb_end = cb + cb_size; +do_next_cb: + nr_cbs--; + cb_pos = cb; + vcn = start_vcn; + start_vcn += cb_clusters; + + /* Check whether the compression block is sparse. */ + rl = ntfs_attr_find_vcn(na, vcn); + if (!rl || rl->lcn < LCN_HOLE) { + free(cb); + free(dest); + if (total) + return total; + /* FIXME: Do we want EIO or the error code? (AIA) */ + errno = EIO; + return -1; + } + if (rl->lcn == LCN_HOLE) { + /* Sparse cb, zero out destination range overlapping the cb. */ + ntfs_log_debug("Found sparse compression block.\n"); + to_read = min(count, cb_size - ofs); + memset(b, 0, to_read); + ofs = 0; + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { + s64 tdata_size, tinitialized_size; + /* + * Uncompressed cb, read it straight into the destination range + * overlapping the cb. + */ + ntfs_log_debug("Found uncompressed compression block.\n"); + /* + * Read the uncompressed data into the destination buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the data by simply using ntfs_attr_pread(). (-8 + * NOTE: we have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = min(count, cb_size - ofs); + ofs += vcn << vol->cluster_size_bits; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, ofs, to_read, b); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + total += br; + count -= br; + b = (u8*)b + br; + to_read -= br; + ofs += br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + ofs = 0; + } else { + s64 tdata_size, tinitialized_size; + + /* + * Compressed cb, decompress it into the temporary buffer, then + * copy the data to the destination range overlapping the cb. + */ + ntfs_log_debug("Found compressed compression block.\n"); + /* + * Read the compressed data into the temporary buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the raw, compressed data by simply using + * ntfs_attr_pread(). (-8 + * NOTE: We have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = cb_size; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, + (vcn << vol->cluster_size_bits) + + (cb_pos - cb), to_read, cb_pos); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + cb_pos += br; + to_read -= br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + /* Just a precaution. */ + if (cb_pos + 2 <= cb_end) + *(u16*)cb_pos = 0; + ntfs_log_debug("Successfully read the compression block.\n"); + if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { + err = errno; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return -1; + } + to_read = min(count, cb_size - ofs); + memcpy(b, dest + ofs, to_read); + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + ofs = 0; + } + /* Do we have more work to do? */ + if (nr_cbs) + goto do_next_cb; + /* We no longer need the buffers. */ + free(cb); + free(dest); + /* Return number of bytes read. */ + return total + total2; +} + +/* + * Read data from a set of clusters + * + * Returns the amount of data read + */ + +static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, u32 to_read, char *inbuf) +{ + u32 count; + int xgot; + u32 got; + s64 xpos; + BOOL first; + char *xinbuf; + const runlist_element *xrl; + + got = 0; + xrl = rl; + xinbuf = inbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_read - got) < count) + count = to_read - got; + xgot = ntfs_pread(vol->dev, xpos, count, xinbuf); + if (xgot == (int)count) { + got += count; + xpos += count; + xinbuf += count; + xrl++; + } + first = FALSE; + } while ((xgot == (int)count) && (got < to_read)); + return (got); +} + +/* + * Write data to a set of clusters + * + * Returns the amount of data written + */ + +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) +{ + s32 count; + s32 put, xput; + s64 xpos; + BOOL first; + const char *xoutbuf; + const runlist_element *xrl; + + put = 0; + xrl = rl; + xoutbuf = outbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_write - put) < count) + count = to_write - put; + xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf); + if (xput == count) { + put += count; + xpos += count; + xoutbuf += count; + xrl++; + } + first = FALSE; + } while ((xput == count) && (put < to_write)); + return (put); +} + + +/* + * Compress and write a set of blocks + * + * returns the size actually written (rounded to a full cluster) + * or 0 if all zeroes (nothing is written) + * or -1 if could not compress (nothing is written) + * or -2 if there were an irrecoverable error (errno set) + */ + +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) +{ + ntfs_volume *vol; + char *outbuf; + char *pbuf; + u32 compsz; + s32 written; + s32 rounded; + unsigned int clsz; + u32 p; + unsigned int sz; + unsigned int bsz; + BOOL fail; + BOOL allzeroes; + /* a single compressed zero */ + static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ; + /* a couple of compressed zeroes */ + static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ; + /* more compressed zeroes, to be followed by some count */ + static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ; + + vol = na->ni->vol; + written = -1; /* default return */ + clsz = 1 << vol->cluster_size_bits; + /* may need 2 extra bytes per block and 2 more bytes */ + outbuf = (char*)ntfs_malloc(na->compression_block_size + + 2*(na->compression_block_size/NTFS_SB_SIZE) + + 2); + if (outbuf) { + fail = FALSE; + compsz = 0; + allzeroes = TRUE; + for (p=0; (p na->compression_block_size)) + fail = TRUE; + else { + if (allzeroes) { + /* check whether this is all zeroes */ + switch (sz) { + case 4 : + allzeroes = !memcmp( + pbuf,onezero,4); + break; + case 5 : + allzeroes = !memcmp( + pbuf,twozeroes,5); + break; + case 6 : + allzeroes = !memcmp( + pbuf,morezeroes,4); + break; + default : + allzeroes = FALSE; + break; + } + } + compsz += sz; + } + } + if (!fail && !allzeroes) { + /* add a couple of null bytes, space has been checked */ + outbuf[compsz++] = 0; + outbuf[compsz++] = 0; + /* write a full cluster, to avoid partial reading */ + rounded = ((compsz - 1) | (clsz - 1)) + 1; + written = write_clusters(vol, rl, offs, rounded, outbuf); + if (written != rounded) { + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ + ntfs_log_error("error writing compressed data\n"); + errno = EIO; + written = -2; + } + } else + if (!fail) + written = 0; + free(outbuf); + } + return (written); +} + +/* + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid + */ + +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) +{ + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; + + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); +} + +/* + * Free unneeded clusters after overwriting compressed data + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + * + * +======= start of block =====+ + * 0 |A chunk may overflow | <-- rl usedcnt : A + B + * |A on previous block | then B + * |A | + * +-- end of allocated chunk --+ freelength : C + * |B | (incl overflow) + * +== end of compressed data ==+ + * |C | <-- freerl freecnt : C + D + * |C chunk may overflow | + * |C on next block | + * +-- end of allocated chunk --+ + * |D | + * |D chunk may overflow | + * 15 |D on next block | + * +======== end of block ======+ + * + */ + +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) +{ + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; + + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; + + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); + + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; + + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); +} + + +/* + * Free unneeded clusters after compression + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + */ + +static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, + s64 used, s64 reserved, BOOL appending, + VCN *update_from) +{ + s32 freecnt; + s32 usedcnt; + int res; + s64 freelcn; + s64 freevcn; + s32 freelength; + BOOL mergeholes; + BOOL beginhole; + ntfs_volume *vol; + runlist_element *freerl; + + res = -1; /* default return */ + vol = na->ni->vol; + freecnt = (reserved - used) >> vol->cluster_size_bits; + usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; + /* skip entries fully used, if any */ + while (rl->length && (rl->length < usedcnt)) { + usedcnt -= rl->length; /* must be > 0 */ + rl++; + } + if (rl->length) { + /* + * Splitting the current allocation block requires + * an extra runlist element to create the hole. + * The required entry has been prereserved when + * mapping the runlist. + */ + /* get the free part in initial run */ + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + /* new count of allocated clusters */ + if (!((freevcn + freecnt) + & (na->compression_block_clusters - 1))) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; + + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { + /* + * move the unused part to the end. Doing so, + * the vcn will be out of order. This does + * not harm, the vcn are meaningless now, and + * only the lcn are meaningful for freeing. + */ + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; + } else { + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { + freerl--; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; + } + } + } else { + ntfs_log_error("Bad end of a compression block set\n"); + errno = EIO; + } + } else { + ntfs_log_error("No cluster to free after compression\n"); + errno = EIO; + } + return (res); +} + +/* + * Read existing data, decompress and append buffer + * Do nothing if something fails + */ + +static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, + s64 offs, u32 compsz, s32 pos, BOOL appending, + char *outbuf, s64 to_write, const void *b) +{ + int fail = 1; + char *compbuf; + u32 decompsz; + u32 got; + + if (compsz == na->compression_block_size) { + /* if the full block was requested, it was a hole */ + memset(outbuf,0,compsz); + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } else { + compbuf = (char*)ntfs_malloc(compsz); + if (compbuf) { + /* must align to full block for decompression */ + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; + got = read_clusters(na->ni->vol, rl, offs, + compsz, compbuf); + if ((got == compsz) + && !ntfs_decompress((u8*)outbuf,decompsz, + (u8*)compbuf,compsz)) { + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } + free(compbuf); + } + } + return (fail); +} + +/* + * Flush a full compression block + * + * returns the size actually written (rounded to a full cluster) + * or 0 if could not compress (and written uncompressed) + * or -1 if there were an irrecoverable error (errno set) + */ + +static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) +{ + s32 rounded; + s32 written; + int clsz; + + if (compress) { + written = ntfs_comp_set(na, rl, offs, count, outbuf); + if (written == -1) + compress = FALSE; + if ((written >= 0) + && ntfs_compress_free(na,rl,offs + written, + offs + na->compression_block_size, appending, + update_from)) + written = -1; + } else + written = 0; + if (!compress) { + clsz = 1 << na->ni->vol->cluster_size_bits; + rounded = ((count - 1) | (clsz - 1)) + 1; + written = write_clusters(na->ni->vol, rl, + offs, rounded, outbuf); + if (written != rounded) + written = -1; + } + return (written); +} + +/* + * Write some data to be compressed. + * Compression only occurs when a few clusters (usually 16) are + * full. When this occurs an extra runlist slot may be needed, so + * it has to be reserved beforehand. + * + * Returns the size of uncompressed data written, + * or negative if an error occurred. + * When the returned size is less than requested, new clusters have + * to be allocated before the function is called again. + */ + +s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 to_flush; + s64 roffs; + s64 got; + s64 start_vcn; + s64 nextblock; + s64 endwrite; + u32 compsz; + char *inbuf; + char *outbuf; + BOOL fail; + BOOL done; + BOOL compress; + BOOL appending; + + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + compress = FALSE; + done = FALSE; + /* + * Cannot accept writing beyond the current compression set + * because when compression occurs, clusters are freed + * and have to be reallocated. + * (cannot happen with standard fuse 4K buffers) + * Caller has to avoid this situation, or face consequences. + */ + nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) + | (na->compression_block_size - 1)) + 1; + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { + /* it is time to compress */ + compress = TRUE; + /* only process what we can */ + to_write = rounded = nextblock + - (offs + (wrl->vcn << vol->cluster_size_bits)); + } + start_vcn = 0; + fail = FALSE; + brl = wrl; + roffs = 0; + /* + * If we are about to compress or we need to decompress + * existing data, we have to process a full set of blocks. + * So relocate the parameters to the beginning of allocation + * containing the first byte of the set of blocks. + */ + if (compress || compressed_part) { + /* find the beginning of block */ + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + while (brl->vcn && (brl->vcn > start_vcn)) { + /* jumping back a hole means big trouble */ + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when appending\n"); + fail = TRUE; + errno = EIO; + } + brl--; + offs += brl->length << vol->cluster_size_bits; + } + roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + } + if (compressed_part && !fail) { + /* + * The set of compression blocks contains compressed data + * (we are reopening an existing file to append to it) + * Decompress the data and append + */ + compsz = (s32)compressed_part << vol->cluster_size_bits; + outbuf = (char*)ntfs_malloc(na->compression_block_size); + if (outbuf) { + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } + if (!ntfs_read_append(na, brl, roffs, compsz, + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { + written = ntfs_flush(na, brl, roffs, + outbuf, to_flush, compress, appending, + update_from); + if (written >= 0) { + written = to_write; + done = TRUE; + } + } + free(outbuf); + } + } else { + if (compress && !fail) { + /* + * we are filling up a block, read the full set + * of blocks and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + to_read = offs - roffs; + if (to_read) + got = read_clusters(vol, brl, roffs, + to_read, inbuf); + else + got = 0; + if (got == to_read) { + memcpy(&inbuf[to_read],b,to_write); + written = ntfs_comp_set(na, brl, roffs, + to_read + to_write, inbuf); + /* + * if compression was not successful, + * only write the part which was requested + */ + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + + roffs, + appending, update_from)) { + done = TRUE; + written = to_write; + } + } + free(inbuf); + } + } + if (!done) { + /* + * if the compression block is not full, or + * if compression failed for whatever reason, + * write uncompressed + */ + /* check we are not overflowing current allocation */ + if ((wpos + rounded) + > ((wrl->lcn + wrl->length) + << vol->cluster_size_bits)) { + ntfs_log_error("writing on unallocated clusters\n"); + errno = EIO; + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounded, b); + if (written == rounded) + written = to_write; + } + } + } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; + return (written); +} + +/* + * Close a file written compressed. + * This compresses the last partial compression block of the file. + * Two empty runlist slots have to be reserved beforehand. + * + * Returns zero if closing is successful. + */ + +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 roffs; + s64 got; + s64 start_vcn; + char *inbuf; + BOOL fail; + BOOL done; + + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + done = FALSE; + /* + * There generally is an uncompressed block at end of file, + * read the full block and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); + brl = wrl; + fail = FALSE; + while (brl->vcn && (brl->vcn > start_vcn)) { + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when closing\n"); + fail = TRUE; + errno = EIO; + } + brl--; + } + if (!fail) { + /* roffs can be an offset from another uncomp block */ + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; + if (to_read) { + got = read_clusters(vol, brl, roffs, to_read, + inbuf); + if (got == to_read) { + written = ntfs_comp_set(na, brl, roffs, + to_read, inbuf); + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + roffs, + TRUE, update_from)) { + done = TRUE; + } else + /* if compression failed, leave uncompressed */ + if (written == -1) + done = TRUE; + } + } else + done = TRUE; + free(inbuf); + } + } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; + return (!done); +} diff --git a/lib/libntfs/src/source/compress.h b/lib/libntfs/src/source/compress.h new file mode 100644 index 0000000..c256932 --- /dev/null +++ b/lib/libntfs/src/source/compress.h @@ -0,0 +1,41 @@ +/* + * compress.h - Exports for compressed attribute handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPRESS_H +#define _NTFS_COMPRESS_H + +#include "types.h" +#include "attrib.h" + +extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, + void *b); + +extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from); + +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); + +#endif /* defined _NTFS_COMPRESS_H */ + diff --git a/lib/libntfs/src/source/config.h b/lib/libntfs/src/source/config.h new file mode 100644 index 0000000..f145094 --- /dev/null +++ b/lib/libntfs/src/source/config.h @@ -0,0 +1,394 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +#undef ENABLE_CRYPTO + +/* Define to 1 if debug should be enabled */ +#undef ENABLE_DEBUG + +/* Define to 1 if the nfconv patch should be enabled */ +#undef ENABLE_NFCONV + +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +#undef ENABLE_UUID + +/* Define to 1 if using internal fuse */ +#undef FUSE_INTERNAL + +/* Define to 1 if you have the `atexit' function. */ +#define HAVE_ATEXIT 1 + +/* Define to 1 if you have the `basename' function. */ +#undef HAVE_BASENAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the `daemon' function. */ +#undef HAVE_DAEMON + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the header file. */ +#undef HAVE_FEATURES_H + +/* Define to 1 if you have the `ffs' function. */ +#define HAVE_FFS 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getmntent' function. */ +#undef HAVE_GETMNTENT + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `hasmntopt' function. */ +#undef HAVE_HASMNTOPT + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBGEN_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBINTL_H + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_FD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_HDREG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_MAJOR_H + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MACHINE_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if mbrtowc and mbstate_t are properly declared. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbsinit' function. */ +#define HAVE_MBSINIT 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MNTENT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setxattr' function. */ +#undef HAVE_SETXATTR + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#define HAVE_STAT_EMPTY_STRING_BUG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if `st_atim' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM + +/* Define to 1 if `st_atimensec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC + +/* Define to 1 if `st_atimespec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC + +/* Define to 1 if `st_blocks' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define to 1 if `st_rdev' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_BYTEORDER_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_DISK_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MKDEV_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSMACROS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_VFS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Don't update /etc/mtab */ +#undef IGNORE_MTAB + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Don't use default IO ops */ +#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +/* Name of package */ +#define PACKAGE "ntfs-3g" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "ntfs-3g" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "ntfs-3g 2012.1.15" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "ntfs-3g" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2012.1.15" + +/* POSIX ACL support */ +#undef POSIXACLS + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + +/* Version number of package */ +#define VERSION "2012.1.15" + +/* Define to 1 if this is a Windows OS */ +#undef WINDOWS + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#define WORDS_BIGENDIAN 1 + +/* Define to 1 if your processor stores words with the least significant byte + first (like Intel and VAX, unlike Motorola and SPARC). */ +#undef WORDS_LITTLEENDIAN + +/* system extended attributes mappings */ +#undef XATTR_MAPPINGS + +/* Number of bits in a file offset, on hosts where this is settable. */ +#define _FILE_OFFSET_BITS 64 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +/* Define for large files, on AIX-style hosts. */ +#define _LARGE_FILES 1 + +/* Required define if using POSIX threads */ +#undef _REENTRANT + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline __inline__ +#endif + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/lib/libntfs/src/source/debug.c b/lib/libntfs/src/source/debug.c new file mode 100644 index 0000000..f193483 --- /dev/null +++ b/lib/libntfs/src/source/debug.c @@ -0,0 +1,79 @@ +/** + * debug.c - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "runlist.h" +#include "debug.h" +#include "logging.h" + +#ifdef DEBUG +/** + * ntfs_debug_runlist_dump - Dump a runlist. + * @rl: + * + * Description... + * + * Returns: + */ +void ntfs_debug_runlist_dump(const runlist_element *rl) +{ + int i = 0; + const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", + "LCN_ENOENT ", "LCN_EINVAL ", + "LCN_unknown " }; + + ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); + if (!rl) { + ntfs_log_debug("Run list not present.\n"); + return; + } + ntfs_log_debug("VCN LCN Run length\n"); + do { + LCN lcn = (rl + i)->lcn; + + if (lcn < (LCN)0) { + int idx = -lcn - 1; + + if (idx > -LCN_EINVAL - 1) + idx = 4; + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } else + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } while (rl[i++].length); +} + +#endif + diff --git a/lib/libntfs/src/source/debug.h b/lib/libntfs/src/source/debug.h new file mode 100644 index 0000000..f7f3c6f --- /dev/null +++ b/lib/libntfs/src/source/debug.h @@ -0,0 +1,47 @@ +/* + * debug.h - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEBUG_H +#define _NTFS_DEBUG_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "logging.h" + +struct _runlist_element; + +#ifdef DEBUG +extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); +#else +static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} +#endif + +#define NTFS_BUG(msg) \ +{ \ + int ___i = 1; \ + ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ + ntfs_log_debug("Forcing segmentation fault!"); \ + ___i = ((int*)NULL)[___i]; \ +} + +#endif /* defined _NTFS_DEBUG_H */ diff --git a/lib/libntfs/src/source/device.c b/lib/libntfs/src/source/device.c new file mode 100644 index 0000000..274abac --- /dev/null +++ b/lib/libntfs/src/source/device.c @@ -0,0 +1,808 @@ +/** + * device.c - Low level device io functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2006 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_DISK_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_LINUX_HDREG_H +#include +#endif + +#include "types.h" +#include "mst.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#endif +#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#endif +#if defined(linux) && !defined(HDIO_GETGEO) +#define HDIO_GETGEO 0x0301 /* Get device geometry. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKSSZGET) +# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKBSZSET) +# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ +#endif + +/** + * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it + * @name: name of the device (must be present) + * @state: initial device state (usually zero) + * @dops: ntfs device operations to use with the device (must be present) + * @priv_data: pointer to private data (optional) + * + * Allocate an ntfs device structure and pre-initialize it with the user- + * specified device operations @dops, device state @state, device name @name, + * and optional private data @priv_data. + * + * Note, @name is copied and can hence be freed after this functions returns. + * + * On success return a pointer to the allocated ntfs device structure and on + * error return NULL with errno set to the error code returned by ntfs_malloc(). + */ +struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data) +{ + struct ntfs_device *dev; + + if (!name) { + errno = EINVAL; + return NULL; + } + + dev = ntfs_malloc(sizeof(struct ntfs_device)); + if (dev) { + if (!(dev->d_name = strdup(name))) { + int eo = errno; + free(dev); + errno = eo; + return NULL; + } + dev->d_ops = dops; + dev->d_state = state; + dev->d_private = priv_data; + } + return dev; +} + +/** + * ntfs_device_free - free an ntfs device structure + * @dev: ntfs device structure to free + * + * Free the ntfs device structure @dev. + * + * Return 0 on success or -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid pointer @dev. + * EBUSY Device is still open. Close it before freeing it! + */ +int ntfs_device_free(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + free(dev->d_name); + free(dev); + return 0; +} + +/* + * Sync the device + * + * returns zero if successful. + */ + +int ntfs_device_sync(struct ntfs_device *dev) +{ + int ret; + struct ntfs_device_operations *dops; + + if (NDevDirty(dev)) { + dops = dev->d_ops; + ret = dops->sync(dev); + } else + ret = 0; + return ret; +} + +/** + * ntfs_pread - positioned read from disk + * @dev: device to read from + * @pos: position in device to read from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes from device @dev at position @pos into + * the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that we have either reached end of file or + * encountered an error during the read so that the read is partial. 0 means + * end of file or nothing to read (@count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of either seek, read, or set to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) +{ + s64 br, total; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + + dops = dev->d_ops; + + for (total = 0; count; count -= br, total += br) { + br = dops->pread(dev, (char*)b + total, count, pos + total); + /* If everything ok, continue. */ + if (br > 0) + continue; + /* If EOF or error return number of bytes read. */ + if (!br || total) + return total; + /* Nothing read and error, return error status. */ + return br; + } + /* Finally, return the number of bytes read. */ + return total; +} + +/** + * ntfs_pwrite - positioned write to disk + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the device @dev + * at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + */ +s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b) +{ + s64 written, total, ret = -1; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + goto out; + } + if (!count) + return 0; + if (NDevReadOnly(dev)) { + errno = EROFS; + goto out; + } + + dops = dev->d_ops; + + NDevSetDirty(dev); + for (total = 0; count; count -= written, total += written) { + written = dops->pwrite(dev, (const char*)b + total, count, + pos + total); + /* If everything ok, continue. */ + if (written > 0) + continue; + /* + * If nothing written or error return number of bytes written. + */ + if (!written || total) + break; + /* Nothing written and error, return error status. */ + total = written; + break; + } + if (NDevSync(dev) && total && dops->sync(dev)) { + total--; /* on sync error, return partially written */ + } + ret = total; +out: + return ret; +} + +/** + * ntfs_mst_pread - multi sector transfer (mst) positioned read + * @dev: device to read from + * @pos: position in file descriptor to read from + * @count: number of blocks to read + * @bksize: size of each block that needs mst deprotecting + * @b: output data buffer + * + * Multi sector transfer (mst) positioned read. This function will read @count + * blocks of size @bksize bytes each from device @dev at position @pos into the + * the data buffer @b. + * + * On success, return the number of successfully read blocks. If this number is + * lower than @count this means that we have reached end of file, that the read + * was interrupted, or that an error was encountered during the read so that + * the read is partial. 0 means end of file or nothing was read (also return 0 + * when @count or @bksize are 0). + * + * On error and nothing was read, return -1 with errno set appropriately to the + * return code of either seek, read, or set to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer has been detected the magic + * will have been changed to magic_BAAD but no error will be returned. Thus it + * is possible that we return count blocks as being read but that any number + * (between zero and count!) of these blocks is actually subject to a multi + * sector transfer error. This should be detected by the caller by checking for + * the magic being "BAAD". + */ +s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 br, i; + + if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + /* Do the read. */ + br = ntfs_pread(dev, pos, count * bksize, b); + if (br < 0) + return br; + /* + * Apply fixups to successfully read data, disregarding any errors + * returned from the MST fixup function. This is because we want to + * fixup everything possible and we rely on the fact that the "BAAD" + * magic will be detected later on. + */ + count = br / bksize; + for (i = 0; i < count; ++i) + ntfs_mst_post_read_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + /* Finally, return the number of complete blocks read. */ + return count; +} + +/** + * ntfs_mst_pwrite - multi sector transfer (mst) positioned write + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of blocks to write + * @bksize: size of each block that needs mst protecting + * @b: data buffer to write to disk + * + * Multi sector transfer (mst) positioned write. This function will write + * @count blocks of size @bksize bytes each from data buffer @b to the device + * @dev at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @count this means that the write has been interrupted or that + * an error was encountered during the write so that the write is partial. 0 + * means nothing was written (also return 0 when @count or @bksize are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 written, i; + + if (count < 0 || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < count; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + if (err < 0) { + /* Abort write at this position. */ + if (!i) + return err; + count = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_pwrite(dev, pos, count * bksize, b); + /* Quickly deprotect the data again. */ + for (i = 0; i < count; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bksize; +} + +/** + * ntfs_cluster_read - read ntfs clusters + * @vol: volume to read from + * @lcn: starting logical cluster number + * @count: number of clusters to read + * @b: output data buffer + * + * Read @count ntfs clusters starting at logical cluster number @lcn from + * volume @vol into buffer @b. Return number of clusters read or -1 on error, + * with errno set to the error code. + */ +s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, + void *b) +{ + s64 br; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to read outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + if (br < 0) { + ntfs_log_perror("Error reading cluster(s)"); + return br; + } + return br >> vol->cluster_size_bits; +} + +/** + * ntfs_cluster_write - write ntfs clusters + * @vol: volume to write to + * @lcn: starting logical cluster number + * @count: number of clusters to write + * @b: data buffer to write to disk + * + * Write @count ntfs clusters starting at logical cluster number @lcn from + * buffer @b to volume @vol. Return the number of clusters written or -1 on + * error, with errno set to the error code. + */ +s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b) +{ + s64 bw; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to write outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + if (!NVolReadOnly(vol)) + bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + else + bw = count << vol->cluster_size_bits; + if (bw < 0) { + ntfs_log_perror("Error writing cluster(s)"); + return bw; + } + return bw >> vol->cluster_size_bits; +} + +/** + * ntfs_device_offset_valid - test if a device offset is valid + * @dev: open device + * @ofs: offset to test for validity + * + * Test if the offset @ofs is an existing location on the device described + * by the open device structure @dev. + * + * Return 0 if it is valid and -1 if it is not valid. + */ +static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) +{ + char ch; + + if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && + dev->d_ops->read(dev, &ch, 1) == 1) + return 0; + return -1; +} + +/** + * ntfs_device_size_get - return the size of a device in blocks + * @dev: open device + * @block_size: block size in bytes in which to return the result + * + * Return the number of @block_size sized blocks in the device described by the + * open device @dev. + * + * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. + * + * On error return -1 with errno set to the error code. + */ +s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) +{ + s64 high, low; + + if (!dev || block_size <= 0 || (block_size - 1) & block_size) { + errno = EINVAL; + return -1; + } +#ifdef BLKGETSIZE64 + { u64 size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef BLKGETSIZE + { unsigned long size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", + size, size); + return (s64)size * 512 / block_size; + } + } +#endif +#ifdef FDGETPRM + { struct floppy_struct this_floppy; + + if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", + (unsigned long)this_floppy.size, + (unsigned long)this_floppy.size); + return (s64)this_floppy.size * 512 / block_size; + } + } +#endif +#ifdef DIOCGMEDIASIZE + { + /* FreeBSD */ + off_t size; + + if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) { + ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef DKIOCGETBLOCKCOUNT + { + /* Mac OS X */ + uint64_t blocks; + int sector_size; + + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size >= 0 && dev->d_ops->ioctl(dev, + DKIOCGETBLOCKCOUNT, &blocks) >= 0) + { + ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n", + (unsigned long long) blocks, + (unsigned long long) blocks); + return blocks * sector_size / block_size; + } + } +#endif + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; + + if (!ntfs_device_offset_valid(dev, mid)) + low = mid; + else + high = mid; + } + dev->d_ops->seek(dev, 0LL, SEEK_SET); + return (low + 1LL) / block_size; +} + +/** + * ntfs_device_partition_start_sector_get - get starting sector of a partition + * @dev: open device + * + * On success, return the starting sector of the partition @dev in the parent + * block device of @dev. On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", + geo.start, geo.start); + return geo.start; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_heads_get - get number of heads of device + * @dev: open device + * + * On success, return the number of heads on the device @dev. On error return + * -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +int ntfs_device_heads_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", + (unsigned)geo.heads, + (unsigned)geo.heads); + return geo.heads; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_sectors_per_track_get - get number of sectors per track of device + * @dev: open device + * + * On success, return the number of sectors per track on the device @dev. On + * error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", + (unsigned)geo.sectors, + (unsigned)geo.sectors); + return geo.sectors; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_sector_size_get - get sector size of a device + * @dev: open device + * + * On success, return the sector size in bytes of the device @dev. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKSSZGET ioctl + * ENOTTY @dev is a file or a device not supporting BLKSSZGET + */ +int ntfs_device_sector_size_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKSSZGET + { + int sect_size = 0; + + if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { + ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", + sect_size); + return sect_size; + } + } +#elif defined(DIOCGSECTORSIZE) + { + /* FreeBSD */ + size_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) { + ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#elif defined(DKIOCGETBLOCKSIZE) + { + /* Mac OS X */ + uint32_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) { + ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_block_size_set - set block size of a device + * @dev: open device + * @block_size: block size to set @dev to + * + * On success, return 0. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKBSZSET ioctl + * ENOTTY @dev is a file or a device not supporting BLKBSZSET + */ +int ntfs_device_block_size_set(struct ntfs_device *dev, + int block_size __attribute__((unused))) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKBSZSET + { + size_t s_block_size = block_size; + if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { + ntfs_log_debug("Used BLKBSZSET to set block size to " + "%d bytes.\n", block_size); + return 0; + } + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + } +#else + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + errno = EOPNOTSUPP; +#endif + return -1; +} diff --git a/lib/libntfs/src/source/device.h b/lib/libntfs/src/source/device.h new file mode 100644 index 0000000..ad34ac5 --- /dev/null +++ b/lib/libntfs/src/source/device.h @@ -0,0 +1,134 @@ +/* + * device.h - Exports for low level device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_H +#define _NTFS_DEVICE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "device_io.h" +#include "types.h" +#include "support.h" +#include "volume.h" + +/** + * enum ntfs_device_state_bits - + * + * Defined bits for the state field in the ntfs_device structure. + */ +typedef enum { + ND_Open, /* 1: Device is open. */ + ND_ReadOnly, /* 1: Device is read-only. */ + ND_Dirty, /* 1: Device is dirty, needs sync. */ + ND_Block, /* 1: Device is a block device. */ + ND_Sync, /* 1: Device is mounted with "-o sync" */ +} ntfs_device_state_bits; + +#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) +#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) +#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) + +#define NDevOpen(nd) test_ndev_flag(nd, Open) +#define NDevSetOpen(nd) set_ndev_flag(nd, Open) +#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) + +#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) +#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) +#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) + +#define NDevDirty(nd) test_ndev_flag(nd, Dirty) +#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) +#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) + +#define NDevBlock(nd) test_ndev_flag(nd, Block) +#define NDevSetBlock(nd) set_ndev_flag(nd, Block) +#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) + +#define NDevSync(nd) test_ndev_flag(nd, Sync) +#define NDevSetSync(nd) set_ndev_flag(nd, Sync) +#define NDevClearSync(nd) clear_ndev_flag(nd, Sync) + +/** + * struct ntfs_device - + * + * The ntfs device structure defining all operations needed to access the low + * level device underlying the ntfs volume. + */ +struct ntfs_device { + struct ntfs_device_operations *d_ops; /* Device operations. */ + unsigned long d_state; /* State of the device. */ + char *d_name; /* Name of device. */ + void *d_private; /* Private data used by the + device operations. */ +}; + +struct stat; + +/** + * struct ntfs_device_operations - + * + * The ntfs device operations defining all operations that can be performed on + * the low level device described by an ntfs device structure. + */ +struct ntfs_device_operations { + int (*open)(struct ntfs_device *dev, int flags); + int (*close)(struct ntfs_device *dev); + s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); + s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); + s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); + s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); + s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, + s64 offset); + int (*sync)(struct ntfs_device *dev); + int (*stat)(struct ntfs_device *dev, struct stat *buf); + int (*ioctl)(struct ntfs_device *dev, int request, void *argp); +}; + +extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data); +extern int ntfs_device_free(struct ntfs_device *dev); +extern int ntfs_device_sync(struct ntfs_device *dev); + +extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, + void *b); +extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b); + +extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); +extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); + +extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, + const s64 count, void *b); +extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b); + +extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); +extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); +extern int ntfs_device_heads_get(struct ntfs_device *dev); +extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); +extern int ntfs_device_sector_size_get(struct ntfs_device *dev); +extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); + +#endif /* defined _NTFS_DEVICE_H */ diff --git a/lib/libntfs/src/source/device_io.c b/lib/libntfs/src/source/device_io.c new file mode 100644 index 0000000..f76bf70 --- /dev/null +++ b/lib/libntfs/src/source/device_io.c @@ -0,0 +1,40 @@ +/* + * device_io.c - Default device io operations. Originated from the Linux-NTFS project. + * + * Copyright (c) 2003 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifndef GEKKO +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +#ifndef __CYGWIN32__ + +/* Not on Cygwin; use standard Unix style low level device operations. */ +#include "unix_io.c" + +#else /* __CYGWIN32__ */ + +/* On Cygwin; use Win32 low level device operations. */ +#include "win32_io.c" + +#endif /* __CYGWIN32__ */ + +#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ +#endif /* GEKKO */ diff --git a/lib/libntfs/src/source/device_io.h b/lib/libntfs/src/source/device_io.h new file mode 100644 index 0000000..fad4d85 --- /dev/null +++ b/lib/libntfs/src/source/device_io.h @@ -0,0 +1,82 @@ +/* + * device_io.h - Exports for default device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_IO_H +#define _NTFS_DEVICE_IO_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +#ifndef __CYGWIN32__ + +#ifndef GEKKO +/* Not on Cygwin; use standard Unix style low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_unix_io_ops +#else +/* Wii i/o device. */ +#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +#endif + +#else /* __CYGWIN32__ */ + +#ifndef HDIO_GETGEO +# define HDIO_GETGEO 0x301 +/** + * struct hd_geometry - + */ +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; +#endif +#ifndef BLKGETSIZE +# define BLKGETSIZE 0x1260 +#endif +#ifndef BLKSSZGET +# define BLKSSZGET 0x1268 +#endif +#ifndef BLKGETSIZE64 +# define BLKGETSIZE64 0x80041272 +#endif +#ifndef BLKBSZSET +# define BLKBSZSET 0x40041271 +#endif + +/* On Cygwin; use Win32 low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_win32_io_ops + +#endif /* __CYGWIN32__ */ + + +/* Forward declaration. */ +struct ntfs_device_operations; + +extern struct ntfs_device_operations ntfs_device_default_io_ops; + +#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ + +#endif /* defined _NTFS_DEVICE_IO_H */ + diff --git a/lib/libntfs/src/source/dir.c b/lib/libntfs/src/source/dir.c new file mode 100644 index 0000000..ccae47c --- /dev/null +++ b/lib/libntfs/src/source/dir.c @@ -0,0 +1,2657 @@ +/** + * dir.c - Directory handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "param.h" +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "ntfstime.h" +#include "lcnalloc.h" +#include "logging.h" +#include "cache.h" +#include "misc.h" +#include "security.h" +#include "reparse.h" +#include "object_id.h" + +#ifdef HAVE_SETXATTR +#include +#endif + +/* + * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" + * and "$Q" as global constants. + */ +ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), + const_cpu_to_le16('3'), const_cpu_to_le16('0'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('I'), const_cpu_to_le16('I'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('D'), const_cpu_to_le16('H'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), + const_cpu_to_le16('\0') }; + +#if CACHE_INODE_SIZE + +/* + * Pathname hashing + * + * Based on first char and second char (which may be '\0') + */ + +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) +{ + const char *path; + const unsigned char *name; + + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); +} + +/* + * Pathname comparing for entering/fetching from cache + */ + +static int inode_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (!cached->variable + || strcmp(cached->variable, wanted->variable)); +} + +/* + * Pathname comparing for invalidating entries in cache + * + * A partial path is compared in order to invalidate all paths + * related to a renamed directory + * inode numbers are also checked, as deleting a long name may + * imply deleting a short name and conversely + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; + + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); +} + +#endif + +#if CACHE_LOOKUP_SIZE + +/* + * File name comparing for entering/fetching from lookup cache + */ + +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); +} + +/* + * Inode number comparing for invalidating lookup cache + * + * All entries with designated inode number are invalidated + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); +} + +/* + * Lookup hashing + * + * Based on first, second and and last char + */ + +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) +{ + const unsigned char *name; + int count; + unsigned int val; + + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); +} + +#endif + +/** + * ntfs_inode_lookup_by_name - find an inode in a directory given its name + * @dir_ni: ntfs inode of the directory in which to search for the name + * @uname: Unicode name for which to search in the directory + * @uname_len: length of the name @uname in Unicode characters + * + * Look for an inode with name @uname in the directory with inode @dir_ni. + * ntfs_inode_lookup_by_name() walks the contents of the directory looking for + * the Unicode name. If the name is found in the directory, the corresponding + * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it + * is a 64-bit number containing the sequence number. + * + * On error, return -1 with errno set to the error code. If the inode is is not + * found errno is ENOENT. + * + * Note, @uname_len does not include the (optional) terminating NULL character. + * + * Note, we look for a case sensitive match first but we also look for a case + * insensitive match at the same time. If we find a case insensitive match, we + * save that for the case that we don't find an exact match, where we return + * the mft reference of the case insensitive match. + * + * If the volume is mounted with the case sensitive flag set, then we only + * allow exact matches. + */ +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) +{ + VCN vcn; + u64 mref = 0; + s64 br; + ntfs_volume *vol = dir_ni->vol; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; + u8 *index_end; + ntfs_attr *ia_na; + int eo, rc; + u32 index_block_size; + u8 index_vcn_size_bits; + + ntfs_log_trace("Entering\n"); + + if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { + errno = EINVAL; + return -1; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + return -1; + + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto put_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + mref = le64_to_cpu(ie->indexed_file); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present return error code + * ENOENT, unless we have got the mft reference of a matching name + * cached in mref in which case return mref. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_attr_put_search_ctx(ctx); + if (mref) + return mref; + ntfs_log_debug("Entry not found - between root entries.\n"); + errno = ENOENT; + return -1; + } /* Child node present, descend into it. */ + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + ntfs_log_perror("Failed to open index allocation (inode %lld)", + (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) { + ntfs_attr_close(ia_na); + goto put_err_out; + } + + /* Determine the size of a vcn in the directory index. */ + if (vol->cluster_size <= index_block_size) { + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + } + + /* Get the starting vcn of the index_block holding the child node. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + +descend_into_child_node: + + /* Read the index block starting at vcn. */ + br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read vcn 0x%llx", + (unsigned long long)vcn); + goto close_err_out; + } + + if (sle64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx).\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)vcn); + errno = EIO; + goto close_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)vcn, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, + (unsigned)index_block_size); + errno = EIO; + goto close_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "0x%llx exceeds maximum size.\n", + (long long)vcn, (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds check. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto close_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + mref = le64_to_cpu(ie->indexed_file); + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node. + */ + if (ie->ie_flags & INDEX_ENTRY_NODE) { + if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* Child node present, descend into it. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + if (vcn >= 0) + goto descend_into_child_node; + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + /* + * No child node present, return error code ENOENT, unless we have got + * the mft reference of a matching name cached in mref in which case + * return mref. + */ + if (mref) + return mref; + ntfs_log_debug("Entry not found.\n"); + errno = ENOENT; + return -1; +put_err_out: + eo = EIO; + ntfs_log_debug("Corrupt directory. Aborting lookup.\n"); +eo_put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return -1; +close_err_out: + eo = errno; + free(ia); + ntfs_attr_close(ia_na); + goto eo_put_err_out; +} + +/* + * Lookup a file in a directory from its UTF-8 name + * + * The name is first fetched from cache if one is defined + * + * Returns the inode number + * or -1 if not possible (errno tells why) + */ + +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) +{ + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; + + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { +#if CACHE_LOOKUP_SIZE + + /* + * fetch inode from cache + */ + + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else +#endif + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); +} + +/* + * Update a cache lookup record when a name has been defined + * + * The UTF-8 name is required + */ + +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) +{ +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; + + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } +#endif +} + +/** + * ntfs_pathname_to_inode - Find the inode which represents the given pathname + * @vol: An ntfs volume obtained from ntfs_mount + * @parent: A directory inode to begin the search (may be NULL) + * @pathname: Pathname to be located + * + * Take an ASCII pathname and find the inode that represents it. The function + * splits the path and then descends the directory tree. If @parent is NULL, + * then the root directory '.' will be used as the base for the search. + * + * Return: inode Success, the pathname was valid + * NULL Error, the pathname was invalid, or some other error occurred + */ +ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname) +{ + u64 inum; + int len, err = 0; + char *p, *q; + ntfs_inode *ni; + ntfs_inode *result = NULL; + ntfschar *unicode = NULL; + char *ascii = NULL; +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + struct CACHED_INODE *cached; + char *fullname; +#endif + + if (!vol || !pathname) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("path: '%s'\n", pathname); + + ascii = strdup(pathname); + if (!ascii) { + ntfs_log_error("Out of memory.\n"); + err = ENOMEM; + goto out; + } + + p = ascii; + /* Remove leading /'s. */ + while (p && *p && *p == PATH_SEP) + p++; +#if CACHE_INODE_SIZE + fullname = p; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",ascii); +#endif + if (parent) { + ni = parent; + } else { +#if CACHE_INODE_SIZE + /* + * fetch inode for full path from cache + */ + if (*fullname) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + } else + cached = (struct CACHED_INODE*)NULL; + if (cached) { + /* + * return opened inode if found in cache + */ + inum = MREF(cached->inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + } + result = ni; + goto out; + } +#endif + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_debug("Couldn't open the inode of the root " + "directory.\n"); + err = EIO; + result = (ntfs_inode*)NULL; + goto out; + } + } + + while (p && *p) { + /* Find the end of the first token. */ + q = strchr(p, PATH_SEP); + if (q != NULL) { + *q = '\0'; + } +#if CACHE_INODE_SIZE + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } +#else + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); +#endif + if (inum == (u64) -1) { + ntfs_log_debug("Couldn't find name '%s' in pathname " + "'%s'.\n", p, pathname); + err = ENOENT; + goto close; + } + + if (ni != parent) + if (ntfs_inode_close(ni)) { + err = errno; + goto out; + } + + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + goto close; + } + + free(unicode); + unicode = NULL; + + if (q) *q++ = PATH_SEP; /* JPA */ + p = q; + while (p && *p && *p == PATH_SEP) + p++; + } + + result = ni; + ni = NULL; +close: + if (ni && (ni != parent)) + if (ntfs_inode_close(ni) && !err) + err = errno; +out: + free(ascii); + free(unicode); + if (err) + errno = err; + return result; +} + +/* + * The little endian Unicode string ".." for ntfs_readdir(). + */ +static const ntfschar dotdot[3] = { const_cpu_to_le16('.'), + const_cpu_to_le16('.'), + const_cpu_to_le16('\0') }; + +/* + * union index_union - + * More helpers for ntfs_readdir(). + */ +typedef union { + INDEX_ROOT *ir; + INDEX_ALLOCATION *ia; +} index_union __attribute__((__transparent_union__)); + +/** + * enum INDEX_TYPE - + * More helpers for ntfs_readdir(). + */ +typedef enum { + INDEX_TYPE_ROOT, /* index root */ + INDEX_TYPE_ALLOCATION, /* index allocation */ +} INDEX_TYPE; + +/** + * ntfs_filldir - ntfs specific filldir method + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @ivcn_bits: log(2) of index vcn size + * @index_type: specifies whether @iu is an index root or an index allocation + * @iu: index root or index block to which @ie belongs + * @ie: current index entry + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Pass information specifying the current directory entry @ie to the @filldir + * callback. + */ +static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, + const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, + void *dirent, ntfs_filldir_t filldir) +{ + FILE_NAME_ATTR *fn = &ie->key.file_name; + unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; + + ntfs_log_trace("Entering.\n"); + + /* Advance the position even if going to skip the entry. */ + if (index_type == INDEX_TYPE_ALLOCATION) + *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu( + iu.ia->index_block_vcn) << ivcn_bits) + + dir_ni->vol->mft_record_size; + else /* if (index_type == INDEX_TYPE_ROOT) */ + *pos = (u8*)ie - (u8*)iu.ir; + /* Skip root directory self reference entry. */ + if (MREF_LE(ie->indexed_file) == FILE_root) + return 0; + if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT) + dt_type = NTFS_DT_DIR; + else if (fn->file_attributes & FILE_ATTR_SYSTEM) + dt_type = NTFS_DT_UNKNOWN; + else + dt_type = NTFS_DT_REG; + + /* return metadata files and hidden files if requested */ + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); +} + +/** + * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode + * @ni: ntfs inode whose parent directory to find + * + * Find the parent directory of the ntfs inode @ni. To do this, find the first + * file name attribute in the mft record of @ni and return the parent mft + * reference from that. + * + * Note this only makes sense for directories, since files can be hard linked + * from multiple directories and there is no way for us to tell which one is + * being looked for. + * + * Technically directories can have hard links, too, but we consider that as + * illegal as Linux/UNIX do not support directory hard links. + * + * Return the mft reference of the parent directory on success or -1 on error + * with errno set to the error code. + */ +static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) +{ + MFT_REF mref; + ntfs_attr_search_ctx *ctx; + FILE_NAME_ATTR *fn; + int eo; + + ntfs_log_trace("Entering.\n"); + + if (!ni) { + errno = EINVAL; + return ERR_MREF(-1); + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ERR_MREF(-1); + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + if (ctx->attr->non_resident) { + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); + goto io_err_out; + } + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > + (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); + goto io_err_out; + } + mref = le64_to_cpu(fn->parent_directory); + ntfs_attr_put_search_ctx(ctx); + return mref; +io_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return ERR_MREF(-1); +} + +/** + * ntfs_readdir - read the contents of an ntfs directory + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Parse the index root and the index blocks that are marked in use in the + * index bitmap and hand each found directory entry to the @filldir callback + * supplied by the caller. + * + * Return 0 on success or -1 on error with errno set to the error code. + * + * Note: Index blocks are parsed in ascending vcn order, from which follows + * that the directory entries are not returned sorted. + */ +int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir) +{ + s64 i_size, br, ia_pos, bmp_pos, ia_start; + ntfs_volume *vol; + ntfs_attr *ia_na, *bmp_na = NULL; + ntfs_attr_search_ctx *ctx = NULL; + u8 *index_end, *bmp = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia = NULL; + int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; + u32 index_block_size; + u8 index_block_size_bits, index_vcn_size_bits; + + ntfs_log_trace("Entering.\n"); + + if (!dir_ni || !pos || !filldir) { + errno = EINVAL; + return -1; + } + + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = ENOTDIR; + return -1; + } + + vol = dir_ni->vol; + + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", + (unsigned long long)dir_ni->mft_no, (long long)*pos); + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to open index allocation attribute. " + "Directory inode %lld is corrupt or bug", + (unsigned long long)dir_ni->mft_no); + return -1; + } + i_size = 0; + } else + i_size = ia_na->data_size; + + rc = 0; + + /* Are we at end of dir yet? */ + if (*pos >= i_size + vol->mft_record_size) + goto done; + + /* Emulate . and .. for all directories. */ + if (!*pos) { + rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos, + MK_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)), + NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + if (*pos == 1) { + MFT_REF parent_mref; + + parent_mref = ntfs_mft_get_parent_ref(dir_ni); + if (parent_mref == ERR_MREF(-1)) { + ntfs_log_perror("Parent directory not found"); + goto dir_err_out; + } + + rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, + parent_mref, NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + goto err_out; + + /* Get the offset into the index root attribute. */ + ir_pos = (int)*pos; + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + /* Determine the size of a vcn in the directory index. */ + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto dir_err_out; + } + index_block_size_bits = ffs(index_block_size) - 1; + if (vol->cluster_size <= index_block_size) { + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + } + + /* Are we jumping straight into the index allocation attribute? */ + if (*pos >= vol->mft_record_size) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto skip_index_root; + } + + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until filldir tells us it has had enough + * or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) + goto dir_err_out; + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index root entry if continuing previous readdir. */ + if (ir_pos > (u8*)ie - (u8*)ir) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ROOT, ir, ie, dirent, filldir); + if (rc) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto err_out; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* If there is no index allocation attribute we are finished. */ + if (!ia_na) + goto EOD; + + /* Advance *pos to the beginning of the index allocation. */ + *pos = vol->mft_record_size; + +skip_index_root: + + if (!ia_na) + goto done; + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) + goto err_out; + + bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!bmp_na) { + ntfs_log_perror("Failed to open index bitmap attribute"); + goto dir_err_out; + } + + /* Get the offset into the index allocation attribute. */ + ia_pos = *pos - vol->mft_record_size; + + bmp_pos = ia_pos >> index_block_size_bits; + if (bmp_pos >> 3 >= bmp_na->data_size) { + ntfs_log_error("Current index position exceeds index bitmap " + "size.\n"); + goto dir_err_out; + } + + bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096); + bmp = ntfs_malloc(bmp_buf_size); + if (!bmp) + goto err_out; + + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + + bmp_buf_pos = 0; + /* If the index block is not in use find the next one that is. */ + while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) { +find_next_index_buffer: + bmp_pos++; + bmp_buf_pos++; + /* If we have reached the end of the bitmap, we are done. */ + if (bmp_pos >> 3 >= bmp_na->data_size) + goto EOD; + ia_pos = bmp_pos << index_block_size_bits; + if (bmp_buf_pos >> 3 < bmp_buf_size) + continue; + /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; + if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) + bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + } + + ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos); + + /* Read the index block starting at bmp_pos. */ + br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read index block"); + goto err_out; + } + + ia_start = ia_pos & ~(s64)(index_block_size - 1); + if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> + index_vcn_size_bits) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx) in inode 0x%llx.\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)ia_start >> + index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + + 0x18, (unsigned)index_block_size); + goto dir_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "%lld exceeds maximum size.\n", + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until ntfs_filldir tells us it has had + * enough or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index allocation, offset 0x%llx.\n", + (long long)ia_start + ((u8*)ie - (u8*)ia)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory inode " + "%lld.\n", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index entry if continuing previous readdir. */ + if (ia_pos - ia_start > (u8*)ie - (u8*)ia) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); + if (rc) + goto err_out; + } + goto find_next_index_buffer; +EOD: + /* We are finished, set *pos to EOD. */ + *pos = i_size + vol->mft_record_size; +done: + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); + return 0; +dir_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_log_trace("failed.\n"); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + errno = eo; + return -1; +} + + +/** + * __ntfs_create - create object on ntfs volume + * @dir_ni: ntfs inode for directory in which create new object + * @securid: id of inheritable security descriptor, 0 if none + * @name: unicode name of new object + * @name_len: length of the name in unicode characters + * @type: type of the object to create + * @dev: major and minor device numbers (obtained from makedev()) + * @target: target in unicode (only for symlinks) + * @target_len: length of target in unicode characters + * + * Internal, use ntfs_create{,_device,_symlink} wrappers instead. + * + * @type can be: + * S_IFREG to create regular file + * S_IFDIR to create directory + * S_IFBLK to create block device + * S_IFCHR to create character device + * S_IFLNK to create symbolic link + * S_IFIFO to create FIFO + * S_IFSOCK to create socket + * other values are invalid. + * + * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value + * ignored. + * + * @target and @target_len are used only if @type is S_IFLNK, in other cases + * their value ignored. + * + * Return opened ntfs inode that describes created object on success or NULL + * on error with errno set to the error code. + */ +static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev, + ntfschar *target, int target_len) +{ + ntfs_inode *ni; + int rollback_data = 0, rollback_sd = 0; + FILE_NAME_ATTR *fn = NULL; + STANDARD_INFORMATION *si = NULL; + int err, fn_len, si_len; + + ntfs_log_trace("Entering.\n"); + + /* Sanity checks. */ + if (!dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return NULL; + } + + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + return NULL; + } + + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); + if (!ni) + return NULL; +#if CACHE_NIDATA_SIZE + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); +#endif + /* + * Create STANDARD_INFORMATION attribute. + * JPA Depending on available inherited security descriptor, + * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3 + */ + if (securid) + si_len = sizeof(STANDARD_INFORMATION); + else + si_len = offsetof(STANDARD_INFORMATION, v1_end); + si = ntfs_calloc(si_len); + if (!si) { + err = errno; + goto err_out; + } + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; + if (securid) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = si->owner_id = 0; + ni->security_id = si->security_id = securid; + ni->quota_charged = si->quota_charged = const_cpu_to_le64(0); + ni->usn = si->usn = const_cpu_to_le64(0); + } else + clear_nino_flag(ni, v3_Extensions); + if (!S_ISREG(type) && !S_ISDIR(type)) { + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ + if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (S_ISREG(type) || S_ISDIR(type))) + ni->flags |= FILE_ATTR_COMPRESSED; + /* Add STANDARD_INFORMATION to inode. */ + if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + (u8*)si, si_len)) { + err = errno; + ntfs_log_error("Failed to add STANDARD_INFORMATION " + "attribute.\n"); + goto err_out; + } + + if (!securid) { + if (ntfs_sd_add_everyone(ni)) { + err = errno; + goto err_out; + } + } + rollback_sd = 1; + + if (S_ISDIR(type)) { + INDEX_ROOT *ir = NULL; + INDEX_ENTRY *ie; + int ir_len, index_len; + + /* Create INDEX_ROOT attribute. */ + index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); + ir_len = offsetof(INDEX_ROOT, index) + index_len; + ir = ntfs_calloc(ir_len); + if (!ir) { + err = errno; + goto err_out; + } + ir->type = AT_FILE_NAME; + ir->collation_rule = COLLATION_FILE_NAME; + ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); + if (ni->vol->cluster_size <= ni->vol->indx_record_size) + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->cluster_size_bits; + else + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + NTFS_BLOCK_SIZE_BITS; + ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); + ir->index.index_length = cpu_to_le32(index_len); + ir->index.allocated_size = cpu_to_le32(index_len); + ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + ie->key_length = 0; + ie->ie_flags = INDEX_ENTRY_END; + /* Add INDEX_ROOT attribute to inode. */ + if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, + (u8*)ir, ir_len)) { + err = errno; + free(ir); + ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); + goto err_out; + } + free(ir); + } else { + INTX_FILE *data; + int data_len; + + switch (type) { + case S_IFBLK: + case S_IFCHR: + data_len = offsetof(INTX_FILE, device_end); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic = INTX_CHARACTER_DEVICE; + break; + case S_IFLNK: + data_len = sizeof(INTX_FILE_TYPES) + + target_len * sizeof(ntfschar); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, + target_len * sizeof(ntfschar)); + break; + case S_IFSOCK: + data = NULL; + data_len = 1; + break; + default: /* FIFO or regular file. */ + data = NULL; + data_len = 0; + break; + } + /* Add DATA attribute to inode. */ + if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, + data_len)) { + err = errno; + ntfs_log_error("Failed to add DATA attribute.\n"); + free(data); + goto err_out; + } + rollback_data = 1; + free(data); + } + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + if (S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; + if (!S_ISREG(type) && !S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_SYSTEM; + else + fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + err = errno; + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + goto err_out; + } + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add entry to the index"); + goto err_out; + } + /* Set hard links count and directory flag. */ + ni->mrec->link_count = cpu_to_le16(1); + if (S_ISDIR(type)) + ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; + ntfs_inode_mark_dirty(ni); + /* Done! */ + free(fn); + free(si); + ntfs_log_trace("Done.\n"); + return ni; +err_out: + ntfs_log_trace("Failed.\n"); + + if (rollback_sd) + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + + if (rollback_data) + ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0); + /* + * Free extent MFT records (should not exist any with current + * ntfs_create implementation, but for any case if something will be + * changed in the future). + */ + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + if (ntfs_mft_record_free(ni->vol, ni)) + ntfs_log_error("Failed to free MFT record. " + "Leaving inconsistent metadata. Run chkdsk.\n"); + free(fn); + free(si); + errno = err; + return NULL; +} + +/** + * Some wrappers around __ntfs_create() ... + */ + +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name, + u8 name_len, mode_t type) +{ + if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && + type != S_IFSOCK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, 0, NULL, 0); +} + +ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev) +{ + if (type != S_IFCHR && type != S_IFBLK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, dev, NULL, 0); +} + +ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len) +{ + if (!target || !target_len) { + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, S_IFLNK, 0, + target, target_len); +} + +int ntfs_check_empty_dir(ntfs_inode *ni) +{ + ntfs_attr *na; + int ret = 0; + + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) + return 0; + + na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!na) { + errno = EIO; + ntfs_log_perror("Failed to open directory"); + return -1; + } + + /* Non-empty directory? */ + if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ + /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ + errno = ENOTEMPTY; + ntfs_log_debug("Directory is not empty\n"); + ret = -1; + } + + ntfs_attr_close(na); + return ret; +} + +static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) +{ + int link_count = le16_to_cpu(ni->mrec->link_count); + int ret; + + ret = ntfs_check_empty_dir(ni); + if (!ret || errno != ENOTEMPTY) + return ret; + /* + * Directory is non-empty, so we can unlink only if there is more than + * one "real" hard link, i.e. links aren't different DOS and WIN32 names + */ + if ((link_count == 1) || + (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { + errno = ENOTEMPTY; + ntfs_log_debug("Non-empty directory without hard links\n"); + goto no_hardlink; + } + + ret = 0; +no_hardlink: + return ret; +} + +/** + * ntfs_delete - delete file or directory from ntfs volume + * @ni: ntfs inode for object to delte + * @dir_ni: ntfs inode for directory in which delete object + * @name: unicode name of the object to delete + * @name_len: length of the name in unicode characters + * + * @ni is always closed after the call to this function (even if it failed), + * user does not need to call ntfs_inode_close himself. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_delete(ntfs_volume *vol, const char *pathname, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +{ + ntfs_attr_search_ctx *actx = NULL; + FILE_NAME_ATTR *fn = NULL; + BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; + BOOL case_sensitive_match = TRUE; + int err = 0; +#if CACHE_NIDATA_SIZE + int i; +#endif +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + const char *p; + u64 inum = (u64)-1; + int count; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP lkitem; +#endif + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + if (dir_ni->nr_extents == -1) + dir_ni = dir_ni->base_ni; + /* + * Search for FILE_NAME attribute with such name. If it's in POSIX or + * WIN32_AND_DOS namespace, then simply remove it from index and inode. + * If filename in DOS or in WIN32 namespace, then remove DOS name first, + * only then remove WIN32 name. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + goto err_out; +search: + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *s; + IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; + + errno = 0; + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + s = ntfs_attr_name_get(fn->file_name, fn->file_name_length); + ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d " + "case: %d\n", s, fn->file_name_type, + looking_for_dos_name, looking_for_win32_name, + case_sensitive_match); + ntfs_attr_name_free(&s); + if (looking_for_dos_name) { + if (fn->file_name_type == FILE_NAME_DOS) + break; + else + continue; + } + if (looking_for_win32_name) { + if (fn->file_name_type == FILE_NAME_WIN32) + break; + else + continue; + } + + /* Ignore hard links from other directories */ + if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { + ntfs_log_debug("MFT record numbers don't match " + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); + continue; + } + if (case_sensitive_match + || ((fn->file_name_type == FILE_NAME_POSIX) + && NVolCaseSensitive(ni->vol))) + case_sensitive = CASE_SENSITIVE; + + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, + name, name_len, case_sensitive, + ni->vol->upcase, ni->vol->upcase_len)){ + + if (fn->file_name_type == FILE_NAME_WIN32) { + looking_for_dos_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + continue; + } + if (fn->file_name_type == FILE_NAME_DOS) + looking_for_dos_name = TRUE; + break; + } + } + if (errno) { + /* + * If case sensitive search failed, then try once again + * ignoring case. + */ + if (errno == ENOENT && case_sensitive_match) { + case_sensitive_match = FALSE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + goto err_out; + } + + if (ntfs_check_unlinkable_dir(ni, fn) < 0) + goto err_out; + + if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length))) + goto err_out; + + if (ntfs_attr_record_rm(actx)) + goto err_out; + + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) - 1); + + ntfs_inode_mark_dirty(ni); + if (looking_for_dos_name) { + looking_for_dos_name = FALSE; + looking_for_win32_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + /* TODO: Update object id, quota and securiry indexes if required. */ + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ +#if CACHE_LOOKUP_SIZE + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); +#endif +#if CACHE_INODE_SIZE + inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); +#endif + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + goto ok; + } + if (ntfs_delete_reparse_index(ni)) { + /* + * Failed to remove the reparse index : proceed anyway + * This is not a critical error, the entry is useless + * because of sequence_number, and stopping file deletion + * would be much worse as the file is not referenced now. + */ + err = errno; + } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } + ntfs_attr_reinit_search_ctx(actx); + while (!ntfs_attrs_walk(actx)) { + if (actx->attr->non_resident) { + runlist *rl; + + rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, + NULL); + if (!rl) { + err = errno; + ntfs_log_error("Failed to decompress runlist. " + "Leaving inconsistent metadata.\n"); + continue; + } + if (ntfs_cluster_free_from_rl(ni->vol, rl)) { + err = errno; + ntfs_log_error("Failed to free clusters. " + "Leaving inconsistent metadata.\n"); + continue; + } + free(rl); + } + } + if (errno != ENOENT) { + err = errno; + ntfs_log_error("Attribute enumeration failed. " + "Probably leaving inconsistent metadata.\n"); + } + /* All extents should be attached after attribute walk. */ +#if CACHE_NIDATA_SIZE + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; +#else + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } +#endif + if (ntfs_mft_record_free(ni->vol, ni)) { + err = errno; + ntfs_log_error("Failed to free base MFT record. " + "Leaving inconsistent metadata.\n"); + } + ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); +out: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) + err = errno; + if (err) { + errno = err; + ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); + return -1; + } + ntfs_log_trace("Done.\n"); + return 0; +err_out: + err = errno; + goto out; +} + +/** + * ntfs_link - create hard link for file or directory + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters + * + * NOTE: At present we allow creating hardlinks to directories, we use them + * in a temporary state during rename. But it's defenitely bad idea to have + * hard links to directories as a result of operation. + * FIXME: Create internal __ntfs_link that allows hard links to a directories + * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len, FILE_NAME_TYPE_FLAGS nametype) +{ + FILE_NAME_ATTR *fn = NULL; + int fn_len, err; + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len || + ni->mft_no == dir_ni->mft_no) { + err = EINVAL; + ntfs_log_perror("ntfs_link wrong arguments"); + goto err_out; + } + + if ((ni->flags & FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + err = EOPNOTSUPP; + goto err_out; + } + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = nametype; + fn->file_attributes = ni->flags; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add filename to the index"); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; + /* Try to remove just added attribute from index. */ + if (ntfs_index_remove(dir_ni, ni, fn, fn_len)) + goto rollback_failed; + goto err_out; + } + /* Increment hard links count. */ + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) + 1); + /* Done! */ + ntfs_inode_mark_dirty(ni); + free(fn); + ntfs_log_trace("Done.\n"); + return 0; +rollback_failed: + ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n"); +err_out: + free(fn); + errno = err; + return -1; +} + +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +{ + return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); +} + +/* + * Get a parent directory from an inode entry + * + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. + * + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link + */ + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) +{ + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); + + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); +} + +#ifdef HAVE_SETXATTR + +#define MAX_DOS_NAME_LENGTH 12 + +/* + * Get a DOS name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_DOS) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a DOS or WIN32+DOS name for the entry + * copy name, after truncation for safety + */ + outsize = fn->file_name_length; +/* TODO : reject if name is too long ? */ + if (outsize > MAX_DOS_NAME_LENGTH) + outsize = MAX_DOS_NAME_LENGTH; + memcpy(dosname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + +/* + * Get a long name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + +/* + * Get the ntfs DOS name into an extended attribute + */ + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int outsize = 0; + char *outname = (char*)NULL; + u64 dnum; + int doslen; + ntfschar dosname[MAX_DOS_NAME_LENGTH]; + + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); + outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); + } + } else { + if (doslen == 0) + errno = ENODATA; + outsize = -errno; + } + return (outsize); +} + +/* + * Change the name space of an existing file or directory + * + * Returns the old namespace if successful + * -1 if an error occurred (described by errno) + */ + +static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *name, int len, + FILE_NAME_TYPE_FLAGS nametype) +{ + ntfs_attr_search_ctx *actx; + ntfs_index_context *icx; + FILE_NAME_ATTR *fnx; + FILE_NAME_ATTR *fn = NULL; + BOOL found; + int lkup; + int ret; + + ret = -1; + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (actx) { + found = FALSE; + do { + lkup = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx); + if (!lkup) { + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + found = (MREF_LE(fn->parent_directory) + == dir_ni->mft_no) + && !memcmp(fn->file_name, name, + len*sizeof(ntfschar)); + } + } while (!lkup && !found); + if (found) { + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + lkup = ntfs_index_lookup((char*)fn, len, icx); + if (!lkup && icx->data && icx->data_len) { + fnx = (FILE_NAME_ATTR*)icx->data; + ret = fn->file_name_type; + fn->file_name_type = nametype; + fnx->file_name_type = nametype; + ntfs_inode_mark_dirty(ni); + ntfs_index_entry_mark_dirty(icx); + } + ntfs_index_ctx_put(icx); + } + } + ntfs_attr_put_search_ctx(actx); + } + return (ret); +} + +/* + * Set a DOS name to a file and adjust name spaces + * + * If the new names are collapsible (same uppercased chars) : + * + * - the existing DOS name or DOS+Win32 name is made Posix + * - if it was a real DOS name, the existing long name is made DOS+Win32 + * and the existing DOS name is deleted + * - finally the existing long name is made DOS+Win32 unless already done + * + * If the new names are not collapsible : + * + * - insert the short name as a DOS name + * - delete the old long name or existing short name + * - insert the new long name (as a Win32 or DOS+Win32 name) + * + * Deleting the old long name will not delete the file + * provided the old name was in the Posix name space, + * because the alternate name has been set before. + * + * The inodes of file and parent directory are always closed + * + * Returns 0 if successful + * -1 if failed + */ + +static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + ntfschar *shortname, int shortlen, + ntfschar *longname, int longlen, + ntfschar *deletename, int deletelen, BOOL existed) +{ + unsigned int linkcount; + ntfs_volume *vol; + BOOL collapsible; + BOOL deleted; + BOOL done; + FILE_NAME_TYPE_FLAGS oldnametype; + u64 dnum; + u64 fnum; + int res; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + fnum = ni->mft_no; + /* save initial link count */ + linkcount = le16_to_cpu(ni->mrec->link_count); + + /* check whether the same name may be used as DOS and WIN32 */ + collapsible = ntfs_collapsible_chars(ni->vol, shortname, shortlen, + longname, longlen); + if (collapsible) { + deleted = FALSE; + done = FALSE; + if (existed) { + oldnametype = set_namespace(ni, dir_ni, deletename, + deletelen, FILE_NAME_POSIX); + if (oldnametype == FILE_NAME_DOS) { + if (set_namespace(ni, dir_ni, longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, dir_ni, + deletename, deletelen)) + res = 0; + deleted = TRUE; + } else + done = TRUE; + } + } + if (!deleted) { + if (!done && (set_namespace(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0)) + res = 0; + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } else { + if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, + FILE_NAME_DOS) + /* make sure a new link was recorded */ + && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { + /* delete the existing long name or short name */ +// is it ok to not provide the path ? + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { + /* delete closes the inodes, so have to open again */ + dir_ni = ntfs_inode_open(vol, dnum); + if (dir_ni) { + ni = ntfs_inode_open(vol, fnum); + if (ni) { + if (!ntfs_link_i(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32)) + res = 0; + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; + } + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } + } else { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + } + return (res); +} + + +/* + * Set the ntfs DOS name into an extended attribute + * + * The DOS name will be added as another file name attribute + * using the existing file name information from the original + * name or overwriting the DOS Name if one exists. + * + * The inode of the file is always closed + */ + +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res = 0; + int longlen = 0; + int shortlen = 0; + char newname[3*MAX_DOS_NAME_LENGTH + 1]; + ntfschar oldname[MAX_DOS_NAME_LENGTH]; + int oldlen; + u64 dnum; + BOOL closed = FALSE; + ntfschar *shortname = NULL; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + /* copy the string to insert a null char, and truncate */ + if (size > 3*MAX_DOS_NAME_LENGTH) + size = 3*MAX_DOS_NAME_LENGTH; + strncpy(newname, value, size); + /* a long name may be truncated badly and be untranslatable */ + newname[size] = 0; + /* convert the string to the NTFS wide chars, and truncate */ + shortlen = ntfs_mbstoucs(newname, &shortname); + if (shortlen > MAX_DOS_NAME_LENGTH) + shortlen = MAX_DOS_NAME_LENGTH; + /* make sure the short name has valid chars */ + if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + res = -errno; + return res; + } + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + oldlen = get_dos_name(ni, dnum, oldname); + if ((oldlen >= 0) + && !ntfs_forbidden_chars(longname, longlen)) { + if (oldlen > 0) { + if (flags & XATTR_CREATE) { + res = -1; + errno = EEXIST; + } else + if ((shortlen == oldlen) + && !memcmp(shortname,oldname, + oldlen*sizeof(ntfschar))) + /* already set, done */ + res = 0; + else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + oldname, oldlen, TRUE); + closed = TRUE; + } + } else { + if (flags & XATTR_REPLACE) { + res = -1; + errno = ENODATA; + } else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + longname, longlen, FALSE); + closed = TRUE; + } + } + } else + res = -1; + } else { + res = -1; + errno = ENOENT; + } + free(shortname); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res ? -1 : 0); +} + +/* + * Delete the ntfs DOS name + */ + +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + int oldnametype; + int longlen = 0; + int shortlen; + u64 dnum; + ntfs_volume *vol; + BOOL deleted = FALSE; + ntfschar shortname[MAX_DOS_NAME_LENGTH]; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + shortlen = get_dos_name(ni, dnum, shortname); + if (shortlen >= 0) { + /* migrate the long name as Posix */ + oldnametype = set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_POSIX); + switch (oldnametype) { + case FILE_NAME_WIN32_AND_DOS : + /* name was Win32+DOS : done */ + res = 0; + break; + case FILE_NAME_DOS : + /* name was DOS, make it back to DOS */ + set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_DOS); + errno = ENOENT; + break; + case FILE_NAME_WIN32 : + /* name was Win32, make it Posix and delete */ + if (set_namespace(ni,dir_ni,shortname,shortlen, + FILE_NAME_POSIX) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, + dir_ni, shortname, + shortlen)) + res = 0; + deleted = TRUE; + } else { + /* + * DOS name has been found, but cannot + * migrate to Posix : something bad + * has happened + */ + errno = EIO; + ntfs_log_error("Could not change" + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); + } + break; + default : + /* name was Posix or not found : error */ + errno = ENOENT; + break; + } + } + } else { + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res); +} + +#endif diff --git a/lib/libntfs/src/source/dir.h b/lib/libntfs/src/source/dir.h new file mode 100644 index 0000000..56e76fe --- /dev/null +++ b/lib/libntfs/src/source/dir.h @@ -0,0 +1,128 @@ +/* + * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DIR_H +#define _NTFS_DIR_H + +#include "types.h" + +#define PATH_SEP '/' + +/* + * We do not have these under DJGPP, so define our version that do not conflict + * with other S_IFs defined under DJGPP. + */ +#ifdef DJGPP +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#ifndef S_IFSOCK +#define S_IFSOCK 0140000 +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#endif + +/* + * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R + * as a global constant. + */ +extern ntfschar NTFS_INDEX_I30[5]; +extern ntfschar NTFS_INDEX_SII[5]; +extern ntfschar NTFS_INDEX_SDH[5]; +extern ntfschar NTFS_INDEX_O[3]; +extern ntfschar NTFS_INDEX_Q[3]; +extern ntfschar NTFS_INDEX_R[3]; + +extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); + +extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname); +extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type); +extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, mode_t type, dev_t dev); +extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + ntfschar *name, u8 name_len, ntfschar *target, int target_len); +extern int ntfs_check_empty_dir(ntfs_inode *ni); +extern int ntfs_delete(ntfs_volume *vol, const char *path, + ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); + +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); + +/* + * File types (adapted from include ) + */ +#define NTFS_DT_UNKNOWN 0 +#define NTFS_DT_FIFO 1 +#define NTFS_DT_CHR 2 +#define NTFS_DT_DIR 4 +#define NTFS_DT_BLK 6 +#define NTFS_DT_REG 8 +#define NTFS_DT_LNK 10 +#define NTFS_DT_SOCK 12 +#define NTFS_DT_WHT 14 + +/* + * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let + * the caller specify what kind of dirent layout it wants to have. + * This allows the caller to read directories into their application or + * to have different dirent layouts depending on the binary type. + */ +typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, + const int name_len, const int name_type, const s64 pos, + const MFT_REF mref, const unsigned dt_type); + +extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir); + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_INODE_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); + +#endif + +#endif /* defined _NTFS_DIR_H */ + diff --git a/lib/libntfs/src/source/efs.c b/lib/libntfs/src/source/efs.c new file mode 100644 index 0000000..7957005 --- /dev/null +++ b/lib/libntfs/src/source/efs.c @@ -0,0 +1,437 @@ +/** + * efs.c - Limited processing of encrypted files + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Martin Bene + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "efs.h" +#include "index.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +static ntfschar logged_utility_stream_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('E'), + const_cpu_to_le16('F'), + const_cpu_to_le16('S'), + const_cpu_to_le16(0) +} ; + + +/* + * Get the ntfs EFS info into an extended attribute + */ + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) +{ + EFS_ATTR_HEADER *efs_info; + s64 attr_size = 0; + + if (ni) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, + &attr_size); + if (efs_info + && (le32_to_cpu(efs_info->length) == attr_size)) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,efs_info,attr_size); + else { + errno = EFAULT; + attr_size = 0; + } + } else + if (size) { + errno = ERANGE; + attr_size = 0; + } + free (efs_info); + } else { + if (efs_info) { + free(efs_info); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); + } else { + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); + } + errno = EIO; + attr_size = 0; + } + } else { + errno = ENODATA; + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); + } + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Fix all encrypted AT_DATA attributes of an inode + * + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. + * + * Returns zero if successful. + * -1 if there is a problem. + */ + +static int fixup_loop(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + int cnt; + int maxcnt; + int res = 0; + + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); +} + +/* + * Set the efs data from an extended attribute + * Warning : the new data is not checked + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + +{ + int res; + int written; + ntfs_attr *na; + const EFS_ATTR_HEADER *info_header; + + res = 0; + if (ni && value && size) { + if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); + errno = EEXIST; + } else { + /* + * Possible problem : if encrypted file was + * restored in a compressed directory, it was + * restored as compressed. + * TODO : decompress first. + */ + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); + errno = EIO; + } + return -1; + } + info_header = (const EFS_ATTR_HEADER*)value; + /* make sure we get a likely efsinfo */ + if (le32_to_cpu(info_header->length) != size) { + errno = EINVAL; + return (-1); + } + if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, + (ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no logged_utility_stream attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name,4, + (u8*)NULL,(s64)size); + } else { + errno = ENODATA; + res = -1; + } + } else { + errno = EEXIST; + res = -1; + } + if (!res) { + /* + * open and update the existing efs data + */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name, 4); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to " + "update efs data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + } else + res = -1; + } + if (!res) { + /* Don't handle AT_DATA Attribute(s) if inode is a directory */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* iterate over AT_DATA attributes */ + /* set encrypted flag, truncate attribute to match padding bytes */ + + if (fixup_loop(ni)) + return -1; + } + ni->flags |= FILE_ATTR_ENCRYPTED; + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Fixup raw encrypted AT_DATA Attribute + * read padding length from last two bytes + * truncate attribute, make non-resident, + * set data size to match padding length + * set ATTR_IS_ENCRYPTED flag on attribute + * + * Return 0 if successful + * -1 if failed (errno tells why) + */ + +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) +{ + u64 newsize; + u64 oldsize; + le16 appended_bytes; + u16 padding_length; + ntfs_inode *ni; + BOOL close_ctx = FALSE; + + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); + goto err_out; + } + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + goto err_out; + } + close_ctx = TRUE; + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; + } + } + + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; + + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } + } + ni = na->ni; + if (!na->name_len) { + ni->data_size = newsize; + ni->allocated_size = na->allocated_size; + } + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + + ctx->attr->data_size = cpu_to_le64(newsize); + if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->initialized_size = ctx->attr->data_size; + ctx->attr->flags |= ATTR_IS_ENCRYPTED; + if (close_ctx) + ntfs_attr_put_search_ctx(ctx); + + return (0); +err_out: + if (close_ctx && ctx) + ntfs_attr_put_search_ctx(ctx); + return (-1); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/src/source/efs.h b/lib/libntfs/src/source/efs.h new file mode 100644 index 0000000..6eada06 --- /dev/null +++ b/lib/libntfs/src/source/efs.h @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2009 Martin Bene + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFS_H +#define EFS_H + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); + +#endif /* EFS_H */ diff --git a/lib/libntfs/src/source/endians.h b/lib/libntfs/src/source/endians.h new file mode 100644 index 0000000..397f1c2 --- /dev/null +++ b/lib/libntfs/src/source/endians.h @@ -0,0 +1,203 @@ +/* + * endians.h - Definitions related to handling of byte ordering. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ENDIANS_H +#define _NTFS_ENDIANS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * Notes: + * We define the conversion functions including typecasts since the + * defaults don't necessarily perform appropriate typecasts. + * Also, using our own functions means that we can change them if it + * turns out that we do need to use the unaligned access macros on + * architectures requiring aligned memory accesses... + */ + +#ifdef HAVE_ENDIAN_H +#include +#endif +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif +#ifdef HAVE_MACHINE_ENDIAN_H +#include +#endif +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef __BYTE_ORDER +# if defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# elif defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(__BYTE_ORDER__) +# define __BYTE_ORDER __BYTE_ORDER__ +# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ +# define __BIG_ENDIAN __BIG_ENDIAN__ +# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ + defined(WORDS_LITTLEENDIAN) +# define __BYTE_ORDER 1 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ + defined(WORDS_BIGENDIAN) +# define __BYTE_ORDER 0 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# else +# error "__BYTE_ORDER is not defined." +# endif +#endif + +#define __ntfs_bswap_constant_16(x) \ + (u16)((((u16)(x) & 0xff00) >> 8) | \ + (((u16)(x) & 0x00ff) << 8)) + +#define __ntfs_bswap_constant_32(x) \ + (u32)((((u32)(x) & 0xff000000u) >> 24) | \ + (((u32)(x) & 0x00ff0000u) >> 8) | \ + (((u32)(x) & 0x0000ff00u) << 8) | \ + (((u32)(x) & 0x000000ffu) << 24)) + +#define __ntfs_bswap_constant_64(x) \ + (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ + (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ + (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ + (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ + (((u64)(x) & 0x00000000ff000000ull) << 8) | \ + (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ + (((u64)(x) & 0x000000000000ff00ull) << 40) | \ + (((u64)(x) & 0x00000000000000ffull) << 56)) + +#ifdef HAVE_BYTESWAP_H +# include +#else +# define bswap_16(x) __ntfs_bswap_constant_16(x) +# define bswap_32(x) __ntfs_bswap_constant_32(x) +# define bswap_64(x) __ntfs_bswap_constant_64(x) +#endif + +#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) + +#define __le16_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le64_to_cpu(x) (x) + +#define __cpu_to_le16(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le64(x) (x) + +#define __constant_le16_to_cpu(x) (x) +#define __constant_le32_to_cpu(x) (x) +#define __constant_le64_to_cpu(x) (x) + +#define __constant_cpu_to_le16(x) (x) +#define __constant_cpu_to_le32(x) (x) +#define __constant_cpu_to_le64(x) (x) + +#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) + +#define __le16_to_cpu(x) bswap_16(x) +#define __le32_to_cpu(x) bswap_32(x) +#define __le64_to_cpu(x) bswap_64(x) + +#define __cpu_to_le16(x) bswap_16(x) +#define __cpu_to_le32(x) bswap_32(x) +#define __cpu_to_le64(x) bswap_64(x) + +#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) + +#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) + +#else + +#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." + +#endif + +/* Unsigned from LE to CPU conversion. */ + +#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) +#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) +#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) + +#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) +#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) +#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) + +/* Signed from LE to CPU conversion. */ + +#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) +#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) +#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) + +#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) +#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) +#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) + +/* Unsigned from CPU to LE conversion. */ + +#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) +#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) +#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) + +#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) +#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) +#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) + +/* Signed from CPU to LE conversion. */ + +#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) +#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) +#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) + +#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) +#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) +#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) + +/* Constant endianness conversion defines. */ + +#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) +#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) +#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) + +#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) +#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) +#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) + +#endif /* defined _NTFS_ENDIANS_H */ diff --git a/lib/libntfs/src/source/gekko_io.c b/lib/libntfs/src/source/gekko_io.c new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/lib/libntfs/src/source/gekko_io.c @@ -0,0 +1,658 @@ +/** + * gekko_io.c - Gekko style disk io functions. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "ntfs.h" +#include "types.h" +#include "logging.h" +#include "device_io.h" +#include "gekko_io.h" +#include "cache.h" +#include "device.h" +#include "bootsect.h" + +#define DEV_FD(dev) ((gekko_fd *)dev->d_private) + +/* Prototypes */ +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf); +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer); +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf); +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer); + +/** + * + */ +static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) +{ + ntfs_log_trace("dev %p, flags %i\n", dev, flags); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Start the device interface and ensure that it is inserted + if (!interface->startup()) { + ntfs_log_perror("device failed to start\n"); + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + ntfs_log_perror("device media is not inserted\n"); + errno = EIO; + return -1; + } + + // Check that the device isn't already open (used by another volume?) + if (NDevOpen(dev)) { + ntfs_log_perror("device is busy (already open)\n"); + errno = EBUSY; + return -1; + } + + // Check that there is a valid NTFS boot sector at the start of the device + NTFS_BOOT_SECTOR *boot = (NTFS_BOOT_SECTOR *) ntfs_alloc(MAX_SECTOR_SIZE); + if(boot == NULL) { + errno = ENOMEM; + return -1; + } + + if (!interface->readSectors(fd->startSector, 1, boot)) { + ntfs_log_perror("read failure @ sector %d\n", fd->startSector); + errno = EIO; + ntfs_free(boot); + return -1; + } + + if (!ntfs_boot_sector_is_ntfs(boot)) { + errno = EINVALPART; + ntfs_free(boot); + return -1; + } + + // Parse the boot sector + fd->hiddenSectors = le32_to_cpu(boot->bpb.hidden_sectors); + fd->sectorSize = le16_to_cpu(boot->bpb.bytes_per_sector); + fd->sectorCount = sle64_to_cpu(boot->number_of_sectors); + fd->pos = 0; + fd->len = (fd->sectorCount * fd->sectorSize); + fd->ino = le64_to_cpu(boot->volume_serial_number); + + // Free memory for boot sector + ntfs_free(boot); + + // Mark the device as read-only (if required) + if (flags & O_RDONLY) { + NDevSetReadOnly(dev); + } + + // Create the cache + fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); + + // Mark the device as open + NDevSetBlock(dev); + NDevSetOpen(dev); + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_close(struct ntfs_device *dev) +{ + ntfs_log_trace("dev %p\n", dev); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Check that the device is actually open + if (!NDevOpen(dev)) { + ntfs_log_perror("device is not open\n"); + errno = EIO; + return -1; + } + + // Mark the device as closed + NDevClearOpen(dev); + NDevClearBlock(dev); + + // Flush the device (if dirty and not read-only) + if (NDevDirty(dev) && !NDevReadOnly(dev)) { + ntfs_log_debug("device is dirty, will now sync\n"); + + // ...? + + // Mark the device as clean + NDevClearDirty(dev); + + } + + // Flush and destroy the cache (if required) + if (fd->cache) { + _NTFS_cache_flush(fd->cache); + _NTFS_cache_destructor(fd->cache); + } + + // Shutdown the device interface + /*const DISC_INTERFACE* interface = fd->interface; + if (interface) { + interface->shutdown(); + }*/ + + // Free the device driver private data + ntfs_free(dev->d_private); + dev->d_private = NULL; + + return 0; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_seek(struct ntfs_device *dev, s64 offset, int whence) +{ + ntfs_log_trace("dev %p, offset %Li, whence %i\n", dev, offset, whence); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Set the current position on the device (in bytes) + switch(whence) { + case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break; + case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break; + case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break; + } + + return 0; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_read(struct ntfs_device *dev, void *buf, s64 count) +{ + return ntfs_device_gekko_io_readbytes(dev, DEV_FD(dev)->pos, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_write(struct ntfs_device *dev, const void *buf, s64 count) +{ + return ntfs_device_gekko_io_writebytes(dev, DEV_FD(dev)->pos, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset) +{ + return ntfs_device_gekko_io_readbytes(dev, offset, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_pwrite(struct ntfs_device *dev, const void *buf, s64 count, s64 offset) +{ + return ntfs_device_gekko_io_writebytes(dev, offset, count, buf); +} + +/** + * + */ +static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf) +{ + ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(offset < 0) + { + errno = EROFS; + return -1; + } + + if(!count) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this read + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // If this read happens to be on the sector boundaries then do the read straight into the destination buffer + + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { + + // Read from the device + ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + + // Else read into a buffer and copy over only what was requested + } + else + { + + // Allocate a buffer to hold the read data + buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + + // Read from the device + ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + ntfs_free(buffer); + errno = EIO; + return -1; + } + + // Copy what was requested to the destination buffer + memcpy(buf, buffer + buffer_offset, count); + ntfs_free(buffer); + + } + + return count; +} + +/** + * + */ +static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf) +{ + ntfs_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Check that the device can be written to + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this write + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // If this write happens to be on the sector boundaries then do the write straight to disc + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Write to the device + ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { + ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else write from a buffer aligned to the sector boundaries + } + else + { + // Allocate a buffer to hold the write data + buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + // Read the first and last sectors of the buffer from disc (if required) + // NOTE: This is done because the data does not line up with the sector boundaries, + // we just read in the buffer edges where the data overlaps with the rest of the disc + if(buffer_offset != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ntfs_log_perror("read failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + + // Copy the data into the write buffer + memcpy(buffer + buffer_offset, buf, count); + + // Write to the device + ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) { + ntfs_log_perror("buffered write failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + + // Free the buffer + ntfs_free(buffer); + } + + // Mark the device as dirty (if we actually wrote anything) + NDevSetDirty(dev); + + return count; +} + +static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + // Read the sectors from disc (or cache, if enabled) + if (fd->cache) + return _NTFS_cache_readSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->readSectors(sector, numSectors, buffer); + + return false; +} + +static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + + // Write the sectors to disc (or cache, if enabled) + if (fd->cache) + return _NTFS_cache_writeSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->writeSectors(sector, numSectors, buffer); + + return false; +} + +/** + * + */ +static int ntfs_device_gekko_io_sync(struct ntfs_device *dev) +{ + gekko_fd *fd = DEV_FD(dev); + ntfs_log_trace("dev %p\n", dev); + + // Check that the device can be written to + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + + // Mark the device as clean + NDevClearDirty(dev); + NDevClearSync(dev); + + // Flush any sectors in the disc cache (if required) + if (fd->cache) { + if (!_NTFS_cache_flush(fd->cache)) { + errno = EIO; + return -1; + } + } + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_stat(struct ntfs_device *dev, struct stat *buf) +{ + ntfs_log_trace("dev %p, buf %p\n", dev, buf); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Build the device mode + mode_t mode = (S_IFBLK) | + (S_IRUSR | S_IRGRP | S_IROTH) | + ((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct stat)); + + // Build the device stats + buf->st_dev = fd->interface->ioType; + buf->st_ino = fd->ino; + buf->st_mode = mode; + buf->st_rdev = fd->interface->ioType; + buf->st_blksize = fd->sectorSize; + buf->st_blocks = fd->sectorCount; + + return 0; +} + +/** + * + */ +static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void *argp) +{ + ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Figure out which i/o control was requested + switch (request) { + + // Get block device size (sectors) + #if defined(BLKGETSIZE) + case BLKGETSIZE: { + *(u32*)argp = fd->sectorCount; + return 0; + } + #endif + + // Get block device size (bytes) + #if defined(BLKGETSIZE64) + case BLKGETSIZE64: { + *(u64*)argp = (fd->sectorCount * fd->sectorSize); + return 0; + } + #endif + + // Get hard drive geometry + #if defined(HDIO_GETGEO) + case HDIO_GETGEO: { + struct hd_geometry *geo = (struct hd_geometry*)argp; + geo->sectors = 0; + geo->heads = 0; + geo->cylinders = 0; + geo->start = fd->hiddenSectors; + return -1; + } + #endif + + // Get block device sector size (bytes) + #if defined(BLKSSZGET) + case BLKSSZGET: { + *(int*)argp = fd->sectorSize; + return 0; + } + #endif + + // Set block device block size (bytes) + #if defined(BLKBSZSET) + case BLKBSZSET: { + int sectorSize = *(int*)argp; + fd->sectorSize = sectorSize; + return 0; + } + #endif + + // Unimplemented ioctrl + default: { + ntfs_log_perror("Unimplemented ioctrl %i\n", request); + errno = EOPNOTSUPP; + return -1; + } + + } + + return 0; +} + +/** + * Device operations for working with gekko style devices and files. + */ +struct ntfs_device_operations ntfs_device_gekko_io_ops = { + .open = ntfs_device_gekko_io_open, + .close = ntfs_device_gekko_io_close, + .seek = ntfs_device_gekko_io_seek, + .read = ntfs_device_gekko_io_read, + .write = ntfs_device_gekko_io_write, + .pread = ntfs_device_gekko_io_pread, + .pwrite = ntfs_device_gekko_io_pwrite, + .sync = ntfs_device_gekko_io_sync, + .stat = ntfs_device_gekko_io_stat, + .ioctl = ntfs_device_gekko_io_ioctl, +}; diff --git a/lib/libntfs/src/source/gekko_io.h b/lib/libntfs/src/source/gekko_io.h new file mode 100644 index 0000000..bc5516a --- /dev/null +++ b/lib/libntfs/src/source/gekko_io.h @@ -0,0 +1,58 @@ +/* +* gekko_io.h - Platform specifics for device io. +* +* Copyright (c) 2009 Rhys "Shareese" Koedijk +* +* This program/include file is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published +* by the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program/include file is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _GEKKO_IO_H +#define _GEKKO_IO_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "types.h" +#include "cache2.h" +#include +#include + +#define MAX_SECTOR_SIZE 4096 + +/** + * gekko_fd - Gekko device driver descriptor + */ +typedef struct _gekko_fd { + const DISC_INTERFACE* interface; /* Device disc interface */ + sec_t startSector; /* LBA of partition start */ + sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */ + u16 sectorSize; /* Device sector size (in bytes) */ + u64 sectorCount; /* Total number of sectors in partition */ + u64 pos; /* Current position within the partition (in bytes) */ + u64 len; /* Total length of partition (in bytes) */ + ino_t ino; /* Device identifier */ + NTFS_CACHE *cache; /* Cache */ + u32 cachePageCount; /* The number of pages in the cache */ + u32 cachePageSize; /* The number of sectors per cache page */ +} gekko_fd; + +/* Forward declarations */ +struct ntfs_device_operations; + +/* Gekko device driver i/o operations */ +extern struct ntfs_device_operations ntfs_device_gekko_io_ops; + +#endif /* _GEKKO_IO_H */ diff --git a/lib/libntfs/src/source/index.c b/lib/libntfs/src/source/index.c new file mode 100644 index 0000000..d498dde --- /dev/null +++ b/lib/libntfs/src/source/index.c @@ -0,0 +1,2085 @@ +/** + * index.c - NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2007 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "index.h" +#include "collate.h" +#include "mst.h" +#include "dir.h" +#include "logging.h" +#include "bitmap.h" +#include "reparse.h" +#include "misc.h" + +/** + * ntfs_index_entry_mark_dirty - mark an index entry dirty + * @ictx: ntfs index context describing the index entry + * + * Mark the index entry described by the index entry context @ictx dirty. + * + * If the index entry is in the index root attribute, simply mark the inode + * containing the index root attribute dirty. This ensures the mftrecord, and + * hence the index root attribute, will be written out to disk later. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, set ib_dirty to TRUE, thus index block will be updated during + * ntfs_index_ctx_put. + */ +void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + ntfs_inode_mark_dirty(ictx->actx->ntfs_ino); + else + ictx->ib_dirty = TRUE; +} + +static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return vcn << icx->vcn_size_bits; +} + +static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return pos >> icx->vcn_size_bits; +} + +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), + 1, icx->block_size, ib); + if (ret != 1) { + ntfs_log_perror("Failed to write index block %lld, inode %llu", + (long long)vcn, (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_icx_ib_write(ntfs_index_context *icx) +{ + if (ntfs_ib_write(icx, icx->ib)) + return STATUS_ERROR; + + icx->ib_dirty = FALSE; + + return STATUS_OK; +} + +/** + * ntfs_index_ctx_get - allocate and initialize a new index context + * @ni: ntfs inode with which to initialize the context + * @name: name of the which context describes + * @name_len: length of the index name + * + * Allocate a new index context, initialize it with @ni and return it. + * Return NULL if allocation failed. + */ +ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len) +{ + ntfs_index_context *icx; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + errno = EINVAL; + return NULL; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + icx = ntfs_calloc(sizeof(ntfs_index_context)); + if (icx) + *icx = (ntfs_index_context) { + .ni = ni, + .name = name, + .name_len = name_len, + }; + return icx; +} + +static void ntfs_index_ctx_free(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (!icx->entry) + return; + + if (icx->actx) + ntfs_attr_put_search_ctx(icx->actx); + + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); + } + + ntfs_attr_close(icx->ia_na); +} + +/** + * ntfs_index_ctx_put - release an index context + * @icx: index context to free + * + * Release the index context @icx, releasing all associated resources. + */ +void ntfs_index_ctx_put(ntfs_index_context *icx) +{ + ntfs_index_ctx_free(icx); + free(icx); +} + +/** + * ntfs_index_ctx_reinit - reinitialize an index context + * @icx: index context to reinitialize + * + * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. + */ +void ntfs_index_ctx_reinit(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + ntfs_index_ctx_free(icx); + + *icx = (ntfs_index_context) { + .ni = icx->ni, + .name = icx->name, + .name_len = icx->name_len, + }; +} + +static VCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) +{ + return (VCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); +} + +/** + * Get the subnode vcn to which the index entry refers. + */ +VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) +{ + return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie)); +} + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); +} + +static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie) +{ + return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length)); +} + +static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) +{ + /* FIXME: check if it isn't overflowing the index block size */ + return (u8 *)ih + le32_to_cpu(ih->index_length); +} + +static int ntfs_ie_end(INDEX_ENTRY *ie) +{ + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; +} + +/** + * Find the last entry in the index block + */ +static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end) +{ + ntfs_log_trace("Entering\n"); + + while ((char *)ie < ies_end && !ntfs_ie_end(ie)) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("pos: %d\n", pos); + + ie = ntfs_ie_get_first(ih); + + while (pos-- > 0) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + INDEX_ENTRY *ie_prev = NULL; + INDEX_ENTRY *tmp; + + ntfs_log_trace("Entering\n"); + + tmp = ntfs_ie_get_first(ih); + + while (tmp != ie) { + ie_prev = tmp; + tmp = ntfs_ie_get_next(tmp); + } + + return ie_prev; +} + +char *ntfs_ie_filename_get(INDEX_ENTRY *ie) +{ + FILE_NAME_ATTR *fn; + + fn = (FILE_NAME_ATTR *)&ie->key; + return ntfs_attr_name_get(fn->file_name, fn->file_name_length); +} + +void ntfs_ie_filename_dump(INDEX_ENTRY *ie) +{ + char *s; + + s = ntfs_ie_filename_get(ie); + ntfs_log_debug("'%s' ", s); + ntfs_attr_name_free(&s); +} + +void ntfs_ih_filename_dump(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + while (!ntfs_ie_end(ie)) { + ntfs_ie_filename_dump(ie); + ie = ntfs_ie_get_next(ie); + } +} + +static int ntfs_ih_numof_entries(INDEX_HEADER *ih) +{ + int n; + INDEX_ENTRY *ie; + u8 *end; + + ntfs_log_trace("Entering\n"); + + end = ntfs_ie_get_end(ih); + ie = ntfs_ie_get_first(ih); + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) + ie = ntfs_ie_get_next(ie); + return n; +} + +static int ntfs_ih_one_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 1); +} + +static int ntfs_ih_zero_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 0); +} + +static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + u32 new_size; + + ntfs_log_trace("Entering\n"); + + new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); + ih->index_length = cpu_to_le32(new_size); + memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), + new_size - ((u8 *)ie - (u8 *)ih)); +} + +static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) +{ + *ntfs_ie_get_vcn_addr(ie) = cpu_to_le64(vcn); +} + +/** + * Insert @ie index entry at @pos entry. Used @ih values should be ok already. + */ +static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos) +{ + int ie_size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); + memmove((u8 *)pos + ie_size, pos, + le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size); + memcpy(pos, ie, ie_size); +} + +static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + + ntfs_log_trace("Entering\n"); + + dup = ntfs_malloc(le16_to_cpu(ie->length)); + if (dup) + memcpy(dup, ie, le16_to_cpu(ie->length)); + + return dup; +} + +static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + int size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + if (ie->ie_flags & INDEX_ENTRY_NODE) + size -= sizeof(VCN); + + dup = ntfs_malloc(size); + if (dup) { + memcpy(dup, ie, size); + dup->ie_flags &= ~INDEX_ENTRY_NODE; + dup->length = cpu_to_le16(size); + } + return dup; +} + +static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) +{ + u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; + + ntfs_log_trace("Entering\n"); + + if (!ntfs_is_indx_record(ib->magic)) { + + ntfs_log_error("Corrupt index block signature: vcn %lld inode " + "%llu\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (sle64_to_cpu(ib->index_block_vcn) != vcn) { + + ntfs_log_error("Corrupt index block: VCN (%lld) is different " + "from expected VCN (%lld) in inode %llu\n", + (long long)sle64_to_cpu(ib->index_block_vcn), + (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (ib_size != icx->block_size) { + + ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " + "has a size (%u) differing from the index " + "specified size (%u)\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no, ib_size, + icx->block_size); + return -1; + } + return 0; +} + +static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, + u32 name_len, ntfs_attr_search_ctx **ctx) +{ + ATTR_RECORD *a; + INDEX_ROOT *ir = NULL; + + ntfs_log_trace("Entering\n"); + + *ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!*ctx) + return NULL; + + if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) { + ntfs_log_perror("Failed to lookup $INDEX_ROOT"); + goto err_out; + } + + a = (*ctx)->attr; + if (a->non_resident) { + errno = EINVAL; + ntfs_log_perror("Non-resident $INDEX_ROOT detected"); + goto err_out; + } + + ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); +err_out: + if (!ir) { + ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; +} + +static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) +{ + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); + return ir; +} + +/** + * Find a key in the index block. + * + * Return values: + * STATUS_OK with errno set to ESUCCESS if we know for sure that the + * entry exists and @ie_out points to this entry. + * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the + * entry doesn't exist and @ie_out is the insertion point. + * STATUS_KEEP_SEARCHING if we can't answer the above question and + * @vcn will contain the node index block. + * STATUS_ERROR with errno set if on unexpected error during lookup. + */ +static int ntfs_ie_lookup(const void *key, const int key_len, + ntfs_index_context *icx, INDEX_HEADER *ih, + VCN *vcn, INDEX_ENTRY **ie_out) +{ + INDEX_ENTRY *ie; + u8 *index_end; + int rc, item = 0; + + ntfs_log_trace("Entering\n"); + + index_end = ntfs_ie_get_end(ih); + + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { + /* Bounds checks. */ + if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + errno = ERANGE; + ntfs_log_error("Index entry out of bounds in inode " + "%llu.\n", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ntfs_ie_end(ie)) + break; + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); + if (rc == NTFS_COLLATION_ERROR) { + ntfs_log_error("Collation error. Perhaps a filename " + "contains invalid characters?\n"); + errno = ERANGE; + return STATUS_ERROR; + } + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + + if (!rc) { + *ie_out = ie; + errno = 0; + icx->parent_pos[icx->pindex] = item; + return STATUS_OK; + } + + item++; + } + /* + * We have finished with this index block without success. Check for the + * presence of a child node and if not present return with errno ENOENT, + * otherwise we will keep searching in another index block. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_log_debug("Index entry wasn't found.\n"); + *ie_out = ie; + errno = ENOENT; + return STATUS_NOT_FOUND; + } + + /* Get the starting vcn of the index_block holding the child node. */ + *vcn = ntfs_ie_get_vcn(ie); + if (*vcn < 0) { + errno = EINVAL; + ntfs_log_perror("Negative vcn in inode %llu", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + ntfs_log_trace("Parent entry number %d\n", item); + icx->parent_pos[icx->pindex] = item; + + return STATUS_KEEP_SEARCHING; +} + +static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) +{ + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open index allocation of inode " + "%llu", (unsigned long long)ni->mft_no); + return NULL; + } + + return na; +} + +static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) +{ + s64 pos, ret; + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + pos = ntfs_ib_vcn_to_pos(icx, vcn); + + ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, (u8 *)dst); + if (ret != 1) { + if (ret == -1) + ntfs_log_perror("Failed to read index block"); + else + ntfs_log_error("Failed to read full index block at " + "%lld\n", (long long)pos); + return -1; + } + + if (ntfs_ia_check(icx, dst, vcn)) + return -1; + + return 0; +} + +static int ntfs_icx_parent_inc(ntfs_index_context *icx) +{ + icx->pindex++; + if (icx->pindex >= MAX_PARENT_VCN) { + errno = EOPNOTSUPP; + ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN); + return STATUS_ERROR; + } + return STATUS_OK; +} + +static int ntfs_icx_parent_dec(ntfs_index_context *icx) +{ + icx->pindex--; + if (icx->pindex < 0) { + errno = EINVAL; + ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex); + return STATUS_ERROR; + } + return STATUS_OK; +} + +/** + * ntfs_index_lookup - find a key in an index and return its index entry + * @key: [IN] key for which to search in the index + * @key_len: [IN] length of @key in bytes + * @icx: [IN/OUT] context describing the index and the returned entry + * + * Before calling ntfs_index_lookup(), @icx must have been obtained from a + * call to ntfs_index_ctx_get(). + * + * Look for the @key in the index specified by the index lookup context @icx. + * ntfs_index_lookup() walks the contents of the index looking for the @key. + * + * If the @key is found in the index, 0 is returned and @icx is setup to + * describe the index entry containing the matching @key. @icx->entry is the + * index entry and @icx->data and @icx->data_len are the index entry data and + * its length in bytes, respectively. + * + * If the @key is not found in the index, -1 is returned, errno = ENOENT and + * @icx is setup to describe the index entry whose key collates immediately + * after the search @key, i.e. this is the position in the index at which + * an index entry with a key of @key would need to be inserted. + * + * If an error occurs return -1, set errno to error code and @icx is left + * untouched. + * + * When finished with the entry and its data, call ntfs_index_ctx_put() to free + * the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) +{ + VCN old_vcn, vcn; + ntfs_inode *ni = icx->ni; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + int ret, err = 0; + + ntfs_log_trace("Entering\n"); + + if (!key || key_len <= 0) { + errno = EINVAL; + ntfs_log_perror("key: %p key_len: %d", key, key_len); + return -1; + } + + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); + if (!ir) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + icx->block_size = le32_to_cpu(ir->index_block_size); + if (icx->block_size < NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("Index block size (%d) is smaller than the " + "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); + goto err_out; + } + + if (ni->vol->cluster_size <= icx->block_size) + icx->vcn_size_bits = ni->vol->cluster_size_bits; + else + icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { + err = errno = EOPNOTSUPP; + ntfs_log_perror("Unknown collation rule 0x%x", + (unsigned)le32_to_cpu(ir->collation_rule)); + goto err_out; + } + + old_vcn = VCN_INDEX_ROOT_PARENT; + /* + * FIXME: check for both ir and ib that the first index entry is + * within the index block. + */ + ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); + if (ret == STATUS_ERROR) { + err = errno; + goto err_out; + } + + icx->ir = ir; + + if (ret != STATUS_KEEP_SEARCHING) { + /* STATUS_OK or STATUS_NOT_FOUND */ + err = errno; + icx->is_in_root = TRUE; + icx->parent_vcn[icx->pindex] = old_vcn; + goto done; + } + + /* Child node present, descend into it. */ + + icx->ia_na = ntfs_ia_open(icx, ni); + if (!icx->ia_na) + goto err_out; + + ib = ntfs_malloc(icx->block_size); + if (!ib) { + err = errno; + goto err_out; + } + +descend_into_child_node: + + icx->parent_vcn[icx->pindex] = old_vcn; + if (ntfs_icx_parent_inc(icx)) { + err = errno; + goto err_out; + } + old_vcn = vcn; + + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); + + if (ntfs_ib_read(icx, vcn, ib)) + goto err_out; + + ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); + if (ret != STATUS_KEEP_SEARCHING) { + err = errno; + if (ret == STATUS_ERROR) + goto err_out; + + /* STATUS_OK or STATUS_NOT_FOUND */ + icx->is_in_root = FALSE; + icx->ib = ib; + icx->parent_vcn[icx->pindex] = vcn; + goto done; + } + + if ((ib->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in inode 0x%llx.\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + + goto descend_into_child_node; +err_out: + free(ib); + if (!err) + err = EIO; + errno = err; + return -1; +done: + icx->entry = ie; + icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); + icx->data_len = le16_to_cpu(ie->key_length); + ntfs_log_trace("Done.\n"); + if (err) { + errno = err; + return -1; + } + return 0; + +} + +static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, + INDEX_HEADER_FLAGS node_type) +{ + INDEX_BLOCK *ib; + int ih_size = sizeof(INDEX_HEADER); + + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); + + ib = ntfs_calloc(ib_size); + if (!ib) + return NULL; + + ib->magic = magic_INDX; + ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); + ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); + /* Set USN to 1 */ + *(u16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); + ib->lsn = cpu_to_le64(0); + + ib->index_block_vcn = cpu_to_sle64(ib_vcn); + + ib->index.entries_offset = cpu_to_le32((ih_size + + le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); + ib->index.index_length = 0; + ib->index.allocated_size = cpu_to_le32(ib_size - + (sizeof(INDEX_BLOCK) - ih_size)); + ib->index.ih_flags = node_type; + + return ib; +} + +/** + * Find the median by going through all the entries + */ +static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie, *ie_start; + u8 *ie_end; + int i = 0, median; + + ntfs_log_trace("Entering\n"); + + ie = ie_start = ntfs_ie_get_first(ih); + ie_end = (u8 *)ntfs_ie_get_end(ih); + + while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { + ie = ntfs_ie_get_next(ie); + i++; + } + /* + * NOTE: this could be also the entry at the half of the index block. + */ + median = i / 2 - 1; + + ntfs_log_trace("Entries: %d median: %d\n", i, median); + + for (i = 0, ie = ie_start; i <= median; i++) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size; +} + +static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); +} + +static int ntfs_ibm_add(ntfs_index_context *icx) +{ + u8 bmp[8]; + + ntfs_log_trace("Entering\n"); + + if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) + return STATUS_OK; + /* + * AT_BITMAP must be at least 8 bytes. + */ + memset(bmp, 0, sizeof(bmp)); + if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, + bmp, sizeof(bmp))) { + ntfs_log_perror("Failed to add AT_BITMAP"); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) +{ + u8 byte; + s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); + u32 bpos = pos / 8; + u32 bit = 1 << (pos % 8); + ntfs_attr *na; + int ret = STATUS_ERROR; + + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); + + na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open $BITMAP attribute"); + return -1; + } + + if (set) { + if (na->data_size < bpos + 1) { + if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { + ntfs_log_perror("Failed to truncate AT_BITMAP"); + goto err_na; + } + } + } + + if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to read $BITMAP"); + goto err_na; + } + + if (set) + byte |= bit; + else + byte &= ~bit; + + if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to write $Bitmap"); + goto err_na; + } + + ret = STATUS_OK; +err_na: + ntfs_attr_close(na); + return ret; +} + + +static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 1); +} + +static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 0); +} + +static VCN ntfs_ibm_get_free(ntfs_index_context *icx) +{ + u8 *bm; + int bit; + s64 vcn, byte, size; + + ntfs_log_trace("Entering\n"); + + bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len, + &size); + if (!bm) + return (VCN)-1; + + for (byte = 0; byte < size; byte++) { + + if (bm[byte] == 255) + continue; + + for (bit = 0; bit < 8; bit++) { + if (!(bm[byte] & (1 << bit))) { + vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); + goto out; + } + } + } + + vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); +out: + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); + + if (ntfs_ibm_set(icx, vcn)) + vcn = (VCN)-1; + + free(bm); + return vcn; +} + +static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn) +{ + INDEX_BLOCK *ib; + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + int i; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), LEAF_NODE); + if (!ib) + return NULL; + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Copy all entries, including the termination entry + * as well, which can never have any data. + */ + i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); + memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); + + ib->index.ih_flags = ir->index.ih_flags; + ib->index.index_length = cpu_to_le32(i + + le32_to_cpu(ib->index.entries_offset)); + return ib; +} + +static void ntfs_ir_nill(INDEX_ROOT *ir) +{ + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + + ntfs_log_trace("Entering\n"); + /* + * TODO: This function could be much simpler. + */ + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Move the index root termination entry forward + */ + if ((char *)ie_last > ies_start) { + memmove(ies_start, (char *)ie_last, le16_to_cpu(ie_last->length)); + ie_last = (INDEX_ENTRY *)ies_start; + } +} + +static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, + INDEX_ENTRY *median, VCN new_vcn) +{ + u8 *ies_end; + INDEX_ENTRY *ie_head; /* first entry after the median */ + int tail_size, ret; + INDEX_BLOCK *dst; + + ntfs_log_trace("Entering\n"); + + dst = ntfs_ib_alloc(new_vcn, icx->block_size, + src->index.ih_flags & NODE_MASK); + if (!dst) + return STATUS_ERROR; + + ie_head = ntfs_ie_get_next(median); + + ies_end = (u8 *)ntfs_ie_get_end(&src->index); + tail_size = ies_end - (u8 *)ie_head; + memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); + + dst->index.index_length = cpu_to_le32(tail_size + + le32_to_cpu(dst->index.entries_offset)); + ret = ntfs_ib_write(icx, dst); + + free(dst); + return ret; +} + +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, + INDEX_ENTRY *ie) +{ + char *ies_start, *ies_end; + INDEX_ENTRY *ie_last; + + ntfs_log_trace("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); + + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + if (ie_last->ie_flags & INDEX_ENTRY_NODE) + ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); + + memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); + + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); + + if (ntfs_ib_write(icx, ib)) + return STATUS_ERROR; + + return STATUS_OK; +} + +static int ntfs_ia_add(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (ntfs_ibm_add(icx)) + return -1; + + if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { + + if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, + icx->name_len, NULL, 0)) { + ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); + return -1; + } + } + + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return -1; + + return 0; +} + +static int ntfs_ir_reparent(ntfs_index_context *icx) +{ + ntfs_attr_search_ctx *ctx = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + VCN new_ib_vcn; + int ix_root_size; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto out; + + if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) + if (ntfs_ia_add(icx)) + goto out; + + new_ib_vcn = ntfs_ibm_get_free(icx); + if (new_ib_vcn == -1) + goto out; + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto clear_bmp; + + ib = ntfs_ir_to_ib(ir, new_ib_vcn); + if (ib == NULL) { + ntfs_log_perror("Failed to move index root to index block"); + goto clear_bmp; + } + + if (ntfs_ib_write(icx, ib)) + goto clear_bmp; + +retry : + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + if (!ir) + goto clear_bmp; + + ntfs_ir_nill(ir); + + ie = ntfs_ie_get_first(&ir->index); + ie->ie_flags |= INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); + + ir->index.ih_flags = LARGE_INDEX; + ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + + le16_to_cpu(ie->length)); + ir->index.allocated_size = ir->index.index_length; + ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + + le32_to_cpu(ir->index.allocated_size); + if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + ix_root_size)) { + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((errno == ENOSPC) + && !ctx->al_entry + && !ntfs_inode_add_attrlist(icx->ni)) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, + &ctx); + if (ir + && !ntfs_attr_record_move_away(ctx, ix_root_size + - le32_to_cpu(ctx->attr->value_length))) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + goto retry; + } + } + /* FIXME: revert index root */ + goto clear_bmp; + } + /* + * FIXME: do it earlier if we have enough space in IR (should always), + * so in error case we wouldn't lose the IB. + */ + ntfs_ie_set_vcn(ie, new_ib_vcn); + + ret = STATUS_OK; +err_out: + free(ib); + ntfs_attr_put_search_ctx(ctx); +out: + return ret; +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; +} + +/** + * ntfs_ir_truncate - Truncate index root attribute + * + * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. + */ +static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open INDEX_ROOT"); + return STATUS_ERROR; + } + /* + * INDEX_ROOT must be resident and its entries can be moved to + * INDEX_BLOCK, so ENOSPC isn't a real error. + */ + ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); + if (ret == STATUS_OK) { + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + icx->ir->index.allocated_size = cpu_to_le32(data_size); + + } else if (ret == STATUS_ERROR) + ntfs_log_perror("Failed to truncate INDEX_ROOT"); + + ntfs_attr_close(na); + return ret; +} + +/** + * ntfs_ir_make_space - Make more space for the index root attribute + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) +{ + int ret; + + ntfs_log_trace("Entering\n"); + + ret = ntfs_ir_truncate(icx, data_size); + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); + } + + return ret; +} + +/* + * NOTE: 'ie' must be a copy of a real index entry. + */ +static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) +{ + INDEX_ENTRY *p, *old = *ie; + + old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); + p = realloc(old, le16_to_cpu(old->length)); + if (!p) + return STATUS_ERROR; + + p->ie_flags |= INDEX_ENTRY_NODE; + *ie = p; + + return STATUS_OK; +} + +static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, + int pos) +{ + INDEX_ENTRY *ie_node, *ie; + int ret = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_dup(orig_ie); + if (!ie) + return STATUS_ERROR; + + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) + if (ntfs_ie_add_vcn(&ie)) + goto out; + + ie_node = ntfs_ie_get_by_pos(ih, pos); + old_vcn = ntfs_ie_get_vcn(ie_node); + ntfs_ie_set_vcn(ie_node, new_vcn); + + ntfs_ie_insert(ih, ie, ie_node); + ntfs_ie_set_vcn(ie_node, old_vcn); + ret = STATUS_OK; +out: + free(ie); + + return ret; +} + +static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx) +{ + return icx->parent_vcn[icx->pindex]; +} + +static VCN ntfs_icx_parent_pos(ntfs_index_context *icx) +{ + return icx->parent_pos[icx->pindex]; +} + + +static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, + VCN new_vcn) +{ + u32 new_size; + int ret; + + ntfs_log_trace("Entering\n"); + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + new_size = le32_to_cpu(icx->ir->index.index_length) + + le16_to_cpu(median->length); + if (!(median->ie_flags & INDEX_ENTRY_NODE)) + new_size += sizeof(VCN); + + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + return ret; + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + return ntfs_ih_insert(&icx->ir->index, median, new_vcn, + ntfs_icx_parent_pos(icx)); +} + +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib); + +/** + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) +{ + INDEX_BLOCK *ib; + u32 idx_size, allocated_size; + int err = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return -1; + + old_vcn = ntfs_icx_parent_vcn(icx); + + if (ntfs_ib_read(icx, old_vcn, ib)) + goto err_out; + + idx_size = le32_to_cpu(ib->index.index_length); + allocated_size = le32_to_cpu(ib->index.allocated_size); + /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ + if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { + err = ntfs_ib_split(icx, ib); + if (err == STATUS_OK) + err = STATUS_KEEP_SEARCHING; + goto err_out; + } + + if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) + goto err_out; + + if (ntfs_ib_write(icx, ib)) + goto err_out; + + err = STATUS_OK; +err_out: + free(ib); + return err; +} + +/** + * ntfs_ib_split - Split an index block + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return is STATUS_ERROR. + */ +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *median; + VCN new_vcn; + int ret; + + ntfs_log_trace("Entering\n"); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + median = ntfs_ie_get_median(&ib->index); + new_vcn = ntfs_ibm_get_free(icx); + if (new_vcn == -1) + return STATUS_ERROR; + + if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) { + ntfs_ibm_clear(icx, new_vcn); + return STATUS_ERROR; + } + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ret = ntfs_ir_insert_median(icx, median, new_vcn); + else + ret = ntfs_ib_insert(icx, median, new_vcn); + + if (ret != STATUS_OK) { + ntfs_ibm_clear(icx, new_vcn); + return ret; + } + + ret = ntfs_ib_cut_tail(icx, ib, median); + + return ret; +} + +/* JPA static */ +int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) +{ + INDEX_HEADER *ih; + int allocated_size, new_size; + int ret = STATUS_ERROR; + +#ifdef DEBUG +/* removed by JPA to make function usable for security indexes + char *fn; + fn = ntfs_ie_filename_get(ie); + ntfs_log_trace("file: '%s'\n", fn); + ntfs_attr_name_free(&fn); +*/ +#endif + + while (1) { + + if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx)) { + errno = EEXIST; + ntfs_log_perror("Index already have such entry"); + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("Failed to find place for new entry"); + goto err_out; + } + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + allocated_size = le32_to_cpu(ih->allocated_size); + new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); + + if (new_size <= allocated_size) + break; + + ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", + allocated_size, new_size); + + if (icx->is_in_root) { + if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) + goto err_out; + } else { + if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) + goto err_out; + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_ie_insert(ih, ie, icx->entry); + ntfs_index_entry_mark_dirty(icx); + + ret = STATUS_OK; +err_out: + ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); + return ret; +} + +/** + * ntfs_index_add_filename - add filename to directory index + * @ni: ntfs inode describing directory to which index add filename + * @fn: FILE_NAME attribute to add + * @mref: reference of the inode which @fn describes + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) +{ + INDEX_ENTRY *ie; + ntfs_index_context *icx; + int fn_size, ie_size, err, ret = -1; + + ntfs_log_trace("Entering\n"); + + if (!ni || !fn) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + fn_size = (fn->file_name_length * sizeof(ntfschar)) + + sizeof(FILE_NAME_ATTR); + ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; + + ie = ntfs_calloc(ie_size); + if (!ie) + return -1; + + ie->indexed_file = cpu_to_le64(mref); + ie->length = cpu_to_le16(ie_size); + ie->key_length = cpu_to_le16(fn_size); + memcpy(&ie->key, fn, fn_size); + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + goto out; + + ret = ntfs_ie_add(icx, ie); + err = errno; + ntfs_index_ctx_put(icx); + errno = err; +out: + free(ie); + return ret; +} + +static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_ENTRY *ie, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie_roam; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ie_roam = ntfs_ie_dup_novcn(ie); + if (!ie_roam) + return STATUS_ERROR; + + ntfs_ie_delete(ih, ie); + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + else + if (ntfs_ib_write(icx, ib)) + goto out; + + ntfs_index_ctx_reinit(icx); + + ret = ntfs_ie_add(icx, ie_roam); +out: + free(ie_roam); + return ret; +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is the only one there. + */ +static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + ie->ie_flags &= ~INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN)); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - sizeof(VCN)); + ih->ih_flags &= ~LARGE_INDEX; + + /* Not fatal error */ + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is not the only one there. + */ +static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie, *ie_prev; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); + ie_prev = ntfs_ie_prev(ih, ie); + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); + + return ntfs_ih_takeout(icx, ih, ie_prev, ib); +} + +static int ntfs_index_rm_leaf(ntfs_index_context *icx) +{ + INDEX_BLOCK *ib = NULL; + INDEX_HEADER *parent_ih; + INDEX_ENTRY *ie; + int ret = STATUS_ERROR; + + ntfs_log_trace("pindex: %d\n", icx->pindex); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1])) + return STATUS_ERROR; + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + parent_ih = &icx->ir->index; + else { + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib)) + goto out; + + parent_ih = &ib->index; + } + + ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); + if (!ntfs_ie_end(ie)) { + ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); + goto out; + } + + if (ntfs_ih_zero_entry(parent_ih)) { + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ntfs_ir_leafify(icx, parent_ih); + goto ok; + } + + ret = ntfs_index_rm_leaf(icx); + goto out; + } + + if (ntfs_ih_reparent_end(icx, parent_ih, ib)) + goto out; +ok: + ret = STATUS_OK; +out: + free(ib); + return ret; +} + +static int ntfs_index_rm_node(ntfs_index_context *icx) +{ + int entry_pos, pindex; + VCN vcn; + INDEX_BLOCK *ib = NULL; + INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; + INDEX_HEADER *ih; + u32 new_size; + int delta, ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + if (!icx->ia_na) { + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return STATUS_ERROR; + } + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + ie_succ = ntfs_ie_get_next(icx->entry); + entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; +descend: + vcn = ntfs_ie_get_vcn(ie_succ); + if (ntfs_ib_read(icx, vcn, ib)) + goto out; + + ie_succ = ntfs_ie_get_first(&ib->index); + + if (ntfs_icx_parent_inc(icx)) + goto out; + + icx->parent_vcn[icx->pindex] = vcn; + icx->parent_pos[icx->pindex] = 0; + + if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE) + goto descend; + + if (ntfs_ih_zero_entry(&ib->index)) { + errno = EIO; + ntfs_log_perror("Empty index block"); + goto out; + } + + ie = ntfs_ie_dup(ie_succ); + if (!ie) + goto out; + + if (ntfs_ie_add_vcn(&ie)) + goto out2; + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); + new_size = le32_to_cpu(ih->index_length) + delta; + if (delta > 0) { + if (icx->is_in_root) { + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + goto out2; + + ih = &icx->ir->index; + entry = ntfs_ie_get_by_pos(ih, entry_pos); + + } else if (new_size > le32_to_cpu(ih->allocated_size)) { + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + goto out2; + } + } + + ntfs_ie_delete(ih, entry); + ntfs_ie_insert(ih, ie, entry); + + if (icx->is_in_root) { + if (ntfs_ir_truncate(icx, new_size)) + goto out2; + } else + if (ntfs_icx_ib_write(icx)) + goto out2; + + ntfs_ie_delete(&ib->index, ie_succ); + + if (ntfs_ih_zero_entry(&ib->index)) { + if (ntfs_index_rm_leaf(icx)) + goto out2; + } else + if (ntfs_ib_write(icx, ib)) + goto out2; + + ret = STATUS_OK; +out2: + free(ie); +out: + free(ib); + return ret; +} + +/** + * ntfs_index_rm - remove entry from the index + * @icx: index context describing entry to delete + * + * Delete entry described by @icx from the index. Index context is always + * reinitialized after use of this function, so it can be used for index + * lookup once again. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +/*static JPA*/ +int ntfs_index_rm(ntfs_index_context *icx) +{ + INDEX_HEADER *ih; + int err, ret = STATUS_OK; + + ntfs_log_trace("Entering\n"); + + if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { + + ret = ntfs_index_rm_node(icx); + + } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { + + ntfs_ie_delete(ih, icx->entry); + + if (icx->is_in_root) { + err = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + if (err != STATUS_OK) + goto err_out; + } else + if (ntfs_icx_ib_write(icx)) + goto err_out; + } else { + if (ntfs_index_rm_leaf(icx)) + goto err_out; + } +out: + return ret; +err_out: + ret = STATUS_ERROR; + goto out; +} + +int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen) +{ + int ret = STATUS_ERROR; + ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; + + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; + + if ((((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; +} + +/** + * ntfs_index_root_get - read the index root of an attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @attr: attribute for which we want its index root + * + * This function will read the related index root an ntfs attribute. + * + * On success a buffer is allocated with the content of the index root + * and which needs to be freed when it's not needed anymore. + * + * On error NULL is returned with errno set to the error code. + */ +INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *name; + INDEX_ROOT *root = NULL; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + + if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx)) + return NULL; + + root = ntfs_malloc(sizeof(INDEX_ROOT)); + if (!root) + goto out; + + *root = *((INDEX_ROOT *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset))); +out: + ntfs_attr_put_search_ctx(ctx); + return root; +} + + +/* + * Walk down the index tree (leaf bound) + * until there are no subnode in the first index entry + * returns the entry at the bottom left in subnode + */ + +static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + do { + vcn = ntfs_ie_get_vcn(entry); + if (ictx->is_in_root) { + + /* down from level zero */ + + ictx->ir = (INDEX_ROOT*)NULL; + ictx->ib = (INDEX_BLOCK*)ntfs_malloc(ictx->block_size); + ictx->pindex = 1; + ictx->is_in_root = FALSE; + } else { + + /* down from non-zero level */ + + ictx->pindex++; + } + ictx->parent_pos[ictx->pindex] = 0; + ictx->parent_vcn[ictx->pindex] = vcn; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + ictx->entry = ntfs_ie_get_first(&ictx->ib->index); + entry = ictx->entry; + } else + entry = (INDEX_ENTRY*)NULL; + } while (entry && (entry->ie_flags & INDEX_ENTRY_NODE)); + return (entry); +} + +/* + * Walk up the index tree (root bound) + * until there is a valid data entry in parent + * returns the parent entry or NULL if no more parent + */ + +static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + if (ictx->pindex > 0) { + do { + ictx->pindex--; + if (!ictx->pindex) { + + /* we have reached the root */ + + free(ictx->ib); + ictx->ib = (INDEX_BLOCK*)NULL; + ictx->is_in_root = TRUE; + /* a new search context is to be allocated */ + if (ictx->actx) + free(ictx->actx); + ictx->ir = ntfs_ir_lookup(ictx->ni, + ictx->name, ictx->name_len, + &ictx->actx); + if (ictx->ir) + entry = ntfs_ie_get_by_pos( + &ictx->ir->index, + ictx->parent_pos[ictx->pindex]); + else + entry = (INDEX_ENTRY*)NULL; + } else { + /* up into non-root node */ + vcn = ictx->parent_vcn[ictx->pindex]; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + entry = ntfs_ie_get_by_pos( + &ictx->ib->index, + ictx->parent_pos[ictx->pindex]); + } else + entry = (INDEX_ENTRY*)NULL; + } + ictx->entry = entry; + } while (entry && (ictx->pindex > 0) + && (entry->ie_flags & INDEX_ENTRY_END)); + } else + entry = (INDEX_ENTRY*)NULL; + return (entry); +} + +/* + * Get next entry in an index according to collating sequence. + * Must be initialized through a ntfs_index_lookup() + * + * Returns next entry or NULL if none + * + * Sample layout : + * + * +---+---+---+---+---+---+---+---+ n ptrs to subnodes + * | | | 10| 25| 33| | | | n-1 keys in between + * +---+---+---+---+---+---+---+---+ no key in last entry + * | A | A + * | | | +-------------------------------+ + * +--------------------------+ | +-----+ | + * | +--+ | | + * V | V | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | 11| 12| 13| 14| 15| 16| 17| | | | 26| 27| 28| 29| 30| 31| 32| | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | | + * +-----------------------+ | + * | | + * +---+---+---+---+---+---+---+---+ + * | 18| 19| 20| 21| 22| 23| 24| | + * +---+---+---+---+---+---+---+---+ + */ + +INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx) +{ + INDEX_ENTRY *next; + int flags; + + /* + * lookup() may have returned an invalid node + * when searching for a partial key + * if this happens, walk up + */ + + if (ie->ie_flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(ie, ictx); + else { + /* + * get next entry in same node + * there is always one after any entry with data + */ + + next = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + ++ictx->parent_pos[ictx->pindex]; + flags = next->ie_flags; + + /* walk down if it has a subnode */ + + if (flags & INDEX_ENTRY_NODE) { + next = ntfs_index_walk_down(next,ictx); + } else { + + /* walk up it has no subnode, nor data */ + + if (flags & INDEX_ENTRY_END) { + next = ntfs_index_walk_up(next, ictx); + } + } + } + /* return NULL if stuck at end of a block */ + + if (next && (next->ie_flags & INDEX_ENTRY_END)) + next = (INDEX_ENTRY*)NULL; + return (next); +} + + diff --git a/lib/libntfs/src/source/index.h b/lib/libntfs/src/source/index.h new file mode 100644 index 0000000..c0e7618 --- /dev/null +++ b/lib/libntfs/src/source/index.h @@ -0,0 +1,167 @@ +/* + * index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INDEX_H +#define _NTFS_INDEX_H + +/* Convenience macros to test the versions of gcc. + * Use them like this: + * #if __GNUC_PREREQ (2,8) + * ... code requiring gcc 2.8 or later ... + * #endif + * Note - they won't work for gcc1 or glibc1, since the _MINOR macros + * were not defined then. + */ + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +/* allows us to warn about unused results of certain function calls */ +#ifndef __attribute_warn_unused_result__ +# if __GNUC_PREREQ (3,4) +# define __attribute_warn_unused_result__ \ + __attribute__ ((__warn_unused_result__)) +# else +# define __attribute_warn_unused_result__ /* empty */ +# endif +#endif + +#include "attrib.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "mft.h" + +#define VCN_INDEX_ROOT_PARENT ((VCN)-2) + +#define MAX_PARENT_VCN 32 + +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); + +/** + * struct ntfs_index_context - + * @ni: inode containing the @entry described by this context + * @name: name of the index described by this context + * @name_len: length of the index name + * @entry: index entry (points into @ir or @ia) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia + * @ir: index root if @is_in_root or NULL otherwise + * @actx: attribute search context if in root or NULL otherwise + * @ia: index block if @is_in_root is FALSE or NULL otherwise + * @ia_na: opened INDEX_ALLOCATION attribute + * @parent_pos: parent entries' positions in the index block + * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT + * @new_vcn: new VCN if we need to create a new index block + * @median: move to the parent if splitting index blocks + * @ib_dirty: TRUE if index block was changed + * @block_size: index block size + * @vcn_size_bits: VCN size bits for this index block + * + * @ni is the inode this context belongs to. + * + * @entry is the index entry described by this context. @data and @data_len + * are the index entry data and its length in bytes, respectively. @data + * simply points into @entry. This is probably what the user is interested in. + * + * If @is_in_root is TRUE, @entry is in the index root attribute @ir described + * by the attribute search context @actx and inode @ni. @ia and + * @ib_dirty are undefined in this case. + * + * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia + * point to the index allocation block and VCN where it's placed, + * respectively. @ir and @actx are NULL in this case. @ia_na is opened + * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and + * FALSE otherwise. + * + * To obtain a context call ntfs_index_ctx_get(). + * + * When finished with the @entry and its @data, call ntfs_index_ctx_put() to + * free the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +typedef struct { + ntfs_inode *ni; + ntfschar *name; + u32 name_len; + INDEX_ENTRY *entry; + void *data; + u16 data_len; + COLLATE collate; + BOOL is_in_root; + INDEX_ROOT *ir; + ntfs_attr_search_ctx *actx; + INDEX_BLOCK *ib; + ntfs_attr *ia_na; + int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ + VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ + int pindex; /* maximum it's the number of the parent nodes */ + BOOL ib_dirty; + u32 block_size; + u8 vcn_size_bits; +} ntfs_index_context; + +extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len); +extern void ntfs_index_ctx_put(ntfs_index_context *ictx); +extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); + +extern int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx) __attribute_warn_unused_result__; + +extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, + ntfs_index_context *ictx); + +extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, + MFT_REF mref); +extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen); + +extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); + +extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); + +extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); + +extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); +extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); +extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); + +/* the following was added by JPA for use in security.c */ +extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie); +extern int ntfs_index_rm(ntfs_index_context *icx); + +#endif /* _NTFS_INDEX_H */ + diff --git a/lib/libntfs/src/source/inode.c b/lib/libntfs/src/source/inode.c new file mode 100644 index 0000000..a4a0134 --- /dev/null +++ b/lib/libntfs/src/source/inode.c @@ -0,0 +1,1608 @@ +/** + * inode.c - Inode handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SETXATTR +#include +#endif + +#include "param.h" +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "cache.h" +#include "inode.h" +#include "attrib.h" +#include "debug.h" +#include "mft.h" +#include "attrlist.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "index.h" +#include "dir.h" +#include "ntfstime.h" +#include "logging.h" +#include "misc.h" + +ntfs_inode *ntfs_inode_base(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + return ni->base_ni; + return ni; +} + +/** + * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty + * @ni: ntfs inode to set dirty + * + * Set the inode @ni dirty so it is written out later (at the latest at + * ntfs_inode_close() time). If @ni is an extent inode, set the base inode + * dirty, too. + * + * This function cannot fail. + */ +void ntfs_inode_mark_dirty(ntfs_inode *ni) +{ + NInoSetDirty(ni); + if (ni->nr_extents == -1) + NInoSetDirty(ni->base_ni); +} + +/** + * __ntfs_inode_allocate - Create and initialise an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) +{ + ntfs_inode *ni; + + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); + if (ni) + ni->vol = vol; + return ni; +} + +/** + * ntfs_inode_allocate - Create an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) +{ + return __ntfs_inode_allocate(vol); +} + +/** + * __ntfs_inode_release - Destroy an NTFS inode object + * @ni: + * + * Description... + * + * Returns: + */ +static void __ntfs_inode_release(ntfs_inode *ni) +{ + if (NInoDirty(ni)) + ntfs_log_error("Releasing dirty inode %lld!\n", + (long long)ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + free(ni->attr_list); + free(ni->mrec); + free(ni); + return; +} + +/** + * ntfs_inode_open - open an inode ready for access + * @vol: volume to get the inode from + * @mref: inode number / mft record number to open + * + * Allocate an ntfs_inode structure and initialize it for the given inode + * specified by @mref. @mref specifies the inode number / mft record to read, + * including the sequence number, which can be 0 if no sequence number checking + * is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @vol, and attach it to the ntfs_inode structure (->mrec). The + * mft record is mst deprotected and sanity checked for validity and we abort + * if deprotection or checks fail. + * + * Finally, search for an attribute list attribute in the mft record and if one + * is found, load the attribute list attribute value and attach it to the + * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate + * this. + * + * Return a pointer to the ntfs_inode structure on success or NULL on error, + * with errno set to the error code. + */ +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) +{ + s64 l; + ntfs_inode *ni = NULL; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + le32 lthle; + int olderrno; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + if (!vol) { + errno = EINVAL; + goto out; + } + ni = __ntfs_inode_allocate(vol); + if (!ni) + goto out; + if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) + goto err_out; + if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { + errno = ENOENT; + goto err_out; + } + ni->mft_no = MREF(mref); + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + /* Receive some basic information about inode. */ + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (!ni->mrec->base_mft_record) + ntfs_log_perror("No STANDARD_INFORMATION in base record" + " %lld", (long long)MREF(mref)); + goto put_err_out; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + ni->flags = std_info->file_attributes; + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; + /* JPA insert v3 extensions if present */ + /* length may be seen as 72 (v1.x) or 96 (v3.x) */ + lthle = ctx->attr->length; + if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = std_info->owner_id; + ni->security_id = std_info->security_id; + ni->quota_charged = std_info->quota_charged; + ni->usn = std_info->usn; + } else { + clear_nino_flag(ni, v3_Extensions); + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); + } + /* Set attribute list information. */ + olderrno = errno; + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Attribute list attribute does not present. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + goto get_size; + } + NInoSetAttrList(ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (!l) + goto put_err_out; + if (l > 0x40000) { + errno = EIO; + ntfs_log_perror("Too large attrlist attribute (%lld), inode " + "%lld", (long long)l, (long long)MREF(mref)); + goto put_err_out; + } + ni->attr_list_size = l; + ni->attr_list = ntfs_malloc(ni->attr_list_size); + if (!ni->attr_list) + goto put_err_out; + l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); + if (!l) + goto put_err_out; + if (l != ni->attr_list_size) { + errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " + "%lld", (long long)l, ni->attr_list_size, + (long long)MREF(mref)); + goto put_err_out; + } +get_size: + olderrno = errno; + if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Directory or special file. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + ni->data_size = ni->allocated_size = 0; + } else { + if (ctx->attr->non_resident) { + ni->data_size = sle64_to_cpu(ctx->attr->data_size); + if (ctx->attr->flags & + (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) + ni->allocated_size = sle64_to_cpu( + ctx->attr->compressed_size); + else + ni->allocated_size = sle64_to_cpu( + ctx->attr->allocated_size); + } else { + ni->data_size = le32_to_cpu(ctx->attr->value_length); + ni->allocated_size = (ni->data_size + 7) & ~7; + } + set_nino_flag(ni,KnownSize); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ni; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_close - close an ntfs inode and free all associated memory + * @ni: ntfs inode to close + * + * Make sure the ntfs inode @ni is clean. + * + * If the ntfs inode @ni is a base inode, close all associated extent inodes, + * then deallocate all memory attached to it, and finally free the ntfs inode + * structure itself. + * + * If it is an extent inode, we disconnect it from its base inode before we + * destroy it. + * + * It is OK to pass NULL to this function, it is just noop in this case. + * + * Return 0 on success or -1 on error with errno set to the error code. On + * error, @ni has not been freed. The user should attempt to handle the error + * and call ntfs_inode_close() again. The following error codes are defined: + * + * EBUSY @ni and/or its attribute list runlist is/are dirty and the + * attempt to write it/them to disk failed. + * EINVAL @ni is invalid (probably it is an extent inode). + * EIO I/O error while trying to write inode to disk. + */ + +int ntfs_inode_real_close(ntfs_inode *ni) +{ + int ret = -1; + + if (!ni) + return 0; + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* If we have dirty metadata, write it out. */ + if (NInoDirty(ni) || NInoAttrListDirty(ni)) { + if (ntfs_inode_sync(ni)) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + /* Is this a base inode with mapped extent inodes? */ + if (ni->nr_extents > 0) { + while (ni->nr_extents > 0) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + } else if (ni->nr_extents == -1) { + ntfs_inode **tmp_nis; + ntfs_inode *base_ni; + s32 i; + + /* + * If the inode is an extent inode, disconnect it from the + * base inode before destroying it. + */ + base_ni = ni->base_ni; + for (i = 0; i < base_ni->nr_extents; ++i) { + tmp_nis = base_ni->extent_nis; + if (tmp_nis[i] != ni) + continue; + /* Found it. Disconnect. */ + memmove(tmp_nis + i, tmp_nis + i + 1, + (base_ni->nr_extents - i - 1) * + sizeof(ntfs_inode *)); + /* Buffer should be for multiple of four extents. */ + if ((--base_ni->nr_extents) & 3) { + i = -1; + break; + } + /* + * ElectricFence is unhappy with realloc(x,0) as free(x) + * thus we explicitly separate these two cases. + */ + if (base_ni->nr_extents) { + /* Resize the memory buffer. */ + tmp_nis = realloc(tmp_nis, base_ni->nr_extents * + sizeof(ntfs_inode *)); + /* Ignore errors, they don't really matter. */ + if (tmp_nis) + base_ni->extent_nis = tmp_nis; + } else if (tmp_nis) { + free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } + /* Allow for error checking. */ + i = -1; + break; + } + + /* + * We could successfully sync, so only log this error + * and try to sync other inode extents too. + */ + if (i != -1) + ntfs_log_error("Extent inode %lld was not found\n", + (long long)ni->mft_no); + } + + __ntfs_inode_release(ni); + ret = 0; +err: + ntfs_log_leave("\n"); + return ret; +} + +#if CACHE_NIDATA_SIZE + +/* + * Free an inode structure when there is not more space + * in the cache + */ + +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) +{ + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); +} + +/* + * Compute a hash value for an inode entry + */ + +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) +{ + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); +} + +/* + * inum comparing for entering/fetching from cache + */ + +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); +} + +/* + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. + */ + +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) +{ + struct CACHED_NIDATA item; + + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); +} + +#endif + +/* + * Open an inode + * + * When possible, an entry recorded in the cache is reused + * + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. + */ + +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +{ + ntfs_inode *ni; +#if CACHE_NIDATA_SIZE + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; + + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } + if (!ni) { + debug_double_inode(item.inum, 0); + } +#else + ni = ntfs_inode_real_open(vol, mref); +#endif + return (ni); +} + +/* + * Close an inode entry + * + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. + * + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. + */ + +int ntfs_inode_close(ntfs_inode *ni) +{ + int res; +#if CACHE_NIDATA_SIZE + BOOL dirty; + struct CACHED_NIDATA item; + + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; + + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; +#else + res = ntfs_inode_real_close(ni); +#endif + return (res); +} + +/** + * ntfs_extent_inode_open - load an extent inode and attach it to its base + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load (in little endian) + * + * First check if the extent inode @mref is already attached to the base ntfs + * inode @base_ni, and if so, return a pointer to the attached extent inode. + * + * If the extent inode is not already attached to the base inode, allocate an + * ntfs_inode structure and initialize it for the given inode @mref. @mref + * specifies the inode number / mft record to read, including the sequence + * number, which can be 0 if no sequence number checking is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). + * The mft record is mst deprotected and sanity checked for validity and we + * abort if deprotection or checks fail. + * + * Finally attach the ntfs inode to its base inode @base_ni and return a + * pointer to the ntfs_inode structure on success or NULL on error, with errno + * set to the error code. + * + * Note, extent inodes are never closed directly. They are automatically + * disposed off by the closing of the base inode. + */ +ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) +{ + u64 mft_no = MREF_LE(mref); + VCN extent_vcn; + runlist_element *rl; + ntfs_volume *vol; + ntfs_inode *ni = NULL; + ntfs_inode **extent_nis; + int i; + + if (!base_ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return NULL; + } + + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", + (unsigned long long)mft_no, + (unsigned long long)base_ni->mft_no); + + if (!base_ni->mft_no) { + /* + * When getting extents of MFT, we must be sure + * they are in the MFT part which has already + * been mapped, otherwise we fall into an endless + * recursion. + * Situations have been met where extents locations + * are described in themselves. + * This is a severe error which chkdsk cannot fix. + */ + vol = base_ni->vol; + extent_vcn = mft_no << vol->mft_record_size_bits + >> vol->cluster_size_bits; + rl = vol->mft_na->rl; + if (rl) { + while (rl->length + && ((rl->vcn + rl->length) <= extent_vcn)) + rl++; + } + if (!rl || (rl->lcn < 0)) { + ntfs_log_error("MFT is corrupt, cannot read" + " its unmapped extent record %lld\n", + (long long)mft_no); + ntfs_log_error("Note : chkdsk cannot fix this," + " try ntfsfix\n"); + errno = EIO; + ni = (ntfs_inode*)NULL; + goto out; + } + } + + /* Is the extent inode already open and attached to the base inode? */ + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->extent_nis; + for (i = 0; i < base_ni->nr_extents; i++) { + u16 seq_no; + + ni = extent_nis[i]; + if (mft_no != ni->mft_no) + continue; + /* Verify the sequence number if given. */ + seq_no = MSEQNO_LE(mref); + if (seq_no && seq_no != le16_to_cpu( + ni->mrec->sequence_number)) { + errno = EIO; + ntfs_log_perror("Found stale extent mft " + "reference mft=%lld", + (long long)ni->mft_no); + goto out; + } + goto out; + } + } + /* Wasn't there, we need to load the extent inode. */ + ni = __ntfs_inode_allocate(base_ni->vol); + if (!ni) + goto out; + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) + goto err_out; + ni->mft_no = mft_no; + ni->nr_extents = -1; + ni->base_ni = base_ni; + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + + extent_nis = ntfs_malloc(i); + if (!extent_nis) + goto err_out; + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; +out: + ntfs_log_leave("\n"); + return ni; +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_attach_all_extents - attach all extents for target inode + * @ni: opened ntfs inode for which perform attach + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_inode_attach_all_extents(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + u64 prev_attached = 0; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + /* Inode haven't got attribute list, thus nothing to attach. */ + if (!NInoAttrList(ni)) + return 0; + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + /* Walk through attribute list and attach all extents. */ + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { + ntfs_log_trace("Couldn't attach extent inode.\n"); + return -1; + } + prev_attached = MREF_LE(ale->mft_reference); + } + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_inode_sync_standard_information - update standard information attribute + * @ni: ntfs inode to update standard information + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_standard_information(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u32 lth; + le32 lthle; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); + ntfs_attr_put_search_ctx(ctx); + return -1; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + std_info->file_attributes = ni->flags; + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; + } + + /* JPA update v3.x extensions, ensuring consistency */ + + lthle = ctx->attr->length; + lth = le32_to_cpu(lthle); + if (test_nino_flag(ni, v3_Extensions) + && (lth <= sizeof(STANDARD_INFORMATION))) + ntfs_log_error("bad sync of standard information\n"); + + if (lth > sizeof(STANDARD_INFORMATION)) { + std_info->owner_id = ni->owner_id; + std_info->security_id = ni->security_id; + std_info->quota_charged = ni->quota_charged; + std_info->usn = ni->usn; + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +} + +/** + * ntfs_inode_sync_file_name - update FILE_NAME attributes + * @ni: ntfs inode to update FILE_NAME attributes + * + * Update all FILE_NAME attributes for inode @ni in the index. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + ntfs_attr_search_ctx *ctx = NULL; + ntfs_index_context *ictx; + ntfs_inode *index_ni; + FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; + int err = 0; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } + /* Walk through all FILE_NAME attributes and update them. */ + while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) { + /* + * WARNING: We cheat here and obtain 2 attribute + * search contexts for one inode (first we obtained + * above, second will be obtained inside + * ntfs_index_lookup), it's acceptable for library, + * but will deadlock in the kernel. + */ + index_ni = ni; + } else + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); + if (!index_ni) { + if (!err) + err = errno; + ntfs_log_perror("Failed to open inode %lld with index", + (long long)le64_to_cpu(fn->parent_directory)); + continue; + } + ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); + if (!ictx) { + if (!err) + err = errno; + ntfs_log_perror("Failed to get index ctx, inode %lld", + (long long)index_ni->mft_no); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { + if (!err) { + if (errno == ENOENT) + err = EIO; + else + err = errno; + } + ntfs_log_perror("Index lookup failed, inode %lld", + (long long)index_ni->mft_no); + ntfs_index_ctx_put(ictx); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + /* Update flags and file size. */ + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + (ni->flags & FILE_ATTR_VALID_FLAGS); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + /* + * The file name record has also to be fixed if some + * attribute update implied the unnamed data to be + * made non-resident + */ + fn->allocated_size = fnx->allocated_size; + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; + } + ntfs_index_entry_mark_dirty(ictx); + ntfs_index_ctx_put(ictx); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Attribute lookup failed, inode %lld", + (long long)ni->mft_no); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err) { + errno = err; + return -1; + } + return 0; +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_inode_sync - write the inode (and its dirty extents) to disk + * @ni: ntfs inode to write + * + * Write the inode @ni to disk as well as its dirty extent inodes if such + * exist and @ni is a base inode. If @ni is an extent inode, only @ni is + * written completely disregarding its base inode and any other extent inodes. + * + * For a base inode with dirty extent inodes if any writes fail for whatever + * reason, the failing inode is skipped and the sync process is continued. At + * the end the error condition that brought about the failure is returned. Thus + * the smallest amount of data loss possible occurs. + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EBUSY - Inode and/or one of its extents is busy, try again later. + * EIO - I/O error while writing the inode (or one of its extents). + */ +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int ret = 0; + int err = 0; + if (!ni) { + errno = EINVAL; + ntfs_log_error("Failed to sync NULL inode\n"); + return -1; + } + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* Update STANDARD_INFORMATION. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + ntfs_inode_sync_standard_information(ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + } + + /* Update FILE_NAME's in the index. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoFileNameTestAndClearDirty(ni) && + ntfs_inode_sync_file_name(ni, dir_ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", + (long long)ni->mft_no); + NInoFileNameSetDirty(ni); + } + + /* Write out attribute list from cache to disk. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync failed " + "(open, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + goto sync_inode; + } + + if (na->data_size == ni->attr_list_size) { + if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, + ni->attr_list) != ni->attr_list_size) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync " + "failed (write, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + } + } else { + err = EIO; + ntfs_log_error("Attribute list sync failed (bad size, " + "inode %lld)\n", (long long)ni->mft_no); + NInoAttrListSetDirty(ni); + } + ntfs_attr_close(na); + } + +sync_inode: + /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ + if (NInoTestAndClearDirty(ni)) { + if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(ni); + ntfs_log_perror("MFT record sync failed, inode %lld", + (long long)ni->mft_no); + } + } + + /* If this is a base inode with extents write all dirty extents, too. */ + if (ni->nr_extents > 0) { + s32 i; + + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni; + + eni = ni->extent_nis[i]; + if (!NInoTestAndClearDirty(eni)) + continue; + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, + eni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(eni); + ntfs_log_perror("Extent MFT record sync failed," + " inode %lld/%lld", + (long long)ni->mft_no, + (long long)eni->mft_no); + } + } + } + + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; +} + +int ntfs_inode_sync(ntfs_inode *ni) +{ + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); +} + +/* + * Close an inode with an open parent inode + */ + +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); +} + +/** + * ntfs_inode_add_attrlist - add attribute list to inode and fill it + * @ni: opened ntfs inode to which add attribute list + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EEXIST - Attribute list already exist. + * EIO - Input/Ouput error occurred. + * ENOMEM - Not enough memory to perform add. + */ +int ntfs_inode_add_attrlist(ntfs_inode *ni) +{ + int err; + ntfs_attr_search_ctx *ctx; + u8 *al = NULL, *aln; + int al_len = 0; + ATTR_LIST_ENTRY *ale = NULL; + ntfs_attr *na; + + if (!ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); + + if (NInoAttrList(ni) || ni->nr_extents) { + errno = EEXIST; + ntfs_log_perror("Inode already has attribute list"); + return -1; + } + + /* Form attribute list. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Walk through all attributes. */ + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + + int ale_size; + + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { + err = EIO; + ntfs_log_perror("Attribute list already present"); + goto put_err_out; + } + + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + ctx->attr->name_length + 7) & ~7; + al_len += ale_size; + + aln = realloc(al, al_len); + if (!aln) { + err = errno; + ntfs_log_perror("Failed to realloc %d bytes", al_len); + goto put_err_out; + } + ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); + al = aln; + + memset(ale, 0, ale_size); + + /* Add attribute to attribute list. */ + ale->type = ctx->attr->type; + ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); + ale->name_length = ctx->attr->name_length; + ale->name_offset = (u8 *)ale->name - (u8 *)ale; + if (ctx->attr->non_resident) + ale->lowest_vcn = ctx->attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = MK_LE_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)); + ale->instance = ctx->attr->instance; + memcpy(ale->name, (u8 *)ctx->attr + + le16_to_cpu(ctx->attr->name_offset), + ctx->attr->name_length * sizeof(ntfschar)); + ale = (ATTR_LIST_ENTRY *)(al + al_len); + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed, inode %lld", + __FUNCTION__, (long long)ni->mft_no); + goto put_err_out; + } + + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + NInoAttrListSetDirty(ni); + + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) < + offsetof(ATTR_RECORD, resident_end)) { + if (ntfs_inode_free_space(ni, + offsetof(ATTR_RECORD, resident_end))) { + /* Failed to free space. */ + err = errno; + ntfs_log_perror("Failed to free space for attrlist"); + goto rollback; + } + } + + /* Add $ATTRIBUTE_LIST to mft record. */ + if (ntfs_resident_attr_record_add(ni, + AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { + err = errno; + ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); + goto rollback; + } + + /* Resize it. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); + goto remove_attrlist_record; + } + if (ntfs_attr_truncate(na, al_len)) { + err = errno; + ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); + ntfs_attr_close(na); + goto remove_attrlist_record;; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_attr_close(na); + return 0; + +remove_attrlist_record: + /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + /* Remove $ATTRIBUTE_LIST record. */ + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) + ntfs_log_perror("Rollback failed to remove attrlist"); + } else + ntfs_log_perror("Rollback failed to find attrlist"); + /* Setup back in-memory runlist. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); +rollback: + /* + * Scan attribute list for attributes that placed not in the base MFT + * record and move them to it. + */ + ntfs_attr_reinit_search_ctx(ctx); + ale = (ATTR_LIST_ENTRY*)al; + while ((u8*)ale < al + al_len) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) { + if (!ntfs_attr_lookup(ale->type, ale->name, + ale->name_length, + CASE_SENSITIVE, + sle64_to_cpu(ale->lowest_vcn), + NULL, 0, ctx)) { + if (ntfs_attr_record_move_to(ctx, ni)) + ntfs_log_perror("Rollback failed to " + "move attribute"); + } else + ntfs_log_perror("Rollback failed to find attr"); + ntfs_attr_reinit_search_ctx(ctx); + } + ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); + } + /* Remove in-memory attribute list. */ + ni->attr_list = NULL; + ni->attr_list_size = 0; + NInoClearAttrList(ni); + NInoAttrListClearDirty(ni); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(al); + errno = err; + return -1; +} + +/** + * ntfs_inode_free_space - free space in the MFT record of an inode + * @ni: ntfs inode in which MFT record needs more free space + * @size: amount of space needed to free + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_inode_free_space(ntfs_inode *ni, int size) +{ + ntfs_attr_search_ctx *ctx; + int freed; + + if (!ni || size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, size %d\n", + (unsigned long long)ni->mft_no, size); + + freed = (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use)); + + if (size <= freed) + return 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. + */ + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + goto put_err_out; + + while (1) { + int record_size; + /* + * Check whether attribute is from different MFT record. If so, + * find next, because we don't need such. + */ + while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: + if (ntfs_attr_position(AT_UNUSED, ctx)) + goto put_err_out; + } + + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + + record_size = le32_to_cpu(ctx->attr->length); + + if (ntfs_attr_record_move_away(ctx, 0)) { + ntfs_log_perror("Failed to move out attribute #2"); + break; + } + freed += record_size; + + /* Check whether we are done. */ + if (size <= freed) { + ntfs_attr_put_search_ctx(ctx); + return 0; + } + /* + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + break; + } +put_err_out: + ntfs_attr_put_search_ctx(ctx); + if (errno == ENOSPC) + ntfs_log_trace("No attributes left that could be moved out.\n"); + return -1; +} + +/** + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated + * + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). + */ +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + ntfs_time now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; + } + + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; + + now = ntfs_current_time(); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) + ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) + ni->last_mft_change_time = now; + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); +} + +/** + * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute + * @mft_no: mft record number where @attr is present + * @attr: attribute record used to check for the $Bad attribute + * + * Check if the mft record given by @mft_no and @attr contains the bad sector + * list. Please note that mft record numbers describing $Badclus extent inodes + * will not match the current $Badclus:$Bad check. + * + * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. + * On error return -1 with errno set to the error code. + */ +int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) +{ + int len, ret = 0; + ntfschar *ustr; + + if (!attr) { + ntfs_log_error("Invalid argument.\n"); + errno = EINVAL; + return -1; + } + + if (mft_no != FILE_BadClus) + return 0; + + if (attr->type != AT_DATA) + return 0; + + if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) { + ntfs_log_perror("Couldn't convert '$Bad' to Unicode"); + return -1; + } + + if (ustr && ntfs_names_are_equal(ustr, len, + (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)), + attr->name_length, 0, NULL, 0)) + ret = 1; + + ntfs_ucsfree(ustr); + + return ret; +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get high precision NTFS times + * + * They are returned in following order : create, update, access, change + * provided they fit in requested size. + * + * Returns the modified size if successfull (or 32 if buffer size is null) + * -errno if failed + */ + +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u64 *times; + int ret; + + ret = 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (value && (size >= 8)) { + times = (u64*)value; + times[0] = le64_to_cpu(std_info->creation_time); + ret = 8; + if (size >= 16) { + times[1] = le64_to_cpu(std_info->last_data_change_time); + ret = 16; + } + if (size >= 24) { + times[2] = le64_to_cpu(std_info->last_access_time); + ret = 24; + } + if (size >= 32) { + times[3] = le64_to_cpu(std_info->last_mft_change_time); + ret = 32; + } + } else + if (!size) + ret = 32; + else + ret = -ERANGE; + } + ntfs_attr_put_search_ctx(ctx); + } + return (ret ? ret : -errno); +} + +/* + * Set high precision NTFS times + * + * They are expected in this order : create, update, access + * provided they are present in input. The change time is set to + * current time. + * + * The times are inserted directly in the standard_information and + * file names attributes to avoid manipulating low precision times + * + * Returns 0 if success + * -1 if there were an error (described by errno) + */ + +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + FILE_NAME_ATTR *fn; + const u64 *times; + ntfs_time now; + int cnt; + int ret; + + ret = -1; + if ((size >= 8) && !(flags & XATTR_CREATE)) { + times = (const u64*)value; + now = ntfs_current_time(); + /* update the standard information attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() + */ + set_nino_flag(ni, TimesSet); + std_info->creation_time = cpu_to_le64(times[0]); + ni->creation_time + = std_info->creation_time; + if (size >= 16) { + std_info->last_data_change_time = cpu_to_le64(times[1]); + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { + std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } + std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); + + /* update the file names attributes */ + ntfs_attr_reinit_search_ctx(ctx); + cnt = 0; + while (!ntfs_attr_lookup(AT_FILE_NAME, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + fn->creation_time + = cpu_to_le64(times[0]); + if (size >= 16) + fn->last_data_change_time + = cpu_to_le64(times[1]); + if (size >= 24) + fn->last_access_time + = cpu_to_le64(times[2]); + fn->last_mft_change_time = now; + cnt++; + } + if (cnt) + ret = 0; + else { + ntfs_log_perror("Failed to get file names (inode %lld)", + (long long)ni->mft_no); + } + } + ntfs_attr_put_search_ctx(ctx); + } + } else + if (size < 8) + errno = ERANGE; + else + errno = EEXIST; + return (ret); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/src/source/inode.h b/lib/libntfs/src/source/inode.h new file mode 100644 index 0000000..5a6f7da --- /dev/null +++ b/lib/libntfs/src/source/inode.h @@ -0,0 +1,225 @@ +/* + * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INODE_H +#define _NTFS_INODE_H + +/* Forward declaration */ +typedef struct _ntfs_inode ntfs_inode; + +#include "types.h" +#include "layout.h" +#include "support.h" +#include "volume.h" +#include "ntfstime.h" + +/** + * enum ntfs_inode_state_bits - + * + * Defined bits for the state field in the ntfs_inode structure. + * (f) = files only, (d) = directories only + */ +typedef enum { + NI_Dirty, /* 1: Mft record needs to be written to disk. */ + + /* The NI_AttrList* tests only make sense for base inodes. */ + NI_AttrList, /* 1: Mft record contains an attribute list. */ + NI_AttrListDirty, /* 1: Attribute list needs to be written to the + mft record and then to disk. */ + NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated + in the index. */ + NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ +} ntfs_inode_state_bits; + +#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) +#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) +#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) + +#define test_and_set_nino_flag(ni, flag) \ + test_and_set_bit(NI_##flag, (ni)->state) +#define test_and_clear_nino_flag(ni, flag) \ + test_and_clear_bit(NI_##flag, (ni)->state) + +#define NInoDirty(ni) test_nino_flag(ni, Dirty) +#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) +#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) +#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) +#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) + +#define NInoAttrList(ni) test_nino_flag(ni, AttrList) +#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) +#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) + + +#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) +#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) +#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) + +#define test_and_set_nino_al_flag(ni, flag) \ + test_and_set_nino_flag(ni, AttrList##flag) +#define test_and_clear_nino_al_flag(ni, flag) \ + test_and_clear_nino_flag(ni, AttrList##flag) + +#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) +#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) +#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) + +#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndSetDirty(ni) \ + test_and_set_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndClearDirty(ni) \ + test_and_clear_nino_flag(ni, FileNameDirty) + +/** + * struct _ntfs_inode - The NTFS in-memory inode structure. + * + * It is just used as an extension to the fields already provided in the VFS + * inode. + */ +struct _ntfs_inode { + u64 mft_no; /* Inode / mft record number. */ + MFT_RECORD *mrec; /* The actual mft record of the inode. */ + ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ + unsigned long state; /* NTFS specific flags describing this inode. + See ntfs_inode_state_bits above. */ + FILE_ATTR_FLAGS flags; /* Flags describing the file. + (Copy from STANDARD_INFORMATION) */ + /* + * Attribute list support (for use by the attribute lookup functions). + * Setup during ntfs_open_inode() for all inodes with attribute lists. + * Only valid if NI_AttrList is set in state. + */ + u32 attr_list_size; /* Length of attribute list value in bytes. */ + u8 *attr_list; /* Attribute list value itself. */ + /* Below fields are always valid. */ + s32 nr_extents; /* For a base mft record, the number of + attached extent inodes (0 if none), for + extent records this is -1. */ + union { /* This union is only used if nr_extents != 0. */ + ntfs_inode **extent_nis;/* For nr_extents > 0, array of the + ntfs inodes of the extent mft + records belonging to this base + inode which have been loaded. */ + ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs + inode of the base mft record. */ + }; + + /* Below fields are valid only for base inode. */ + + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. + */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ + s64 allocated_size; /* Allocated size stored in the filename + index. (NOTE: Equal to allocated size of + the unnamed data attribute for normal or + encrypted files and to compressed size + of the unnamed data attribute for sparse or + compressed files.) */ + + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; + /* NTFS 3.x extensions added by JPA */ + /* only if NI_v3_Extensions is set in state */ + le32 owner_id; + le32 security_id; + le64 quota_charged; + le64 usn; +}; + +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) +#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) + +extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); + +extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); + +extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); + +extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_NIDATA_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); + +#endif + + +extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, + const MFT_REF mref); + +extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); + +extern void ntfs_inode_mark_dirty(ntfs_inode *ni); + +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); + +extern int ntfs_inode_sync(ntfs_inode *ni); + +extern int ntfs_inode_add_attrlist(ntfs_inode *ni); + +extern int ntfs_inode_free_space(ntfs_inode *ni, int size); + +extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); + +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); + +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); + +/* debugging */ +#define debug_double_inode(num, type) +#define debug_cached_inode(ni) + +#endif /* defined _NTFS_INODE_H */ diff --git a/lib/libntfs/src/source/layout.h b/lib/libntfs/src/source/layout.h new file mode 100644 index 0000000..c167135 --- /dev/null +++ b/lib/libntfs/src/source/layout.h @@ -0,0 +1,2662 @@ +/* + * layout.h - Ntfs on-disk layout structures. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LAYOUT_H +#define _NTFS_LAYOUT_H + +#include "types.h" +#include "endians.h" +#include "support.h" + +/* The NTFS oem_id */ +#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ +#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ + +/* + * Location of bootsector on partition: + * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. + * On NT4 and above there is one backup copy of the boot sector to + * be found on the last sector of the partition (not normally accessible + * from within Windows as the bootsector contained number of sectors + * value is one less than the actual value!). + * On versions of NT 3.51 and earlier, the backup copy was located at + * number of sectors/2 (integer divide), i.e. in the middle of the volume. + */ + +/** + * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. + */ +typedef struct { + u16 bytes_per_sector; /* Size of a sector in bytes. */ + u8 sectors_per_cluster; /* Size of a cluster in sectors. */ + u16 reserved_sectors; /* zero */ + u8 fats; /* zero */ + u16 root_entries; /* zero */ + u16 sectors; /* zero */ + u8 media_type; /* 0xf8 = hard disk */ + u16 sectors_per_fat; /* zero */ +/*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ +/*0x0f*/u16 heads; /* Required to boot Windows. */ +/*0x11*/u32 hidden_sectors; /* Offset to the start of the partition + relative to the disk in sectors. + Required to boot Windows. */ +/*0x15*/u32 large_sectors; /* zero */ +/* sizeof() = 25 (0x19) bytes */ +} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; + +/** + * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. + */ +typedef struct { + u8 jump[3]; /* Irrelevant (jump to boot up code).*/ + u64 oem_id; /* Magic "NTFS ". */ +/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ + u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ + u8 current_head; /* zero */ + u8 extended_boot_signature; /* 0x80 */ + u8 reserved2; /* zero */ +/*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives + maximum volume size of 2^63 sectors. + Assuming standard sector size of 512 + bytes, the maximum byte size is + approx. 4.7x10^21 bytes. (-; */ + s64 mft_lcn; /* Cluster location of mft data. */ + s64 mftmirr_lcn; /* Cluster location of copy of mft. */ + s8 clusters_per_mft_record; /* Mft record size in clusters. */ + u8 reserved0[3]; /* zero */ + s8 clusters_per_index_record; /* Index block size in clusters. */ + u8 reserved1[3]; /* zero */ + u64 volume_serial_number; /* Irrelevant (serial number). */ + u32 checksum; /* Boot sector checksum. */ +/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ + u16 end_of_sector_marker; /* End of bootsector magic. Always is + 0xaa55 in little endian. */ +/* sizeof() = 512 (0x200) bytes */ +} __attribute__((__packed__)) NTFS_BOOT_SECTOR; + +/** + * enum NTFS_RECORD_TYPES - + * + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + */ +typedef enum { + /* Found in $MFT/$DATA. */ + magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */ + magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */ + magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */ + + /* Found in $LogFile/$DATA. */ + magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */ + magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */ + + /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ + magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */ + + /* Found in all ntfs record containing records. */ + magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector + transfer was detected. */ + + /* + * Found in $LogFile/$DATA when a page is full or 0xff bytes and is + * thus not initialized. User has to initialize the page before using + * it. + */ + magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has + to be initialized before + it can be used. */ +} NTFS_RECORD_TYPES; + +/* + * Generic magic comparison macros. Finally found a use for the ## preprocessor + * operator! (-8 + */ +#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) +#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) + +/* + * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above. + */ +#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) +#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) +#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) +#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) +#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) +#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) +#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) +#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) + +#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) +#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) +#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) +#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) + +#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) +#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) + +#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) +#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) + +#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) +#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) + + +#define NTFS_BLOCK_SIZE 512 +#define NTFS_BLOCK_SIZE_BITS 9 + +/** + * struct NTFS_RECORD - + * + * The Update Sequence Array (usa) is an array of the u16 values which belong + * to the end of each sector protected by the update sequence record in which + * this array is contained. Note that the first entry is the Update Sequence + * Number (usn), a cyclic counter of how many times the protected record has + * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All + * last u16's of each sector have to be equal to the usn (during reading) or + * are set to it (during writing). If they are not, an incomplete multi sector + * transfer has occurred when the data was written. + * The maximum size for the update sequence array is fixed to: + * maximum size = usa_ofs + (usa_count * 2) = 510 bytes + * The 510 bytes comes from the fact that the last u16 in the array has to + * (obviously) finish before the last u16 of the first 512-byte sector. + * This formula can be used as a consistency check in that usa_ofs + + * (usa_count * 2) has to be less than or equal to 510. + */ +typedef struct { + NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the + record type and/or status. */ + u16 usa_ofs; /* Offset to the Update Sequence Array (usa) + from the start of the ntfs record. */ + u16 usa_count; /* Number of u16 sized entries in the usa + including the Update Sequence Number (usn), + thus the number of fixups is the usa_count + minus 1. */ +} __attribute__((__packed__)) NTFS_RECORD; + +/** + * enum NTFS_SYSTEM_FILES - System files mft record numbers. + * + * All these files are always marked as used in the bitmap attribute of the + * mft; presumably in order to avoid accidental allocation for random other + * mft records. Also, the sequence number for each of the system files is + * always equal to their mft record number and it is never modified. + */ +typedef enum { + FILE_MFT = 0, /* Master file table (mft). Data attribute + contains the entries and bitmap attribute + records which ones are in use (bit==1). */ + FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records + in data attribute. If cluster size > 4kiB, + copy of first N mft records, with + N = cluster_size / mft_record_size. */ + FILE_LogFile = 2, /* Journalling log in data attribute. */ + FILE_Volume = 3, /* Volume name attribute and volume information + attribute (flags and ntfs version). Windows + refers to this file as volume DASD (Direct + Access Storage Device). */ + FILE_AttrDef = 4, /* Array of attribute definitions in data + attribute. */ + FILE_root = 5, /* Root directory. */ + FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in + data attribute. */ + FILE_Boot = 7, /* Boot sector (always at cluster 0) in data + attribute. */ + FILE_BadClus = 8, /* Contains all bad clusters in the non-resident + data attribute. */ + FILE_Secure = 9, /* Shared security descriptors in data attribute + and two indexes into the descriptors. + Appeared in Windows 2000. Before that, this + file was named $Quota but was unused. */ + FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode + characters in data attribute. */ + FILE_Extend = 11, /* Directory containing other system files (eg. + $ObjId, $Quota, $Reparse and $UsnJrnl). This + is new to NTFS3.0. */ + FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_first_user = 16, /* First user file, used as test limit for + whether to allow opening a file or not. */ +} NTFS_SYSTEM_FILES; + +/** + * enum MFT_RECORD_FLAGS - + * + * These are the so far known MFT_RECORD_* flags (16-bit) which contain + * information about the mft record in which they are present. + * + * MFT_RECORD_IS_4 exists on all $Extend sub-files. + * It seems that it marks it is a metadata file with MFT record >24, however, + * it is unknown if it is limited to metadata files only. + * + * MFT_RECORD_IS_VIEW_INDEX exists on every metafile with a non directory + * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other + * than "$I30". It is unknown if it is limited to metadata files only. + */ +typedef enum { + MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001), + MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002), + MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004), + MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008), + MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags + 16-bit. */ +} __attribute__((__packed__)) MFT_RECORD_FLAGS; + +/* + * mft references (aka file references or file record segment references) are + * used whenever a structure needs to refer to a record in the mft. + * + * A reference consists of a 48-bit index into the mft and a 16-bit sequence + * number used to detect stale references. + * + * For error reporting purposes we treat the 48-bit index as a signed quantity. + * + * The sequence number is a circular counter (skipping 0) describing how many + * times the referenced mft record has been (re)used. This has to match the + * sequence number of the mft record being referenced, otherwise the reference + * is considered stale and removed (FIXME: only ntfsck or the driver itself?). + * + * If the sequence number is zero it is assumed that no sequence number + * consistency checking should be performed. + * + * FIXME: Since inodes are 32-bit as of now, the driver needs to always check + * for high_part being 0 and if not either BUG(), cause a panic() or handle + * the situation in some other way. This shouldn't be a problem as a volume has + * to become HUGE in order to need more than 32-bits worth of mft records. + * Assuming the standard mft record size of 1kb only the records (never mind + * the non-resident attributes, etc.) would require 4Tb of space on their own + * for the first 32 bits worth of records. This is only if some strange person + * doesn't decide to foul play and make the mft sparse which would be a really + * horrible thing to do as it would trash our current driver implementation. )-: + * Do I hear screams "we want 64-bit inodes!" ?!? (-; + * + * FIXME: The mft zone is defined as the first 12% of the volume. This space is + * reserved so that the mft can grow contiguously and hence doesn't become + * fragmented. Volume free space includes the empty part of the mft zone and + * when the volume's free 88% are used up, the mft zone is shrunk by a factor + * of 2, thus making more space available for more files/data. This process is + * repeated every time there is no more free space except for the mft zone until + * there really is no more free space. + */ + +/* + * Typedef the MFT_REF as a 64-bit value for easier handling. + * Also define two unpacking macros to get to the reference (MREF) and + * sequence number (MSEQNO) respectively. + * The _LE versions are to be applied on little endian MFT_REFs. + * Note: The _LE versions will return a CPU endian formatted value! + */ +#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL +#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU) + +typedef u64 MFT_REF; +typedef le64 leMFT_REF; /* a little-endian MFT_MREF */ + +#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU))) +#define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) + +#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) +#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) +#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) +#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) + +#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) +#define ERR_MREF(x) ((u64)((s64)(x))) +#define MREF_ERR(x) ((int)((s64)(x))) + +/** + * struct MFT_RECORD - An MFT record layout (NTFS 3.1+) + * + * The mft record header present at the beginning of every record in the mft. + * This is followed by a sequence of variable length attribute records which + * is terminated by an attribute of type AT_END which is a truncated attribute + * in that it only consists of the attribute type code AT_END and none of the + * other members of the attribute structure are present. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ +/* 42*/ u16 reserved; /* Reserved/alignment. */ +/* 44*/ u32 mft_record_number; /* Number of this mft record. */ +/* sizeof() = 48 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD; + +/** + * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) + * + * This is the version without the NTFS 3.1+ specific fields. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* sizeof() = 42 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD_OLD; + +/** + * enum ATTR_TYPES - System defined attributes (32-bit). + * + * Each attribute type has a corresponding attribute name (Unicode string of + * maximum 64 character length) as described by the attribute definitions + * present in the data attribute of the $AttrDef system file. + * + * On NTFS 3.0 volumes the names are just as the types are named in the below + * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing + * choice of symbol... (-; + */ +typedef enum { + AT_UNUSED = const_cpu_to_le32( 0), + AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10), + AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20), + AT_FILE_NAME = const_cpu_to_le32( 0x30), + AT_OBJECT_ID = const_cpu_to_le32( 0x40), + AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50), + AT_VOLUME_NAME = const_cpu_to_le32( 0x60), + AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70), + AT_DATA = const_cpu_to_le32( 0x80), + AT_INDEX_ROOT = const_cpu_to_le32( 0x90), + AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0), + AT_BITMAP = const_cpu_to_le32( 0xb0), + AT_REPARSE_POINT = const_cpu_to_le32( 0xc0), + AT_EA_INFORMATION = const_cpu_to_le32( 0xd0), + AT_EA = const_cpu_to_le32( 0xe0), + AT_PROPERTY_SET = const_cpu_to_le32( 0xf0), + AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100), + AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000), + AT_END = const_cpu_to_le32(0xffffffff), +} ATTR_TYPES; + +/** + * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc + * (32-bit). + * + * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary + * Unicode values, except that when a character can be uppercased, the + * upper case value collates before the lower case one. + * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation + * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea + * what the difference is. Perhaps the difference is that file names + * would treat some special characters in an odd way (see + * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] + * for what I mean but COLLATION_UNICODE_STRING would not give any special + * treatment to any characters at all, but this is speculation. + * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key + * values. E.g. used for $SII index in FILE_Secure, which sorts by + * security_id (u32). + * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. + * E.g. used for $O index in FILE_Extend/$Quota. + * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash + * values and second by ascending security_id values. E.g. used for $SDH + * index in FILE_Secure. + * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending + * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which + * sorts by object_id (16-byte), by splitting up the object_id in four + * u32 values and using them as individual keys. E.g. take the following + * two security_ids, stored as follows on disk: + * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 + * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 + * To compare them, they are split into four u32 values each, like so: + * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 + * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 + * Now, it is apparent why the 2nd object_id collates after the 1st: the + * first u32 value of the 1st object_id is less than the first u32 of + * the 2nd object_id. If the first u32 values of both object_ids were + * equal then the second u32 values would be compared, etc. + */ +typedef enum { + COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary + compare where the first byte is most + significant. */ + COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names + as Unicode strings. */ + COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode + strings by comparing their binary + Unicode values, except that when a + character can be uppercased, the upper + case value collates before the lower + case one. */ + COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), + COLLATION_NTOFS_SID = const_cpu_to_le32(17), + COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), + COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), +} COLLATION_RULES; + +/** + * enum ATTR_DEF_FLAGS - + * + * The flags (32-bit) describing attribute properties in the attribute + * definition structure. FIXME: This information is based on Regis's + * information and, according to him, it is not certain and probably + * incomplete. The INDEXABLE flag is fairly certainly correct as only the file + * name attribute has this flag set and this is the only attribute indexed in + * NT4. + */ +typedef enum { + ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be + indexed. */ + ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type + can be present multiple times in the + mft records of an inode. */ + ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value + must contain at least one non-zero + byte. */ + ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be + indexed and the attribute value must be + unique for the attribute type in all of + the mft records of an inode. */ + ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be + named and the name must be unique for + the attribute type in all of the mft + records of an inode. */ + ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be + resident. */ + ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log + modifications to this attribute, + regardless of whether it is resident or + non-resident. Without this, only log + modifications if the attribute is + resident. */ +} ATTR_DEF_FLAGS; + +/** + * struct ATTR_DEF - + * + * The data attribute of FILE_AttrDef contains a sequence of attribute + * definitions for the NTFS volume. With this, it is supposed to be safe for an + * older NTFS driver to mount a volume containing a newer NTFS version without + * damaging it (that's the theory. In practice it's: not damaging it too much). + * Entries are sorted by attribute type. The flags describe whether the + * attribute can be resident/non-resident and possibly other things, but the + * actual bits are unknown. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero + terminated. */ +/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ +/* 84*/ u32 display_rule; /* Default display rule. + FIXME: What does it mean? (AIA) */ +/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ +/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ +/* 90*/ s64 min_size; /* Optional minimum attribute size. */ +/* 98*/ s64 max_size; /* Maximum size of attribute. */ +/* sizeof() = 0xa0 or 160 bytes */ +} __attribute__((__packed__)) ATTR_DEF; + +/** + * enum ATTR_FLAGS - Attribute flags (16-bit). + */ +typedef enum { + ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001), + ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression + method mask. Also, first + illegal value. */ + ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000), + ATTR_IS_SPARSE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) ATTR_FLAGS; + +/* + * Attribute compression. + * + * Only the data attribute is ever compressed in the current ntfs driver in + * Windows. Further, compression is only applied when the data attribute is + * non-resident. Finally, to use compression, the maximum allowed cluster size + * on a volume is 4kib. + * + * The compression method is based on independently compressing blocks of X + * clusters, where X is determined from the compression_unit value found in the + * non-resident attribute record header (more precisely: X = 2^compression_unit + * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4). + * + * There are three different cases of how a compression block of X clusters + * can be stored: + * + * 1) The data in the block is all zero (a sparse block): + * This is stored as a sparse block in the runlist, i.e. the runlist + * entry has length = X and lcn = -1. The mapping pairs array actually + * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at + * all, which is then interpreted by the driver as lcn = -1. + * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then + * the same principles apply as above, except that the length is not + * restricted to being any particular value. + * + * 2) The data in the block is not compressed: + * This happens when compression doesn't reduce the size of the block + * in clusters. I.e. if compression has a small effect so that the + * compressed data still occupies X clusters, then the uncompressed data + * is stored in the block. + * This case is recognised by the fact that the runlist entry has + * length = X and lcn >= 0. The mapping pairs array stores this as + * normal with a run length of X and some specific delta_lcn, i.e. + * delta_lcn has to be present. + * + * 3) The data in the block is compressed: + * The common case. This case is recognised by the fact that the run + * list entry has length L < X and lcn >= 0. The mapping pairs array + * stores this as normal with a run length of X and some specific + * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is + * immediately followed by a sparse entry with length = X - L and + * lcn = -1. The latter entry is to make up the vcn counting to the + * full compression block size X. + * + * In fact, life is more complicated because adjacent entries of the same type + * can be coalesced. This means that one has to keep track of the number of + * clusters handled and work on a basis of X clusters at a time being one + * block. An example: if length L > X this means that this particular runlist + * entry contains a block of length X and part of one or more blocks of length + * L - X. Another example: if length L < X, this does not necessarily mean that + * the block is compressed as it might be that the lcn changes inside the block + * and hence the following runlist entry describes the continuation of the + * potentially compressed block. The block would be compressed if the + * following runlist entry describes at least X - L sparse clusters, thus + * making up the compression block length as described in point 3 above. (Of + * course, there can be several runlist entries with small lengths so that the + * sparse entry does not follow the first data containing entry with + * length < X.) + * + * NOTE: At the end of the compressed attribute value, there most likely is not + * just the right amount of data to make up a compression block, thus this data + * is not even attempted to be compressed. It is just stored as is, unless + * the number of clusters it occupies is reduced when compressed in which case + * it is stored as a compressed compression block, complete with sparse + * clusters at the end. + */ + +/** + * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit). + */ +typedef enum { + RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index + (has implications for deleting and + modifying the attribute). */ +} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS; + +/** + * struct ATTR_RECORD - Attribute record header. + * + * Always aligned to 8-byte boundary. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ +/* 4*/ u32 length; /* Byte size of the resident part of the + attribute (aligned to 8-byte boundary). + Used to get to the next attribute. */ +/* 8*/ u8 non_resident; /* If 0, attribute is resident. + If 1, attribute is non-resident. */ +/* 9*/ u8 name_length; /* Unicode character size of name of attribute. + 0 if unnamed. */ +/* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the + beginning of the name from the attribute + record. Note that the name is stored as a + Unicode string. When creating, place offset + just at the end of the record header. Then, + follow with attribute value or mapping pairs + array, resident and non-resident attributes + respectively, aligning to an 8-byte + boundary. */ +/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ +/* 14*/ u16 instance; /* The instance of this attribute record. This + number is unique within this mft record (see + MFT_RECORD/next_attribute_instance notes + above for more details). */ +/* 16*/ union { + /* Resident attributes. */ + struct { +/* 16 */ u32 value_length; /* Byte size of attribute value. */ +/* 20 */ u16 value_offset; /* Byte offset of the attribute + value from the start of the + attribute record. When creating, + align to 8-byte boundary if we + have a name present as this might + not have a length of a multiple + of 8-bytes. */ +/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ +/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte + boundary. */ +/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, + resident_end) to get size of + a resident attribute. */ + } __attribute__((__packed__)); + /* Non-resident attributes. */ + struct { +/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number + for this portion of the attribute value or + 0 if this is the only extent (usually the + case). - Only when an attribute list is used + does lowest_vcn != 0 ever occur. */ +/* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of + the attribute value. - Usually there is only one + portion, so this usually equals the attribute + value size in clusters minus 1. Can be -1 for + zero length files. Can be 0 for "single extent" + attributes. */ +/* 32*/ u16 mapping_pairs_offset; /* Byte offset from the + beginning of the structure to the mapping pairs + array which contains the mappings between the + vcns and the logical cluster numbers (lcns). + When creating, place this at the end of this + record header aligned to 8-byte boundary. */ +/* 34*/ u8 compression_unit; /* The compression unit expressed + as the log to the base 2 of the number of + clusters in a compression unit. 0 means not + compressed. (This effectively limits the + compression unit size to be a power of two + clusters.) WinNT4 only uses a value of 4. */ +/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ +/* The sizes below are only used when lowest_vcn is zero, as otherwise it would + be difficult to keep them up-to-date.*/ +/* 40*/ s64 allocated_size; /* Byte size of disk space + allocated to hold the attribute value. Always + is a multiple of the cluster size. When a file + is compressed, this field is a multiple of the + compression block size (2^compression_unit) and + it represents the logically allocated space + rather than the actual on disk usage. For this + use the compressed_size (see below). */ +/* 48*/ s64 data_size; /* Byte size of the attribute + value. Can be larger than allocated_size if + attribute value is compressed or sparse. */ +/* 56*/ s64 initialized_size; /* Byte size of initialized + portion of the attribute value. Usually equals + data_size. */ +/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, + non_resident_end) to get + size of a non resident + attribute. */ +/* sizeof(uncompressed attr) = 64*/ +/* 64*/ s64 compressed_size; /* Byte size of the attribute + value after compression. Only present when + compressed. Always is a multiple of the + cluster size. Represents the actual amount of + disk space being used on the disk. */ +/* 72 */ void *compressed_end[0]; + /* Use offsetof(ATTR_RECORD, compressed_end) to + get size of a compressed attribute. */ +/* sizeof(compressed attr) = 72*/ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) ATTR_RECORD; + +typedef ATTR_RECORD ATTR_REC; + +/** + * enum FILE_ATTR_FLAGS - File attribute flags (32-bit). + */ +typedef enum { + /* + * These flags are only present in the STANDARD_INFORMATION attribute + * (in the field file_attributes). + */ + FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001), + FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002), + FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004), + /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ + + FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), + /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved + for the DOS SUBDIRECTORY flag. */ + FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020), + FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040), + FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080), + + FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100), + FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200), + FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400), + FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800), + + FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), + FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), + FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), + + FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), + /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the + FILE_ATTR_DEVICE and preserves everything else. This mask + is used to obtain all flags that are valid for reading. */ + FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7), + /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the + FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, + FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED + and preserves the rest. This mask is used to to obtain all flags that + are valid for setting. */ + + /** + * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? + * + * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft + * record, telling us whether this is a directory or not, i.e. whether + * it has an index root attribute named "$I30" or not. + * + * This flag is only present in the FILE_NAME attribute (in the + * file_attributes field). + */ + FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000), + + /** + * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? + * + * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft + * record, telling us whether this file has a view index present (eg. + * object id index, quota index, one of the security indexes and the + * reparse points index). + * + * This flag is only present in the $STANDARD_INFORMATION and + * $FILE_NAME attributes. + */ + FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000), +} __attribute__((__packed__)) FILE_ATTR_FLAGS; + +/* + * NOTE on times in NTFS: All times are in MS standard time format, i.e. they + * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00 + * universal coordinated time (UTC). (In Linux time starts 1st January 1970, + * 00:00:00 UTC and is stored as the number of 1-second intervals since then.) + */ + +/** + * struct STANDARD_INFORMATION - Attribute: Standard information (0x10). + * + * NOTE: Always resident. + * NOTE: Present in all base file records on a volume. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*Ofs*/ +/* 0*/ s64 creation_time; /* Time file was created. Updated when + a filename is changed(?). */ +/* 8*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 16*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 24*/ s64 last_access_time; /* Approximate time when the file was + last accessed (obviously this is not + updated on read-only volumes). In + Windows this is only updated when + accessed if some time delta has + passed since the last update. Also, + last access times updates can be + disabled altogether for speed. */ +/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 36*/ union { + /* NTFS 1.2 (and previous, presumably) */ + struct { + /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte + boundary. */ + /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); +/* sizeof() = 48 bytes */ + /* NTFS 3.0 */ + struct { +/* + * If a volume has been upgraded from a previous NTFS version, then these + * fields are present only if the file has been accessed since the upgrade. + * Recognize the difference by comparing the length of the resident attribute + * value. If it is 48, then the following fields are missing. If it is 72 then + * the fields are present. Maybe just check like this: + * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { + * Assume NTFS 1.2- format. + * If (volume version is 3.0+) + * Upgrade attribute to NTFS 3.0 format. + * else + * Use NTFS 1.2- format for access. + * } else + * Use NTFS 3.0 format for access. + * Only problem is that it might be legal to set the length of the value to + * arbitrarily large values thus spoiling this check. - But chkdsk probably + * views that as a corruption, assuming that it behaves like this for all + * attributes. + */ + /* 36*/ u32 maximum_versions; /* Maximum allowed versions for + file. Zero if version numbering is disabled. */ + /* 40*/ u32 version_number; /* This file's version (if any). + Set to zero if maximum_versions is zero. */ + /* 44*/ u32 class_id; /* Class id from bidirectional + class id index (?). */ + /* 48*/ u32 owner_id; /* Owner_id of the user owning + the file. Translate via $Q index in FILE_Extend + /$Quota to the quota control entry for the user + owning the file. Zero if quotas are disabled. */ + /* 52*/ u32 security_id; /* Security_id for the file. + Translate via $SII index and $SDS data stream + in FILE_Secure to the security descriptor. */ + /* 56*/ u64 quota_charged; /* Byte size of the charge to + the quota for all streams of the file. Note: Is + zero if quotas are disabled. */ + /* 64*/ u64 usn; /* Last update sequence number + of the file. This is a direct index into the + change (aka usn) journal file. It is zero if + the usn journal is disabled. + NOTE: To disable the journal need to delete + the journal file itself and to then walk the + whole mft and set all Usn entries in all mft + records to zero! (This can take a while!) + The journal is FILE_Extend/$UsnJrnl. Win2k + will recreate the journal and initiate + logging if necessary when mounting the + partition. This, in contrast to disabling the + journal is a very fast process, so the user + won't even notice it. */ + /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* sizeof() = 72 bytes (NTFS 3.0) */ +} __attribute__((__packed__)) STANDARD_INFORMATION; + +/** + * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20). + * + * - Can be either resident or non-resident. + * - Value consists of a sequence of variable length, 8-byte aligned, + * ATTR_LIST_ENTRY records. + * - The attribute list attribute contains one entry for each attribute of + * the file in which the list is located, except for the list attribute + * itself. The list is sorted: first by attribute type, second by attribute + * name (if present), third by instance number. The extents of one + * non-resident attribute (if present) immediately follow after the initial + * extent. They are ordered by lowest_vcn and have their instance set to zero. + * It is not allowed to have two attributes with all sorting keys equal. + * - Further restrictions: + * - If not resident, the vcn to lcn mapping array has to fit inside the + * base mft record. + * - The attribute list attribute value has a maximum size of 256kb. This + * is imposed by the Windows cache manager. + * - Attribute lists are only used when the attributes of mft record do not + * fit inside the mft record despite all attributes (that can be made + * non-resident) having been made non-resident. This can happen e.g. when: + * - File has a large number of hard links (lots of file name + * attributes present). + * - The mapping pairs array of some non-resident attribute becomes so + * large due to fragmentation that it overflows the mft record. + * - The security descriptor is very complex (not applicable to + * NTFS 3.0 volumes). + * - There are many named streams. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ +/* 4*/ u16 length; /* Byte size of this entry. */ +/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the + attribute or 0 if unnamed. */ +/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name + (always set this to where the name would + start even if unnamed). */ +/* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion + of the attribute value. This is usually 0. It + is non-zero for the case where one attribute + does not fit into one mft record and thus + several mft records are allocated to hold + this attribute. In the latter case, each mft + record holds one extent of the attribute and + there is one attribute list entry for each + extent. NOTE: This is DEFINITELY a signed + value! The windows driver uses cmp, followed + by jg when comparing this, thus it treats it + as signed. */ +/* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding + the ATTR_RECORD for this portion of the + attribute value. */ +/* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the + attribute being referenced; otherwise 0. */ +/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use + name_offset to determine the location of the + name. */ +/* sizeof() = 26 + (attribute_name_length * 2) bytes */ +} __attribute__((__packed__)) ATTR_LIST_ENTRY; + +/* + * The maximum allowed length for a file name. + */ +#define NTFS_MAX_NAME_LEN 255 + +/** + * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs. + * (8-bit). + */ +typedef enum { + FILE_NAME_POSIX = 0x00, + /* This is the largest namespace. It is case sensitive and + allows all Unicode characters except for: '\0' and '/'. + Beware that in WinNT/2k files which eg have the same name + except for their case will not be distinguished by the + standard utilities and thus a "del filename" will delete + both "filename" and "fileName" without warning. */ + FILE_NAME_WIN32 = 0x01, + /* The standard WinNT/2k NTFS long filenames. Case insensitive. + All Unicode chars except: '\0', '"', '*', '/', ':', '<', + '>', '?', '\' and '|'. Further, names cannot end with a '.' + or a space. */ + FILE_NAME_DOS = 0x02, + /* The standard DOS filenames (8.3 format). Uppercase only. + All 8-bit characters greater space, except: '"', '*', '+', + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + FILE_NAME_WIN32_AND_DOS = 0x03, + /* 3 means that both the Win32 and the DOS filenames are + identical and hence have been saved in this single filename + record. */ +} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS; + +/** + * struct FILE_NAME_ATTR - Attribute: Filename (0x30). + * + * NOTE: Always resident. + * NOTE: All fields, except the parent_directory, are only updated when the + * filename is changed. Until then, they just become out of sync with + * reality and the more up to date values are present in the standard + * information attribute. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ MFT_REF parent_directory; /* Directory this filename is + referenced from. */ +/* 8*/ s64 creation_time; /* Time file was created. */ +/* 10*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 18*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 20*/ s64 last_access_time; /* Last time this mft record was + accessed. */ +/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space + for the data attribute. So for + normal $DATA, this is the + allocated_size from the unnamed + $DATA attribute and for compressed + and/or sparse $DATA, this is the + compressed_size from the unnamed + $DATA attribute. NOTE: This is a + multiple of the cluster size. */ +/* 30*/ s64 data_size; /* Byte size of actual data in data + attribute. */ +/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 3c*/ union { + /* 3c*/ struct { + /* 3c*/ u16 packed_ea_size; /* Size of the buffer needed to + pack the extended attributes + (EAs), if such are present.*/ + /* 3e*/ u16 reserved; /* Reserved for alignment. */ + } __attribute__((__packed__)); + /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, + present only in reparse + points and only if there are + no EAs. */ + } __attribute__((__packed__)); +/* 40*/ u8 file_name_length; /* Length of file name in + (Unicode) characters. */ +/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ +/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ +} __attribute__((__packed__)) FILE_NAME_ATTR; + +/** + * struct GUID - GUID structures store globally unique identifiers (GUID). + * + * A GUID is a 128-bit value consisting of one group of eight hexadecimal + * digits, followed by three groups of four hexadecimal digits each, followed + * by one group of twelve hexadecimal digits. GUIDs are Microsoft's + * implementation of the distributed computing environment (DCE) universally + * unique identifier (UUID). + * + * Example of a GUID: + * 1F010768-5A73-BC91-0010-A52216A7227B + */ +typedef struct { + u32 data1; /* The first eight hexadecimal digits of the GUID. */ + u16 data2; /* The first group of four hexadecimal digits. */ + u16 data3; /* The second group of four hexadecimal digits. */ + u8 data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} __attribute__((__packed__)) GUID; + +/** + * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O. + * + * This index contains all object_ids present on the volume as the index keys + * and the corresponding mft_record numbers as the index entry data parts. + * + * The data part (defined below) also contains three other object_ids: + * birth_volume_id - object_id of FILE_Volume on which the file was first + * created. Optional (i.e. can be zero). + * birth_object_id - object_id of file when it was first created. Usually + * equals the object_id. Optional (i.e. can be zero). + * domain_id - Reserved (always zero). + */ +typedef struct { + MFT_REF mft_reference; /* Mft record containing the object_id in + the index entry key. */ + union { + struct { + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; + +/** + * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). + * + * NOTE: Always resident. + */ +typedef struct { + GUID object_id; /* Unique id assigned to the + file.*/ + /* The following fields are optional. The attribute value size is 16 + bytes, i.e. sizeof(GUID), if these are not present at all. Note, + the entries can be present but one or more (or all) can be zero + meaning that that particular value(s) is(are) not defined. Note, + when the fields are missing here, it is well possible that they are + to be found within the $Extend/$ObjId system file indexed under the + above object_id. */ + union { + struct { + GUID birth_volume_id; /* Unique id of volume on which + the file was first created.*/ + GUID birth_object_id; /* Unique id of file when it was + first created. */ + GUID domain_id; /* Reserved, zero. */ + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJECT_ID_ATTR; + +#if 0 +/** + * enum IDENTIFIER_AUTHORITIES - + * + * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in + * the SID structure (see below). + */ +typedef enum { /* SID string prefix. */ + SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ + SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ + SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ + SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ + SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ + SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ +} IDENTIFIER_AUTHORITIES; +#endif + +/** + * enum RELATIVE_IDENTIFIERS - + * + * These relative identifiers (RIDs) are used with the above identifier + * authorities to make up universal well-known SIDs. + * + * Note: The relative identifier (RID) refers to the portion of a SID, which + * identifies a user or group in relation to the authority that issued the SID. + * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is + * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and + * the relative identifier SECURITY_CREATOR_OWNER_RID (0). + */ +typedef enum { /* Identifier authority. */ + SECURITY_NULL_RID = 0, /* S-1-0 */ + SECURITY_WORLD_RID = 0, /* S-1-1 */ + SECURITY_LOCAL_RID = 0, /* S-1-2 */ + + SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ + SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ + + SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ + SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ + + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_SERVICE_RID = 6, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID=9, + SECURITY_SERVER_LOGON_RID = 9, + SECURITY_PRINCIPAL_SELF_RID = 0xa, + SECURITY_AUTHENTICATED_USER_RID = 0xb, + SECURITY_RESTRICTED_CODE_RID = 0xc, + SECURITY_TERMINAL_SERVER_RID = 0xd, + + SECURITY_LOGON_IDS_RID = 5, + SECURITY_LOGON_IDS_RID_COUNT = 3, + + SECURITY_LOCAL_SYSTEM_RID = 0x12, + + SECURITY_NT_NON_UNIQUE = 0x15, + + SECURITY_BUILTIN_DOMAIN_RID = 0x20, + + /* + * Well-known domain relative sub-authority values (RIDs). + */ + + /* Users. */ + DOMAIN_USER_RID_ADMIN = 0x1f4, + DOMAIN_USER_RID_GUEST = 0x1f5, + DOMAIN_USER_RID_KRBTGT = 0x1f6, + + /* Groups. */ + DOMAIN_GROUP_RID_ADMINS = 0x200, + DOMAIN_GROUP_RID_USERS = 0x201, + DOMAIN_GROUP_RID_GUESTS = 0x202, + DOMAIN_GROUP_RID_COMPUTERS = 0x203, + DOMAIN_GROUP_RID_CONTROLLERS = 0x204, + DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, + DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, + DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207, + DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, + + /* Aliases. */ + DOMAIN_ALIAS_RID_ADMINS = 0x220, + DOMAIN_ALIAS_RID_USERS = 0x221, + DOMAIN_ALIAS_RID_GUESTS = 0x222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x223, + + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, + + DOMAIN_ALIAS_RID_REPLICATOR = 0x228, + DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, + DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, +} RELATIVE_IDENTIFIERS; + +/* + * The universal well-known SIDs: + * + * NULL_SID S-1-0-0 + * WORLD_SID S-1-1-0 + * LOCAL_SID S-1-2-0 + * CREATOR_OWNER_SID S-1-3-0 + * CREATOR_GROUP_SID S-1-3-1 + * CREATOR_OWNER_SERVER_SID S-1-3-2 + * CREATOR_GROUP_SERVER_SID S-1-3-3 + * + * (Non-unique IDs) S-1-4 + * + * NT well-known SIDs: + * + * NT_AUTHORITY_SID S-1-5 + * DIALUP_SID S-1-5-1 + * + * NETWORD_SID S-1-5-2 + * BATCH_SID S-1-5-3 + * INTERACTIVE_SID S-1-5-4 + * SERVICE_SID S-1-5-6 + * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) + * PROXY_SID S-1-5-8 + * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) + * SELF_SID S-1-5-10 (self RID) + * AUTHENTICATED_USER_SID S-1-5-11 + * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) + * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) + * + * (Logon IDs) S-1-5-5-X-Y + * + * (NT non-unique IDs) S-1-5-0x15-... + * + * (Built-in domain) S-1-5-0x20 + */ + +/** + * union SID_IDENTIFIER_AUTHORITY - A 48-bit value used in the SID structure + * + * NOTE: This is stored as a big endian number. + */ +typedef union { + struct { + u16 high_part; /* High 16-bits. */ + u32 low_part; /* Low 32-bits. */ + } __attribute__((__packed__)); + u8 value[6]; /* Value as individual bytes. */ +} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; + +/** + * struct SID - + * + * The SID structure is a variable-length structure used to uniquely identify + * users or groups. SID stands for security identifier. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * Example SID; the domain-relative SID of the local Administrators group on + * Windows NT/2k: + * S-1-5-32-544 + * This translates to a SID with: + * revision = 1, + * sub_authority_count = 2, + * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY + * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID + * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS + */ +typedef struct { + u8 revision; + u8 sub_authority_count; + SID_IDENTIFIER_AUTHORITY identifier_authority; + u32 sub_authority[1]; /* At least one sub_authority. */ +} __attribute__((__packed__)) SID; + +/** + * enum SID_CONSTANTS - Current constants for SIDs. + */ +typedef enum { + SID_REVISION = 1, /* Current revision level. */ + SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ + SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in + a future revision. */ +} SID_CONSTANTS; + +/** + * enum ACE_TYPES - The predefined ACE types (8-bit, see below). + */ +typedef enum { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ + ACCESS_MAX_MS_V2_ACE_TYPE = 3, + + ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, + + /* The following are Win2k only. */ + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + + ACCESS_MAX_MS_V4_ACE_TYPE = 8, + + /* This one is for WinNT&2k. */ + ACCESS_MAX_MS_ACE_TYPE = 8, +} __attribute__((__packed__)) ACE_TYPES; + +/** + * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance. + * + * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE + * types to indicate that a message is generated (in Windows!) for successful + * accesses. + * + * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types + * to indicate that a message is generated (in Windows!) for failed accesses. + */ +typedef enum { + /* The inheritance flags. */ + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, /* Win2k only. */ + VALID_INHERIT_FLAGS = 0x1f, + + /* The audit flags. */ + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +} __attribute__((__packed__)) ACE_FLAGS; + +/** + * struct ACE_HEADER - + * + * An ACE is an access-control entry in an access-control list (ACL). + * An ACE defines access to an object for a specific user or group or defines + * the types of access that generate system-administration messages or alarms + * for a specific user or group. The user or group is identified by a security + * identifier (SID). + * + * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary), + * which specifies the type and size of the ACE. The format of the subsequent + * data depends on the ACE type. + */ +typedef struct { + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ +} __attribute__((__packed__)) ACE_HEADER; + +/** + * enum ACCESS_MASK - The access mask (32-bit). + * + * Defines the access rights. + */ +typedef enum { + /* + * The specific rights (bits 0 to 15). Depend on the type of the + * object being secured by the ACE. + */ + + /* Specific rights for files and directories are as follows: */ + + /* Right to read data from the file. (FILE) */ + FILE_READ_DATA = const_cpu_to_le32(0x00000001), + /* Right to list contents of a directory. (DIRECTORY) */ + FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001), + + /* Right to write data to the file. (FILE) */ + FILE_WRITE_DATA = const_cpu_to_le32(0x00000002), + /* Right to create a file in the directory. (DIRECTORY) */ + FILE_ADD_FILE = const_cpu_to_le32(0x00000002), + + /* Right to append data to the file. (FILE) */ + FILE_APPEND_DATA = const_cpu_to_le32(0x00000004), + /* Right to create a subdirectory. (DIRECTORY) */ + FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004), + + /* Right to read extended attributes. (FILE/DIRECTORY) */ + FILE_READ_EA = const_cpu_to_le32(0x00000008), + + /* Right to write extended attributes. (FILE/DIRECTORY) */ + FILE_WRITE_EA = const_cpu_to_le32(0x00000010), + + /* Right to execute a file. (FILE) */ + FILE_EXECUTE = const_cpu_to_le32(0x00000020), + /* Right to traverse the directory. (DIRECTORY) */ + FILE_TRAVERSE = const_cpu_to_le32(0x00000020), + + /* + * Right to delete a directory and all the files it contains (its + * children), even if the files are read-only. (DIRECTORY) + */ + FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040), + + /* Right to read file attributes. (FILE/DIRECTORY) */ + FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080), + + /* Right to change file attributes. (FILE/DIRECTORY) */ + FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100), + + /* + * The standard rights (bits 16 to 23). Are independent of the type of + * object being secured. + */ + + /* Right to delete the object. */ + DELETE = const_cpu_to_le32(0x00010000), + + /* + * Right to read the information in the object's security descriptor, + * not including the information in the SACL. I.e. right to read the + * security descriptor and owner. + */ + READ_CONTROL = const_cpu_to_le32(0x00020000), + + /* Right to modify the DACL in the object's security descriptor. */ + WRITE_DAC = const_cpu_to_le32(0x00040000), + + /* Right to change the owner in the object's security descriptor. */ + WRITE_OWNER = const_cpu_to_le32(0x00080000), + + /* + * Right to use the object for synchronization. Enables a process to + * wait until the object is in the signalled state. Some object types + * do not support this access right. + */ + SYNCHRONIZE = const_cpu_to_le32(0x00100000), + + /* + * The following STANDARD_RIGHTS_* are combinations of the above for + * convenience and are defined by the Win32 API. + */ + + /* These are currently defined to READ_CONTROL. */ + STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000), + + /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ + STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000), + + /* + * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and + * SYNCHRONIZE access. + */ + STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000), + + /* + * The access system ACL and maximum allowed access types (bits 24 to + * 25, bits 26 to 27 are reserved). + */ + ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000), + MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000), + + /* + * The generic rights (bits 28 to 31). These map onto the standard and + * specific rights. + */ + + /* Read, write, and execute access. */ + GENERIC_ALL = const_cpu_to_le32(0x10000000), + + /* Execute access. */ + GENERIC_EXECUTE = const_cpu_to_le32(0x20000000), + + /* + * Write access. For files, this maps onto: + * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | + * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_WRITE = const_cpu_to_le32(0x40000000), + + /* + * Read access. For files, this maps onto: + * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | + * STANDARD_RIGHTS_READ | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_READ = const_cpu_to_le32(0x80000000), +} ACCESS_MASK; + +/** + * struct GENERIC_MAPPING - + * + * The generic mapping array. Used to denote the mapping of each generic + * access right to a specific access mask. + * + * FIXME: What exactly is this and what is it for? (AIA) + */ +typedef struct { + ACCESS_MASK generic_read; + ACCESS_MASK generic_write; + ACCESS_MASK generic_execute; + ACCESS_MASK generic_all; +} __attribute__((__packed__)) GENERIC_MAPPING; + +/* + * The predefined ACE type structures are as defined below. + */ + +/** + * struct ACCESS_DENIED_ACE - + * + * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, + SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; + +/** + * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit). + */ +typedef enum { + ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1), + ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2), +} OBJECT_ACE_FLAGS; + +/** + * struct ACCESS_ALLOWED_OBJECT_ACE - + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ +/* 12*/ GUID object_type; +/* 28*/ GUID inherited_object_type; +/* 44*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE, + ACCESS_DENIED_OBJECT_ACE, + SYSTEM_AUDIT_OBJECT_ACE, + SYSTEM_ALARM_OBJECT_ACE; + +/** + * struct ACL - An ACL is an access-control list (ACL). + * + * An ACL starts with an ACL header structure, which specifies the size of + * the ACL and the number of ACEs it contains. The ACL header is followed by + * zero or more access control entries (ACEs). The ACL as well as each ACE + * are aligned on 4-byte boundaries. + */ +typedef struct { + u8 revision; /* Revision of this ACL. */ + u8 alignment1; + u16 size; /* Allocated space in bytes for ACL. Includes this + header, the ACEs and the remaining free space. */ + u16 ace_count; /* Number of ACEs in the ACL. */ + u16 alignment2; +/* sizeof() = 8 bytes */ +} __attribute__((__packed__)) ACL; + +/** + * enum ACL_CONSTANTS - Current constants for ACLs. + */ +typedef enum { + /* Current revision. */ + ACL_REVISION = 2, + ACL_REVISION_DS = 4, + + /* History of revisions. */ + ACL_REVISION1 = 1, + MIN_ACL_REVISION = 2, + ACL_REVISION2 = 2, + ACL_REVISION3 = 3, + ACL_REVISION4 = 4, + MAX_ACL_REVISION = 4, +} ACL_CONSTANTS; + +/** + * enum SECURITY_DESCRIPTOR_CONTROL - + * + * The security descriptor control flags (16-bit). + * + * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the + * SID pointed to by the Owner field was provided by a + * defaulting mechanism rather than explicitly provided by the + * original provider of the security descriptor. This may + * affect the treatment of the SID with respect to inheritance + * of an owner. + * + * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the + * SID in the Group field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of + * the security descriptor. This may affect the treatment of + * the SID with respect to inheritance of a primary group. + * + * SE_DACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a discretionary ACL. If this + * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is + * null, then a null ACL is explicitly being specified. + * + * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Dacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the DaclPresent flag is not set. + * + * SE_SACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a system ACL pointed to by the + * Sacl field. If this flag is set and the Sacl field of the + * SECURITY_DESCRIPTOR is null, then an empty (but present) + * ACL is being specified. + * + * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Sacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the SaclPresent flag is not set. + * + * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the + * security descriptor is in self-relative form. In this form, + * all fields of the security descriptor are contiguous in memory + * and all pointer fields are expressed as offsets from the + * beginning of the security descriptor. + */ +typedef enum { + SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), + SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002), + SE_DACL_PRESENT = const_cpu_to_le16(0x0004), + SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008), + SE_SACL_PRESENT = const_cpu_to_le16(0x0010), + SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020), + SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100), + SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200), + SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400), + SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800), + SE_DACL_PROTECTED = const_cpu_to_le16(0x1000), + SE_SACL_PROTECTED = const_cpu_to_le16(0x2000), + SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000), + SE_SELF_RELATIVE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL; + +/** + * struct SECURITY_DESCRIPTOR_RELATIVE - + * + * Self-relative security descriptor. Contains the owner and group SIDs as well + * as the sacl and dacl ACLs inside the security descriptor itself. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + u32 owner; /* Byte offset to a SID representing an object's + owner. If this is NULL, no owner SID is present in + the descriptor. */ + u32 group; /* Byte offset to a SID representing an object's + primary group. If this is NULL, no primary group + SID is present in the descriptor. */ + u32 sacl; /* Byte offset to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +/* sizeof() = 0x14 bytes */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE; + +/** + * struct SECURITY_DESCRIPTOR - Absolute security descriptor. + * + * Does not contain the owner and group SIDs, nor the sacl and dacl ACLs inside + * the security descriptor. Instead, it contains pointers to these structures + * in memory. Obviously, absolute security descriptors are only useful for in + * memory representations of security descriptors. + * + * On disk, a self-relative security descriptor is used. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + SID *owner; /* Points to a SID representing an object's owner. If + this is NULL, no owner SID is present in the + descriptor. */ + SID *group; /* Points to a SID representing an object's primary + group. If this is NULL, no primary group SID is + present in the descriptor. */ + ACL *sacl; /* Points to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + ACL *dacl; /* Points to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR; + +/** + * enum SECURITY_DESCRIPTOR_CONSTANTS - + * + * Current constants for security descriptors. + */ +typedef enum { + /* Current revision. */ + SECURITY_DESCRIPTOR_REVISION = 1, + SECURITY_DESCRIPTOR_REVISION1 = 1, + + /* The sizes of both the absolute and relative security descriptors is + the same as pointers, at least on ia32 architecture are 32-bit. */ + SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR), +} SECURITY_DESCRIPTOR_CONSTANTS; + +/* + * Attribute: Security descriptor (0x50). + * + * A standard self-relative security descriptor. + * + * NOTE: Can be resident or non-resident. + * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally + * in FILE_Secure and the correct descriptor is found using the security_id + * from the standard information attribute. + */ +typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; + +/* + * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one + * referenced instance of each unique security descriptor is stored. + * + * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It + * does, however, contain two indexes ($SDH and $SII) as well as a named data + * stream ($SDS). + * + * Every unique security descriptor is assigned a unique security identifier + * (security_id, not to be confused with a SID). The security_id is unique for + * the NTFS volume and is used as an index into the $SII index, which maps + * security_ids to the security descriptor's storage location within the $SDS + * data attribute. The $SII index is sorted by ascending security_id. + * + * A simple hash is computed from each security descriptor. This hash is used + * as an index into the $SDH index, which maps security descriptor hashes to + * the security descriptor's storage location within the $SDS data attribute. + * The $SDH index is sorted by security descriptor hash and is stored in a B+ + * tree. When searching $SDH (with the intent of determining whether or not a + * new security descriptor is already present in the $SDS data stream), if a + * matching hash is found, but the security descriptors do not match, the + * search in the $SDH index is continued, searching for a next matching hash. + * + * When a precise match is found, the security_id corresponding to the security + * descriptor in the $SDS attribute is read from the found $SDH index entry and + * is stored in the $STANDARD_INFORMATION attribute of the file/directory to + * which the security descriptor is being applied. The $STANDARD_INFORMATION + * attribute is present in all base mft records (i.e. in all files and + * directories). + * + * If a match is not found, the security descriptor is assigned a new unique + * security_id and is added to the $SDS data attribute. Then, entries + * referencing the this security descriptor in the $SDS data attribute are + * added to the $SDH and $SII indexes. + * + * Note: Entries are never deleted from FILE_Secure, even if nothing + * references an entry any more. + */ + +/** + * struct SECURITY_DESCRIPTOR_HEADER - + * + * This header precedes each security descriptor in the $SDS data stream. + * This is also the index entry data part of both the $SII and $SDH indexes. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; + +/** + * struct SDH_INDEX_DATA - + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ + u32 reserved_II; /* Padding - always unicode "II" or zero. This field + isn't counted in INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) SDH_INDEX_DATA; + +/** + * struct SII_INDEX_DATA - + */ +typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; + +/** + * struct SDS_ENTRY - + * + * The $SDS data stream contains the security descriptors, aligned on 16-byte + * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot + * cross 256kib boundaries (this restriction is imposed by the Windows cache + * manager). Each security descriptor is contained in a SDS_ENTRY structure. + * Also, each security descriptor is stored twice in the $SDS stream with a + * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size) + * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the + * the first copy of the security descriptor will be at offset 0x51d0 in the + * $SDS data stream and the second copy will be at offset 0x451d0. + */ +typedef struct { +/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like + unnamed structs. */ + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security + descriptor. */ +} __attribute__((__packed__)) SDS_ENTRY; + +/** + * struct SII_INDEX_KEY - The index entry key used in the $SII index. + * + * The collation type is COLLATION_NTOFS_ULONG. + */ +typedef struct { + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SII_INDEX_KEY; + +/** + * struct SDH_INDEX_KEY - The index entry key used in the $SDH index. + * + * The keys are sorted first by hash and then by security_id. + * The collation rule is COLLATION_NTOFS_SECURITY_HASH. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SDH_INDEX_KEY; + +/** + * struct VOLUME_NAME - Attribute: Volume name (0x60). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + */ +typedef struct { + ntfschar name[0]; /* The name of the volume in Unicode. */ +} __attribute__((__packed__)) VOLUME_NAME; + +/** + * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). + */ +typedef enum { + VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001), + VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002), + VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004), + VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008), + VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010), + VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020), + VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000), + VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000), + VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f), +} __attribute__((__packed__)) VOLUME_FLAGS; + +/** + * struct VOLUME_INFORMATION - Attribute: Volume information (0x70). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses + * NTFS 1.2. I haven't personally seen other values yet. + */ +typedef struct { + u64 reserved; /* Not used (yet?). */ + u8 major_ver; /* Major version of the ntfs format. */ + u8 minor_ver; /* Minor version of the ntfs format. */ + VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ +} __attribute__((__packed__)) VOLUME_INFORMATION; + +/** + * struct DATA_ATTR - Attribute: Data attribute (0x80). + * + * NOTE: Can be resident or non-resident. + * + * Data contents of a file (i.e. the unnamed stream) or of a named stream. + */ +typedef struct { + u8 data[0]; /* The file's data contents. */ +} __attribute__((__packed__)) DATA_ATTR; + +/** + * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). + */ +typedef enum { + /* When index header is in an index root attribute: */ + SMALL_INDEX = 0, /* The index is small enough to fit inside the + index root attribute and there is no index + allocation attribute present. */ + LARGE_INDEX = 1, /* The index is too large to fit in the index + root attribute and/or an index allocation + attribute is present. */ + /* + * When index header is in an index block, i.e. is part of index + * allocation attribute: + */ + LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more + nodes branching off it. */ + INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a + leaf node. */ + NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ +} __attribute__((__packed__)) INDEX_HEADER_FLAGS; + +/** + * struct INDEX_HEADER - + * + * This is the header for indexes, describing the INDEX_ENTRY records, which + * follow the INDEX_HEADER. Together the index header and the index entries + * make up a complete index. + * + * IMPORTANT NOTE: The offset, length and size structure members are counted + * relative to the start of the index header structure and not relative to the + * start of the index root or index allocation structures themselves. + */ +typedef struct { +/* 0*/ u32 entries_offset; /* Byte offset from the INDEX_HEADER to first + INDEX_ENTRY, aligned to 8-byte boundary. */ +/* 4*/ u32 index_length; /* Data size in byte of the INDEX_ENTRY's, + including the INDEX_HEADER, aligned to 8. */ +/* 8*/ u32 allocated_size; /* Allocated byte size of this index (block), + multiple of 8 bytes. See more below. */ + /* + For the index root attribute, the above two numbers are always + equal, as the attribute is resident and it is resized as needed. + + For the index allocation attribute, the attribute is not resident + and the allocated_size is equal to the index_block_size specified + by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK + size not counting the INDEX_HEADER part (i.e. minus -24). + */ +/* 12*/ INDEX_HEADER_FLAGS ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary.*/ +/* sizeof() == 16 */ +} __attribute__((__packed__)) INDEX_HEADER; + +/** + * struct INDEX_ROOT - Attribute: Index root (0x90). + * + * NOTE: Always resident. + * + * This is followed by a sequence of index entries (INDEX_ENTRY structures) + * as described by the index header. + * + * When a directory is small enough to fit inside the index root then this + * is the only attribute describing the directory. When the directory is too + * large to fit in the index root, on the other hand, two additional attributes + * are present: an index allocation attribute, containing sub-nodes of the B+ + * directory tree (see below), and a bitmap attribute, describing which virtual + * cluster numbers (vcns) in the index allocation attribute are in use by an + * index block. + * + * NOTE: The root directory (FILE_root) contains an entry for itself. Other + * directories do not contain entries for themselves, though. + */ +typedef struct { +/* 0*/ ATTR_TYPES type; /* Type of the indexed attribute. Is + $FILE_NAME for directories, zero + for view indexes. No other values + allowed. */ +/* 4*/ COLLATION_RULES collation_rule; /* Collation rule used to sort the + index entries. If type is $FILE_NAME, + this must be COLLATION_FILE_NAME. */ +/* 8*/ u32 index_block_size; /* Size of index block in bytes (in + the index allocation attribute). */ +/* 12*/ s8 clusters_per_index_block; /* Size of index block in clusters (in + the index allocation attribute), when + an index block is >= than a cluster, + otherwise sectors per index block. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ +/* 16*/ INDEX_HEADER index; /* Index header describing the + following index entries. */ +/* sizeof()= 32 bytes */ +} __attribute__((__packed__)) INDEX_ROOT; + +/** + * struct INDEX_BLOCK - Attribute: Index allocation (0xa0). + * + * NOTE: Always non-resident (doesn't make sense to be resident anyway!). + * + * This is an array of index blocks. Each index block starts with an + * INDEX_BLOCK structure containing an index header, followed by a sequence of + * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER. + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ + u16 usa_ofs; /* See NTFS_RECORD definition. */ + u16 usa_count; /* See NTFS_RECORD definition. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number of the last + modification of this index block. */ +/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ +/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ +/* sizeof()= 40 (0x28) bytes */ +/* + * When creating the index block, we place the update sequence array at this + * offset, i.e. before we start with the index entries. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading use the data from the ntfs record header. + */ +} __attribute__((__packed__)) INDEX_BLOCK; + +typedef INDEX_BLOCK INDEX_ALLOCATION; + +/** + * struct REPARSE_INDEX_KEY - + * + * The system file FILE_Extend/$Reparse contains an index named $R listing + * all reparse points on the volume. The index entry keys are as defined + * below. Note, that there is no index data associated with the index entries. + * + * The index entries are sorted by the index key file_id. The collation rule is + * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the + * primary key / is not a key at all. (AIA) + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + MFT_REF file_id; /* Mft record of the file containing the + reparse point attribute. */ +} __attribute__((__packed__)) REPARSE_INDEX_KEY; + +/** + * enum QUOTA_FLAGS - Quota flags (32-bit). + */ +typedef enum { + /* The user quota flags. Names explain meaning. */ + QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001), + QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002), + QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004), + + QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007), + /* Bit mask for user quota flags. */ + + /* These flags are only present in the quota defaults index entry, + i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ + QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010), + QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020), + QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040), + QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080), + QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100), + QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200), + QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400), + QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800), +} QUOTA_FLAGS; + +/** + * struct QUOTA_CONTROL_ENTRY - + * + * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas + * are on a per volume and per user basis. + * + * The $Q index contains one entry for each existing user_id on the volume. The + * index key is the user_id of the user/group owning this quota control entry, + * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the + * owner_id, is found in the standard information attribute. The collation rule + * for $Q is COLLATION_NTOFS_ULONG. + * + * The $O index contains one entry for each user/group who has been assigned + * a quota on that volume. The index key holds the SID of the user_id the + * entry belongs to, i.e. the owner_id. The collation rule for $O is + * COLLATION_NTOFS_SID. + * + * The $O index entry data is the user_id of the user corresponding to the SID. + * This user_id is used as an index into $Q to find the quota control entry + * associated with the SID. + * + * The $Q index entry data is the quota control entry and is defined below. + */ +typedef struct { + u32 version; /* Currently equals 2. */ + QUOTA_FLAGS flags; /* Flags describing this quota entry. */ + u64 bytes_used; /* How many bytes of the quota are in use. */ + s64 change_time; /* Last time this quota entry was changed. */ + s64 threshold; /* Soft quota (-1 if not limited). */ + s64 limit; /* Hard quota (-1 if not limited). */ + s64 exceeded_time; /* How long the soft quota has been exceeded. */ +/* The below field is NOT present for the quota defaults entry. */ + SID sid; /* The SID of the user/object associated with + this quota entry. If this field is missing + then the INDEX_ENTRY is padded to a multiple + of 8 with zeros which are not counted in + the data_length field. If the sid is present + then this structure is padded with zeros to + a multiple of 8 and the padding is counted in + the INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; + +/** + * struct QUOTA_O_INDEX_DATA - + */ +typedef struct { + u32 owner_id; + u32 unknown; /* Always 32. Seems to be padding and it's not + counted in the INDEX_ENTRY's data_length. + This field shouldn't be really here. */ +} __attribute__((__packed__)) QUOTA_O_INDEX_DATA; + +/** + * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit). + */ +typedef enum { + QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000), + QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001), + QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100), +} PREDEFINED_OWNER_IDS; + +/** + * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit). + */ +typedef enum { + INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a + sub-node, i.e. a reference to an index + block in form of a virtual cluster + number (see below). */ + INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last + entry in an index block. The index + entry does not represent a file but it + can point to a sub-node. */ + INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ +} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; + +/** + * struct INDEX_ENTRY_HEADER - This the index entry header (see below). + * + * ========================================================== + * !!!!! SEE DESCRIPTION OF THE FIELDS AT INDEX_ENTRY !!!!! + * ========================================================== + */ +typedef struct { +/* 0*/ union { + MFT_REF indexed_file; + struct { + u16 data_offset; + u16 data_length; + u32 reservedV; + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; +/* 10*/ u16 key_length; +/* 12*/ INDEX_ENTRY_FLAGS flags; +/* 14*/ u16 reserved; +/* sizeof() = 16 bytes */ +} __attribute__((__packed__)) INDEX_ENTRY_HEADER; + +/** + * struct INDEX_ENTRY - This is an index entry. + * + * A sequence of such entries follows each INDEX_HEADER structure. Together + * they make up a complete index. The index follows either an index root + * attribute or an index allocation attribute. + * + * NOTE: Before NTFS 3.0 only filename attributes were indexed. + */ +typedef struct { +/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ + union { /* Only valid when INDEX_ENTRY_END is not set. */ + MFT_REF indexed_file; /* The mft reference of the file + described by this index + entry. Used for directory + indexes. */ + struct { /* Used for views/indexes to find the entry's data. */ + u16 data_offset; /* Data byte offset from this + INDEX_ENTRY. Follows the + index key. */ + u16 data_length; /* Data length in bytes. */ + u32 reservedV; /* Reserved (zero). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; /* Byte size of this index entry, multiple of + 8-bytes. Size includes INDEX_ENTRY_HEADER + and the optional subnode VCN. See below. */ +/* 10*/ u16 key_length; /* Byte size of the key value, which is in the + index entry. It follows field reserved. Not + multiple of 8-bytes. */ +/* 12*/ INDEX_ENTRY_FLAGS ie_flags; /* Bit field of INDEX_ENTRY_* flags. */ +/* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ +/* End of INDEX_ENTRY_HEADER */ +/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present + if INDEX_ENTRY_END bit in flags is not set. NOTE: On + NTFS versions before 3.0 the only valid key is the + FILE_NAME_ATTR. On NTFS 3.0+ the following + additional index keys are defined: */ + FILE_NAME_ATTR file_name;/* $I30 index in directories. */ + SII_INDEX_KEY sii; /* $SII index in $Secure. */ + SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ + GUID object_id; /* $O index in FILE_Extend/$ObjId: The + object_id of the mft record found in + the data part of the index. */ + REPARSE_INDEX_KEY reparse; /* $R index in + FILE_Extend/$Reparse. */ + SID sid; /* $O index in FILE_Extend/$Quota: + SID of the owner of the user_id. */ + u32 owner_id; /* $Q index in FILE_Extend/$Quota: + user_id of the owner of the quota + control entry in the data part of + the index. */ + } __attribute__((__packed__)) key; + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ +} __attribute__((__packed__)) INDEX_ENTRY; + +/** + * struct BITMAP_ATTR - Attribute: Bitmap (0xb0). + * + * Contains an array of bits (aka a bitfield). + * + * When used in conjunction with the index allocation attribute, each bit + * corresponds to one index block within the index allocation attribute. Thus + * the number of bits in the bitmap * index block size / cluster size is the + * number of clusters in the index allocation attribute. + */ +typedef struct { + u8 bitmap[0]; /* Array of bits. */ +} __attribute__((__packed__)) BITMAP_ATTR; + +/** + * enum PREDEFINED_REPARSE_TAGS - + * + * The reparse point tag defines the type of the reparse point. It also + * includes several flags, which further describe the reparse point. + * + * The reparse point tag is an unsigned 32-bit value divided in three parts: + * + * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of + * the reparse point. + * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. + * 3. The most significant three bits are flags describing the reparse point. + * They are defined as follows: + * bit 29: Name surrogate bit. If set, the filename is an alias for + * another object in the system. + * bit 30: High-latency bit. If set, accessing the first byte of data will + * be slow. (E.g. the data is stored on a tape drive.) + * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User + * defined tags have to use zero here. + */ +typedef enum { + IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), + IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), + IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), + + IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000), + IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), + IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), + + IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005), + IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006), + IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007), + IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008), + + IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0x88000003), + + IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004), + + IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000), + + IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff), +} PREDEFINED_REPARSE_TAGS; + +/** + * struct REPARSE_POINT - Attribute: Reparse point (0xc0). + * + * NOTE: Can be resident or non-resident. + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + u16 reparse_data_length; /* Byte size of reparse data. */ + u16 reserved; /* Align to 8-byte boundary. */ + u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ +} __attribute__((__packed__)) REPARSE_POINT; + +/** + * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). + * + * NOTE: Always resident. + */ +typedef struct { + u16 ea_length; /* Byte size of the packed extended + attributes. */ + u16 need_ea_count; /* The number of extended attributes which have + the NEED_EA bit set. */ + u32 ea_query_length; /* Byte size of the buffer required to query + the extended attributes when calling + ZwQueryEaFile() in Windows NT/2k. I.e. the + byte size of the unpacked extended + attributes. */ +} __attribute__((__packed__)) EA_INFORMATION; + +/** + * enum EA_FLAGS - Extended attribute flags (8-bit). + */ +typedef enum { + NEED_EA = 0x80, /* Indicate that the file to which the EA + belongs cannot be interpreted without + understanding the associated extended + attributes. */ +} __attribute__((__packed__)) EA_FLAGS; + +/** + * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0). + * + * Like the attribute list and the index buffer list, the EA attribute value is + * a sequence of EA_ATTR variable length records. + * + * FIXME: It appears weird that the EA name is not Unicode. Is it true? + * FIXME: It seems that name is always uppercased. Is it true? + */ +typedef struct { + u32 next_entry_offset; /* Offset to the next EA_ATTR. */ + EA_FLAGS flags; /* Flags describing the EA. */ + u8 name_length; /* Length of the name of the extended + attribute in bytes. */ + u16 value_length; /* Byte size of the EA's value. */ + u8 name[0]; /* Name of the EA. */ + u8 value[0]; /* The value of the EA. Immediately + follows the name. */ +} __attribute__((__packed__)) EA_ATTR; + +/** + * struct PROPERTY_SET - Attribute: Property set (0xf0). + * + * Intended to support Native Structure Storage (NSS) - a feature removed from + * NTFS 3.0 during beta testing. + */ +typedef struct { + /* Irrelevant as feature unused. */ +} __attribute__((__packed__)) PROPERTY_SET; + +/** + * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100). + * + * NOTE: Can be resident or non-resident. + * + * Operations on this attribute are logged to the journal ($LogFile) like + * normal metadata changes. + * + * Used by the Encrypting File System (EFS). All encrypted files have this + * attribute with the name $EFS. See below for the relevant structures. + */ +typedef struct { + /* Can be anything the creator chooses. */ +} __attribute__((__packed__)) LOGGED_UTILITY_STREAM; + +/* + * $EFS Data Structure: + * + * The following information is about the data structures that are contained + * inside a logged utility stream (0x100) with a name of "$EFS". + * + * The stream starts with an instance of EFS_ATTR_HEADER. + * + * Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of + * them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence + * of multiple data decryption/recovery fields. + * + * Each data decryption/recovery field starts with a EFS_DF_HEADER and the next + * one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to + * the offset of the beginning of the current EFS_DF_HEADER. + * + * The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a + * SID, an optional GUID, an optional container name, a non-optional user name, + * and the encrypted FEK. + * + * Note all the below are best guesses so may have mistakes/inaccuracies. + * Corrections/clarifications/additions are always welcome! + * + * Ntfs.sys takes an EFS value length of <= 0x54 or > 0x40000 to BSOD, i.e. it + * is invalid. + */ + +/** + * struct EFS_ATTR_HEADER - "$EFS" header. + * + * The header of the Logged utility stream (0x100) attribute named "$EFS". + */ +typedef struct { +/* 0*/ u32 length; /* Length of EFS attribute in bytes. */ + u32 state; /* Always 0? */ + u32 version; /* Efs version. Always 2? */ + u32 crypto_api_version; /* Always 0? */ +/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is + created with a call to UuidCreate() so is + unlikely to be an MD5 hash and is more + likely to be GUID of this encrytped file + or something like that. */ +/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ +/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ +/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data + decryption fields (DDF), see below. Zero if + no DDFs are present. */ + u32 offset_to_drf_array;/* Offset in bytes to the array of data + recovery fields (DRF), see below. Zero if + no DRFs are present. */ + u32 reserved; /* Reserved. */ +} __attribute__((__packed__)) EFS_ATTR_HEADER; + +/** + * struct EFS_DF_ARRAY_HEADER - + */ +typedef struct { + u32 df_count; /* Number of data decryption/recovery fields in + the array. */ +} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; + +/** + * struct EFS_DF_HEADER - + */ +typedef struct { +/* 0*/ u32 df_length; /* Length of this data decryption/recovery + field in bytes. */ + u32 cred_header_offset; /* Offset in bytes to the credential header. */ + u32 fek_size; /* Size in bytes of the encrypted file + encryption key (FEK). */ + u32 fek_offset; /* Offset in bytes to the FEK from the start of + the data decryption/recovery field. */ +/* 16*/ u32 unknown1; /* always 0? Might be just padding. */ +} __attribute__((__packed__)) EFS_DF_HEADER; + +/** + * struct EFS_DF_CREDENTIAL_HEADER - + */ +typedef struct { +/* 0*/ u32 cred_length; /* Length of this credential in bytes. */ + u32 sid_offset; /* Offset in bytes to the user's sid from start + of this structure. Zero if no sid is + present. */ +/* 8*/ u32 type; /* Type of this credential: + 1 = CryptoAPI container. + 2 = Unexpected type. + 3 = Certificate thumbprint. + other = Unknown type. */ + union { + /* CryptoAPI container. */ + struct { +/* 12*/ u32 container_name_offset; /* Offset in bytes to + the name of the container from start of this + structure (may not be zero). */ +/* 16*/ u32 provider_name_offset; /* Offset in bytes to + the name of the provider from start of this + structure (may not be zero). */ + u32 public_key_blob_offset; /* Offset in bytes to + the public key blob from start of this + structure. */ +/* 24*/ u32 public_key_blob_size; /* Size in bytes of + public key blob. */ + } __attribute__((__packed__)); + /* Certificate thumbprint. */ + struct { +/* 12*/ u32 cert_thumbprint_header_size; /* Size in + bytes of the header of the certificate + thumbprint. */ +/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in + bytes to the header of the certificate + thumbprint from start of this structure. */ + u32 unknown1; /* Always 0? Might be padding... */ + u32 unknown2; /* Always 0? Might be padding... */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; + +typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; + +/** + * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - + */ +typedef struct { +/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ + u32 thumbprint_size; /* Size of thumbprint in bytes. */ +/* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the + container from start of this + structure or 0 if no name present. */ + u32 provider_name_offset; /* Offset in bytes to the name of the + cryptographic provider from start of + this structure or 0 if no name + present. */ +/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name + from start of this structure or 0 if + no user name present. (This is also + known as lpDisplayInformation.) */ +} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; + +typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; + +typedef enum { + INTX_SYMBOLIC_LINK = + const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */ + INTX_CHARACTER_DEVICE = + const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */ + INTX_BLOCK_DEVICE = + const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */ +} INTX_FILE_TYPES; + +typedef struct { + INTX_FILE_TYPES magic; /* Intx file magic. */ + union { + /* For character and block devices. */ + struct { + u64 major; /* Major device number. */ + u64 minor; /* Minor device number. */ + void *device_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + /* For symbolic links. */ + ntfschar target[0]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) INTX_FILE; + +#endif /* defined _NTFS_LAYOUT_H */ diff --git a/lib/libntfs/src/source/lcnalloc.c b/lib/libntfs/src/source/lcnalloc.c new file mode 100644 index 0000000..e84d243 --- /dev/null +++ b/lib/libntfs/src/source/lcnalloc.c @@ -0,0 +1,771 @@ +/** + * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "runlist.h" +#include "volume.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +/* + * Plenty possibilities for big optimizations all over in the cluster + * allocation, however at the moment the dominant bottleneck (~ 90%) is + * the update of the mapping pairs which converges to the cubic Faulhaber's + * formula as the function of the number of extents (fragments, runs). + */ +#define NTFS_LCNALLOC_BSIZE 4096 +#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE + +enum { + ZONE_MFT = 1, + ZONE_DATA1 = 2, + ZONE_DATA2 = 4 +} ; + +static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) +{ + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); + + if (tc >= end) + *pos = start; + else if (tc >= start) + *pos = tc; +} + +static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) +{ + ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); + + if (zone == ZONE_MFT) + ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, + &vol->mft_zone_pos, tc); + else if (zone == ZONE_DATA1) + ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, + &vol->data1_zone_pos, tc); + else /* zone == ZONE_DATA2 */ + ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, + &vol->data2_zone_pos, tc); +} + +/* + * Unmark full zones when a cluster has been freed in a full zone + * + * Next allocation will reuse the freed cluster + */ + +static void update_full_status(ntfs_volume *vol, LCN lcn) +{ + if (lcn >= vol->mft_zone_end) { + if (vol->full_zones & ZONE_DATA1) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn); + vol->full_zones &= ~ZONE_DATA1; + } + } else + if (lcn < vol->mft_zone_start) { + if (vol->full_zones & ZONE_DATA2) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn); + vol->full_zones &= ~ZONE_DATA2; + } + } else { + if (vol->full_zones & ZONE_MFT) { + ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn); + vol->full_zones &= ~ZONE_MFT; + } + } +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_log_trace("Entering\n"); + + i = 0; + while (i < size) { + switch (*buf) { + case 0 : + do { + buf++; + run += 8; + i++; + } while ((i < size) && !*buf); + break; + case 255 : + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 - run; + } + run = 0; + do { + buf++; + i++; + } while ((i < size) && (*buf == 255)); + break; + default : + for (j = 0; j < 8; j++) { + + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 + (j - run); + } + run = 0; + } else + run++; + } + i++; + buf++; + + } + } + + if (run > max_range) + start_pos = (s64)i * 8 - run; + + return start_pos; +} + +static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, + u8 *writeback) +{ + s64 written; + + ntfs_log_trace("Entering\n"); + + if (!*writeback) + return 0; + + *writeback = 0; + + written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); + if (written != size) { + if (!written) + errno = EIO; + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); + return -1; + } + + return 0; +} + +/** + * ntfs_cluster_alloc - allocate clusters on an ntfs volume + * @vol: mounted ntfs volume on which to allocate the clusters + * @start_vcn: vcn to use for the first allocated cluster + * @count: number of clusters to allocate + * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) + * @zone: zone from which to allocate the clusters + * + * Allocate @count clusters preferably starting at cluster @start_lcn or at the + * current allocator position if @start_lcn is -1, on the mounted ntfs volume + * @vol. @zone is either DATA_ZONE for allocation of normal clusters and + * MFT_ZONE for allocation of clusters for the master file table, i.e. the + * $MFT/$DATA attribute. + * + * On success return a runlist describing the allocated cluster(s). + * + * On error return NULL with errno set to the error code. + * + * Notes on the allocation algorithm + * ================================= + * + * There are two data zones. First is the area between the end of the mft zone + * and the end of the volume, and second is the area between the start of the + * volume and the start of the mft zone. On unmodified/standard NTFS 1.x + * volumes, the second data zone doesn't exist due to the mft zone being + * expanded to cover the start of the volume in order to reserve space for the + * mft bitmap attribute. + * + * The complexity stems from the need of implementing the mft vs data zoned + * approach and from the fact that we have access to the lcn bitmap via up to + * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over + * boundaries of two buffers. Further, the fact that the allocator allows for + * caller supplied hints as to the location of where allocation should begin + * and the fact that the allocator keeps track of where in the data zones the + * next natural allocation should occur, contribute to the complexity of the + * function. But it should all be worthwhile, because this allocator: + * 1) implements MFT zone reservation + * 2) causes reduction in fragmentation. + * The code is not optimized for speed. + */ +runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) +{ + LCN zone_start, zone_end; /* current search range */ + LCN last_read_pos, lcn; + LCN bmp_pos; /* current bit position inside the bitmap */ + LCN prev_lcn = 0, prev_run_len = 0; + s64 clusters, br; + runlist *rl = NULL, *trl; + u8 *buf, *byte, bit, writeback; + u8 pass = 1; /* 1: inside zone; 2: start of zone */ + u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ + u8 done_zones = 0; + u8 has_guess, used_zone_pos; + int err = 0, rlpos, rlsize, buf_size; + + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " + "zone = %s_ZONE.\n", (long long)count, (long long) + start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || + (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { + errno = EINVAL; + ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", + __FUNCTION__, (long long)start_vcn, + (long long)count, (long long)start_lcn); + goto out; + } + + /* Return empty runlist if @count == 0 */ + if (!count) { + rl = ntfs_malloc(0x1000); + if (rl) { + rl[0].vcn = start_vcn; + rl[0].lcn = LCN_RL_NOT_MAPPED; + rl[0].length = 0; + } + goto out; + } + + buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); + if (!buf) + goto out; + /* + * If no @start_lcn was requested, use the current zone + * position otherwise use the requested @start_lcn. + */ + has_guess = 1; + zone_start = start_lcn; + + if (zone_start < 0) { + if (zone == DATA_ZONE) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->mft_zone_pos; + has_guess = 0; + } + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = ZONE_DATA2; + } else if (zone_start < vol->mft_zone_end) { + zone_end = vol->mft_zone_end; + search_zone = ZONE_MFT; + } else { + zone_end = vol->nr_clusters; + search_zone = ZONE_DATA1; + } + + bmp_pos = zone_start; + + /* Loop until all clusters are allocated. */ + clusters = count; + rlpos = rlsize = 0; + while (1) { + /* check whether we have exhausted the current zone */ + if (search_zone & vol->full_zones) + goto zone_pass_done; + last_read_pos = bmp_pos >> 3; + br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, + NTFS_LCNALLOC_BSIZE, buf); + if (br <= 0) { + if (!br) + goto zone_pass_done; + err = errno; + ntfs_log_perror("Reading $BITMAP failed"); + goto err_ret; + } + /* + * We might have read less than NTFS_LCNALLOC_BSIZE bytes + * if we are close to the end of the attribute. + */ + buf_size = (int)br << 3; + lcn = bmp_pos & 7; + bmp_pos &= ~7; + writeback = 0; + + while (lcn < buf_size) { + byte = buf + (lcn >> 3); + bit = 1 << (lcn & 7); + if (has_guess) { + if (*byte & bit) { + has_guess = 0; + break; + } + } else { + lcn = max_empty_bit_range(buf, br); + if (lcn < 0) + break; + has_guess = 1; + continue; + } + + /* First free bit is at lcn + bmp_pos. */ + + /* Reallocate memory if necessary. */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; + trl = realloc(rl, rlsize); + if (!trl) { + err = ENOMEM; + ntfs_log_perror("realloc() failed"); + goto wb_err_ret; + } + rl = trl; + } + + /* Allocate the bitmap bit. */ + *byte |= bit; + writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", + (long long)vol->free_clusters); + else + vol->free_clusters--; + + /* + * Coalesce with previous run if adjacent LCNs. + * Otherwise, append a new run. + */ + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); + rl[rlpos - 1].length = ++prev_run_len; + } else { + if (rlpos) + rl[rlpos].vcn = rl[rlpos - 1].vcn + + prev_run_len; + else { + rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + (long long)start_vcn); + } + + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; + rl[rlpos].length = prev_run_len = 1; + rlpos++; + } + + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); + /* Done? */ + if (!--clusters) { + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, lcn + bmp_pos + 1 + + NTFS_LCNALLOC_SKIP); + goto done_ret; + } + + lcn++; + } + + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } + + if (!used_zone_pos) { + + used_zone_pos = 1; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_pos; + else if (search_zone == ZONE_DATA1) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else + bmp_pos += buf_size; + + if (bmp_pos < zone_end) + continue; + +zone_pass_done: + ntfs_log_trace("Finished current zone pass(%i).\n", pass); + if (pass == 1) { + pass = 2; + zone_end = zone_start; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_start; + else if (search_zone == ZONE_DATA1) + zone_start = vol->mft_zone_end; + else + zone_start = 0; + + /* Sanity check. */ + if (zone_end < zone_start) + zone_end = zone_start; + + bmp_pos = zone_start; + + continue; + } + /* pass == 2 */ +done_zones_check: + done_zones |= search_zone; + vol->full_zones |= search_zone; + if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) { + ntfs_log_trace("Switching zone.\n"); + pass = 1; + if (rlpos) { + LCN tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; + + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, tc); + } + + switch (search_zone) { + case ZONE_MFT: + ntfs_log_trace("Zone switch: mft -> data1\n"); +switch_to_data1_zone: search_zone = ZONE_DATA1; + zone_start = vol->data1_zone_pos; + zone_end = vol->nr_clusters; + if (zone_start == vol->mft_zone_end) + pass = 2; + break; + case ZONE_DATA1: + ntfs_log_trace("Zone switch: data1 -> data2\n"); + search_zone = ZONE_DATA2; + zone_start = vol->data2_zone_pos; + zone_end = vol->mft_zone_start; + if (!zone_start) + pass = 2; + break; + case ZONE_DATA2: + if (!(done_zones & ZONE_DATA1)) { + ntfs_log_trace("data2 -> data1\n"); + goto switch_to_data1_zone; + } + ntfs_log_trace("Zone switch: data2 -> mft\n"); + search_zone = ZONE_MFT; + zone_start = vol->mft_zone_pos; + zone_end = vol->mft_zone_end; + if (zone_start == vol->mft_zone_start) + pass = 2; + break; + } + + bmp_pos = zone_start; + + if (zone_start == zone_end) { + ntfs_log_trace("Empty zone, skipped.\n"); + goto done_zones_check; + } + + continue; + } + + ntfs_log_trace("All zones are finished, no space on device.\n"); + err = ENOSPC; + goto err_ret; + } +done_ret: + ntfs_log_debug("At done_ret.\n"); + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } +done_err_ret: + free(buf); + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; + +wb_err_ret: + ntfs_log_trace("At wb_err_ret.\n"); + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) + err = errno; +err_ret: + ntfs_log_trace("At err_ret.\n"); + if (rl) { + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + rl = NULL; + } + goto done_err_ret; +} + +/** + * ntfs_cluster_free_from_rl - free clusters from runlist + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist from which deallocate clusters + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + + for (; rl->length; rl++) { + + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)rl->lcn, (long long)rl->length); + + if (rl->lcn >= 0) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); + goto out; + } + nr_freed += rl->length ; + } + } + + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/* + * Basic cluster run free + * Returns 0 if successful + */ + +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); + + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/** + * ntfs_cluster_free - free clusters on an ntfs volume + * @vol: mounted ntfs volume on which to free the clusters + * @na: attribute whose runlist describes the clusters to free + * @start_vcn: vcn in @rl at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters + * + * Free @count clusters starting at the cluster @start_vcn in the runlist + * described by the attribute @na from the mounted ntfs volume @vol. + * + * If @count is -1, all clusters from @start_vcn to the end of the runlist + * are deallocated. + * + * On success return the number of deallocated clusters (not counting sparse + * clusters) and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) +{ + runlist *rl; + s64 delta, to_free, nr_freed = 0; + int ret = -1; + + if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || + (count < 0 && count != -1)) { + ntfs_log_trace("Invalid arguments!\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)count, (long long)start_vcn); + + rl = ntfs_attr_find_vcn(na, start_vcn); + if (!rl) { + if (errno == ENOENT) + ret = 0; + goto leave; + } + + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); + goto leave; + } + + /* Find the starting cluster inside the run that needs freeing. */ + delta = start_vcn - rl->vcn; + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length - delta; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + /* Do the actual freeing of the clusters in this run. */ + update_full_status(vol,rl->lcn + delta); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, + to_free)) + goto leave; + nr_freed = to_free; + } + + /* Go to the next run and adjust the number of clusters left to free. */ + ++rl; + if (count >= 0) + count -= to_free; + + /* + * Loop over the remaining runs, using @count as a capping value, and + * free them. + */ + for (; rl->length && count != 0; ++rl) { + // FIXME: Need to try ntfs_attr_map_runlist() for attribute + // list support! (AIA) + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + // FIXME: Eeek! We need rollback! (AIA) + errno = EIO; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; + } + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + to_free)) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; + } + nr_freed += to_free; + } + + if (count >= 0) + count -= to_free; + } + + if (count != -1 && count != 0) { + // FIXME: Eeek! BUG() + errno = EIO; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; + } + + ret = nr_freed; +out: + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); + return ret; +} diff --git a/lib/libntfs/src/source/lcnalloc.h b/lib/libntfs/src/source/lcnalloc.h new file mode 100644 index 0000000..cbf4c5c --- /dev/null +++ b/lib/libntfs/src/source/lcnalloc.h @@ -0,0 +1,51 @@ +/* + * lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LCNALLOC_H +#define _NTFS_LCNALLOC_H + +#include "types.h" +#include "runlist.h" +#include "volume.h" + +/** + * enum NTFS_CLUSTER_ALLOCATION_ZONES - + */ +typedef enum { + FIRST_ZONE = 0, /* For sanity checking. */ + MFT_ZONE = 0, /* Allocate from $MFT zone. */ + DATA_ZONE = 1, /* Allocate from $DATA zone. */ + LAST_ZONE = 1, /* For sanity checking. */ +} NTFS_CLUSTER_ALLOCATION_ZONES; + +extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); + +extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); + +extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, + s64 count); + +#endif /* defined _NTFS_LCNALLOC_H */ + diff --git a/lib/libntfs/src/source/logfile.c b/lib/libntfs/src/source/logfile.c new file mode 100644 index 0000000..53d8d68 --- /dev/null +++ b/lib/libntfs/src/source/logfile.c @@ -0,0 +1,737 @@ +/** + * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "debug.h" +#include "logfile.h" +#include "volume.h" +#include "mst.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_check_restart_page_header - check the page header for consistency + * @rp: restart page header to check + * @pos: position in logfile at which the restart page header resides + * + * Check the restart page header @rp for consistency and return TRUE if it is + * consistent and FALSE otherwise. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) +{ + u32 logfile_system_page_size, logfile_log_page_size; + u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; + BOOL have_usa = TRUE; + + ntfs_log_trace("Entering.\n"); + /* + * If the system or log page sizes are smaller than the ntfs block size + * or either is not a power of 2 we cannot handle this log file. + */ + logfile_system_page_size = le32_to_cpu(rp->system_page_size); + logfile_log_page_size = le32_to_cpu(rp->log_page_size); + if (logfile_system_page_size < NTFS_BLOCK_SIZE || + logfile_log_page_size < NTFS_BLOCK_SIZE || + logfile_system_page_size & + (logfile_system_page_size - 1) || + logfile_log_page_size & (logfile_log_page_size - 1)) { + ntfs_log_error("$LogFile uses unsupported page size.\n"); + return FALSE; + } + /* + * We must be either at !pos (1st restart page) or at pos = system page + * size (2nd restart page). + */ + if (pos && pos != logfile_system_page_size) { + ntfs_log_error("Found restart area in incorrect " + "position in $LogFile.\n"); + return FALSE; + } + /* We only know how to handle version 1.1. */ + if (sle16_to_cpu(rp->major_ver) != 1 || + sle16_to_cpu(rp->minor_ver) != 1) { + ntfs_log_error("$LogFile version %i.%i is not " + "supported. (This driver supports version " + "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), + (int)sle16_to_cpu(rp->minor_ver)); + return FALSE; + } + /* + * If chkdsk has been run the restart page may not be protected by an + * update sequence array. + */ + if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { + have_usa = FALSE; + goto skip_usa_checks; + } + /* Verify the size of the update sequence array. */ + usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); + if (usa_count != le16_to_cpu(rp->usa_count)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array count.\n"); + return FALSE; + } + /* Verify the position of the update sequence array. */ + usa_ofs = le16_to_cpu(rp->usa_ofs); + usa_end = usa_ofs + usa_count * sizeof(u16); + if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || + usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array offset.\n"); + return FALSE; + } +skip_usa_checks: + /* + * Verify the position of the restart area. It must be: + * - aligned to 8-byte boundary, + * - after the update sequence array, and + * - within the system page size. + */ + ra_ofs = le16_to_cpu(rp->restart_area_offset); + if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : + ra_ofs < sizeof(RESTART_PAGE_HEADER)) || + ra_ofs > logfile_system_page_size) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent restart area offset.\n"); + return FALSE; + } + /* + * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn + * set. + */ + if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { + ntfs_log_error("$LogFile restart page is not modified " + "by chkdsk but a chkdsk LSN is specified.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_restart_area - check the restart area for consistency + * @rp: restart page whose restart area to check + * + * Check the restart area of the restart page @rp for consistency and return + * TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header has already been + * consistency checked. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) +{ + u64 file_size; + RESTART_AREA *ra; + u16 ra_ofs, ra_len, ca_ofs; + u8 fs_bits; + + ntfs_log_trace("Entering.\n"); + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (RESTART_AREA*)((u8*)rp + ra_ofs); + /* + * Everything before ra->file_size must be before the first word + * protected by an update sequence number. This ensures that it is + * safe to access ra->client_array_offset. + */ + if (ra_ofs + offsetof(RESTART_AREA, file_size) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent file offset.\n"); + return FALSE; + } + /* + * Now that we can access ra->client_array_offset, make sure everything + * up to the log client array is before the first word protected by an + * update sequence number. This ensures we can access all of the + * restart area elements safely. Also, the client array offset must be + * aligned to an 8-byte boundary. + */ + ca_ofs = le16_to_cpu(ra->client_array_offset); + if (((ca_ofs + 7) & ~7) != ca_ofs || + ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - + sizeof(u16))) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent client array offset.\n"); + return FALSE; + } + /* + * The restart area must end within the system page size both when + * calculated manually and as specified by ra->restart_area_length. + * Also, the calculated length must not exceed the specified length. + */ + ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * + sizeof(LOG_CLIENT_RECORD); + if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || + (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > + le32_to_cpu(rp->system_page_size) || + ra_len > le16_to_cpu(ra->restart_area_length)) { + ntfs_log_error("$LogFile restart area is out of bounds " + "of the system page size specified by the " + "restart page header and/or the specified " + "restart area length is inconsistent.\n"); + return FALSE; + } + /* + * The ra->client_free_list and ra->client_in_use_list must be either + * LOGFILE_NO_CLIENT or less than ra->log_clients or they are + * overflowing the client array. + */ + if ((ra->client_free_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_free_list) >= + le16_to_cpu(ra->log_clients)) || + (ra->client_in_use_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_in_use_list) >= + le16_to_cpu(ra->log_clients))) { + ntfs_log_error("$LogFile restart area specifies " + "overflowing client free and/or in use lists.\n"); + return FALSE; + } + /* + * Check ra->seq_number_bits against ra->file_size for consistency. + * We cannot just use ffs() because the file size is not a power of 2. + */ + file_size = (u64)sle64_to_cpu(ra->file_size); + fs_bits = 0; + while (file_size) { + file_size >>= 1; + fs_bits++; + } + if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent sequence number bits.\n"); + return FALSE; + } + /* The log record header length must be a multiple of 8. */ + if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != + le16_to_cpu(ra->log_record_header_length)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log record header length.\n"); + return FALSE; + } + /* Ditto for the log page data offset. */ + if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != + le16_to_cpu(ra->log_page_data_offset)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log page data offset.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_log_client_array - check the log client array for consistency + * @rp: restart page whose log client array to check + * + * Check the log client array of the restart page @rp for consistency and + * return TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header and the restart area have + * already been consistency checked. + * + * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this + * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full + * restart page and the page must be multi sector transfer deprotected. + */ +static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + LOG_CLIENT_RECORD *ca, *cr; + u16 nr_clients, idx; + BOOL in_free_list, idx_is_first; + + ntfs_log_trace("Entering.\n"); + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + ca = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + /* + * Check the ra->client_free_list first and then check the + * ra->client_in_use_list. Check each of the log client records in + * each of the lists and check that the array does not overflow the + * ra->log_clients value. Also keep track of the number of records + * visited as there cannot be more than ra->log_clients records and + * that way we detect eventual loops in within a list. + */ + nr_clients = le16_to_cpu(ra->log_clients); + idx = le16_to_cpu(ra->client_free_list); + in_free_list = TRUE; +check_list: + for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, + idx = le16_to_cpu(cr->next_client)) { + if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) + goto err_out; + /* Set @cr to the current log client record. */ + cr = ca + idx; + /* The first log client record must not have a prev_client. */ + if (idx_is_first) { + if (cr->prev_client != LOGFILE_NO_CLIENT) + goto err_out; + idx_is_first = FALSE; + } + } + /* Switch to and check the in use list if we just did the free list. */ + if (in_free_list) { + in_free_list = FALSE; + idx = le16_to_cpu(ra->client_in_use_list); + goto check_list; + } + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + ntfs_log_error("$LogFile log client array is corrupt.\n"); + return FALSE; +} + +/** + * ntfs_check_and_load_restart_page - check the restart page for consistency + * @log_na: opened ntfs attribute for journal $LogFile + * @rp: restart page to check + * @pos: position in @log_na at which the restart page resides + * @wrp: [OUT] copy of the multi sector transfer deprotected restart page + * @lsn: [OUT] set to the current logfile lsn on success + * + * Check the restart page @rp for consistency and return 0 if it is consistent + * and errno otherwise. The restart page may have been modified by chkdsk in + * which case its magic is CHKD instead of RSTR. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + * + * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a + * copy of the complete multi sector transfer deprotected page. On failure, + * *@wrp is undefined. + * + * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current + * logfile lsn according to this restart page. On failure, *@lsn is undefined. + * + * The following error codes are defined: + * EINVAL - The restart page is inconsistent. + * ENOMEM - Not enough memory to load the restart page. + * EIO - Failed to reading from $LogFile. + */ +static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, + RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, + LSN *lsn) +{ + RESTART_AREA *ra; + RESTART_PAGE_HEADER *trp; + int err; + + ntfs_log_trace("Entering.\n"); + /* Check the restart page header for consistency. */ + if (!ntfs_check_restart_page_header(rp, pos)) { + /* Error output already done inside the function. */ + return EINVAL; + } + /* Check the restart area for consistency. */ + if (!ntfs_check_restart_area(rp)) { + /* Error output already done inside the function. */ + return EINVAL; + } + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * Allocate a buffer to store the whole restart page so we can multi + * sector transfer deprotect it. + */ + trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); + if (!trp) + return errno; + /* + * Read the whole of the restart page into the buffer. If it fits + * completely inside @rp, just copy it from there. Otherwise read it + * from disk. + */ + if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) + memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); + else if (ntfs_attr_pread(log_na, pos, + le32_to_cpu(rp->system_page_size), trp) != + le32_to_cpu(rp->system_page_size)) { + err = errno; + ntfs_log_error("Failed to read whole restart page into the " + "buffer.\n"); + if (err != ENOMEM) + err = EIO; + goto err_out; + } + /* + * Perform the multi sector transfer deprotection on the buffer if the + * restart page is protected. + */ + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) + && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, + le32_to_cpu(rp->system_page_size))) { + /* + * A multi sector tranfer error was detected. We only need to + * abort if the restart page contents exceed the multi sector + * transfer fixup of the first sector. + */ + if (le16_to_cpu(rp->restart_area_offset) + + le16_to_cpu(ra->restart_area_length) > + NTFS_BLOCK_SIZE - (int)sizeof(u16)) { + ntfs_log_error("Multi sector transfer error " + "detected in $LogFile restart page.\n"); + err = EINVAL; + goto err_out; + } + } + /* + * If the restart page is modified by chkdsk or there are no active + * logfile clients, the logfile is consistent. Otherwise, need to + * check the log client records for consistency, too. + */ + err = 0; + if (ntfs_is_rstr_record(rp->magic) && + ra->client_in_use_list != LOGFILE_NO_CLIENT) { + if (!ntfs_check_log_client_array(trp)) { + err = EINVAL; + goto err_out; + } + } + if (lsn) { + if (ntfs_is_rstr_record(rp->magic)) + *lsn = sle64_to_cpu(ra->current_lsn); + else /* if (ntfs_is_chkd_record(rp->magic)) */ + *lsn = sle64_to_cpu(rp->chkdsk_lsn); + } + ntfs_log_trace("Done.\n"); + if (wrp) + *wrp = trp; + else { +err_out: + free(trp); + } + return err; +} + +/** + * ntfs_check_logfile - check in the journal if the volume is consistent + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: [OUT] on success this is a copy of the current restart page + * + * Check the $LogFile journal for consistency and return TRUE if it is + * consistent and FALSE if not. On success, the current restart page is + * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. + * + * At present we only check the two restart pages and ignore the log record + * pages. + * + * Note that the MstProtected flag is not set on the $LogFile inode and hence + * when reading pages they are not deprotected. This is because we do not know + * if the $LogFile was created on a system with a different page size to ours + * yet and mst deprotection would fail if our page size is smaller. + */ +BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) +{ + s64 size, pos; + LSN rstr1_lsn, rstr2_lsn; + ntfs_volume *vol = log_na->ni->vol; + u8 *kaddr = NULL; + RESTART_PAGE_HEADER *rstr1_ph = NULL; + RESTART_PAGE_HEADER *rstr2_ph = NULL; + int log_page_size, err; + BOOL logfile_is_empty = TRUE; + u8 log_page_bits; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(vol)) + goto is_empty; + size = log_na->data_size; + /* Make sure the file doesn't exceed the maximum allowed size. */ + if (size > (s64)MaxLogFileSize) + size = MaxLogFileSize; + log_page_size = DefaultLogPageSize; + /* + * Use generic_ffs() instead of ffs() to enable the compiler to + * optimize log_page_size and log_page_bits into constants. + */ + log_page_bits = ffs(log_page_size) - 1; + size &= ~(log_page_size - 1); + + /* + * Ensure the log file is big enough to store at least the two restart + * pages and the minimum number of log record pages. + */ + if (size < log_page_size * 2 || (size - log_page_size * 2) >> + log_page_bits < MinLogRecordPages) { + ntfs_log_error("$LogFile is too small.\n"); + return FALSE; + } + /* Allocate memory for restart page. */ + kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); + if (!kaddr) + return FALSE; + /* + * Read through the file looking for a restart page. Since the restart + * page header is at the beginning of a page we only need to search at + * what could be the beginning of a page (for each page size) rather + * than scanning the whole file byte by byte. If all potential places + * contain empty and uninitialized records, the log file can be assumed + * to be empty. + */ + for (pos = 0; pos < size; pos <<= 1) { + /* + * Read first NTFS_BLOCK_SIZE bytes of potential restart page. + */ + if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != + NTFS_BLOCK_SIZE) { + ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " + "bytes of potential restart page.\n"); + goto err_out; + } + + /* + * A non-empty block means the logfile is not empty while an + * empty block after a non-empty block has been encountered + * means we are done. + */ + if (!ntfs_is_empty_recordp((le32*)kaddr)) + logfile_is_empty = FALSE; + else if (!logfile_is_empty) + break; + /* + * A log record page means there cannot be a restart page after + * this so no need to continue searching. + */ + if (ntfs_is_rcrd_recordp((le32*)kaddr)) + break; + /* If not a (modified by chkdsk) restart page, continue. */ + if (!ntfs_is_rstr_recordp((le32*)kaddr) && + !ntfs_is_chkd_recordp((le32*)kaddr)) { + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * Check the (modified by chkdsk) restart page for consistency + * and get a copy of the complete multi sector transfer + * deprotected restart page. + */ + err = ntfs_check_and_load_restart_page(log_na, + (RESTART_PAGE_HEADER*)kaddr, pos, + !rstr1_ph ? &rstr1_ph : &rstr2_ph, + !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); + if (!err) { + /* + * If we have now found the first (modified by chkdsk) + * restart page, continue looking for the second one. + */ + if (!pos) { + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * We have now found the second (modified by chkdsk) + * restart page, so we can stop looking. + */ + break; + } + /* + * Error output already done inside the function. Note, we do + * not abort if the restart page was invalid as we might still + * find a valid one further in the file. + */ + if (err != EINVAL) + goto err_out; + /* Continue looking. */ + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + } + if (kaddr) { + free(kaddr); + kaddr = NULL; + } + if (logfile_is_empty) { + NVolSetLogFileEmpty(vol); +is_empty: + ntfs_log_trace("Done. ($LogFile is empty.)\n"); + return TRUE; + } + if (!rstr1_ph) { + if (rstr2_ph) + ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); + ntfs_log_error("Did not find any restart pages in " + "$LogFile and it was not empty.\n"); + return FALSE; + } + /* If both restart pages were found, use the more recent one. */ + if (rstr2_ph) { + /* + * If the second restart area is more recent, switch to it. + * Otherwise just throw it away. + */ + if (rstr2_lsn > rstr1_lsn) { + ntfs_log_debug("Using second restart page as it is more " + "recent.\n"); + free(rstr1_ph); + rstr1_ph = rstr2_ph; + /* rstr1_lsn = rstr2_lsn; */ + } else { + ntfs_log_debug("Using first restart page as it is more " + "recent.\n"); + free(rstr2_ph); + } + rstr2_ph = NULL; + } + /* All consistency checks passed. */ + if (rp) + *rp = rstr1_ph; + else + free(rstr1_ph); + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + free(kaddr); + free(rstr1_ph); + free(rstr2_ph); + return FALSE; +} + +/** + * ntfs_is_logfile_clean - check in the journal if the volume is clean + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: copy of the current restart page + * + * Analyze the $LogFile journal and return TRUE if it indicates the volume was + * shutdown cleanly and FALSE if not. + * + * At present we only look at the two restart pages and ignore the log record + * pages. This is a little bit crude in that there will be a very small number + * of cases where we think that a volume is dirty when in fact it is clean. + * This should only affect volumes that have not been shutdown cleanly but did + * not have any pending, non-check-pointed i/o, i.e. they were completely idle + * at least for the five seconds preceding the unclean shutdown. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and in particular if the $LogFile + * is empty this function requires that NVolLogFileEmpty() is true otherwise an + * empty volume will be reported as dirty. + */ +BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(log_na->ni->vol)) { + ntfs_log_trace("$LogFile is empty\n"); + return TRUE; + } + if (!rp) { + ntfs_log_error("Restart page header is NULL\n"); + return FALSE; + } + if (!ntfs_is_rstr_record(rp->magic) && + !ntfs_is_chkd_record(rp->magic)) { + ntfs_log_error("Restart page buffer is invalid\n"); + return FALSE; + } + + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * If the $LogFile has active clients, i.e. it is open, and we do not + * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, + * we assume there was an unclean shutdown. + */ + if (ra->client_in_use_list != LOGFILE_NO_CLIENT && + !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->flags)); + return FALSE; + } + /* $LogFile indicates a clean shutdown. */ + ntfs_log_trace("$LogFile indicates a clean shutdown\n"); + return TRUE; +} + +/** + * ntfs_empty_logfile - empty the contents of the $LogFile journal + * @na: ntfs attribute of journal $LogFile to empty + * + * Empty the contents of the $LogFile journal @na and return 0 on success and + * -1 on error. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() + * has been used to ensure that the $LogFile is clean. + */ +int ntfs_empty_logfile(ntfs_attr *na) +{ + s64 pos, count; + char buf[NTFS_BUF_SIZE]; + + ntfs_log_trace("Entering.\n"); + + if (NVolLogFileEmpty(na->ni->vol)) + return 0; + + if (!NAttrNonResident(na)) { + errno = EIO; + ntfs_log_perror("Resident $LogFile $DATA attribute"); + return -1; + } + + memset(buf, -1, NTFS_BUF_SIZE); + + pos = 0; + while ((count = na->data_size - pos) > 0) { + + if (count > NTFS_BUF_SIZE) + count = NTFS_BUF_SIZE; + + count = ntfs_attr_pwrite(na, pos, count, buf); + if (count <= 0) { + ntfs_log_perror("Failed to reset $LogFile"); + if (count != -1) + errno = EIO; + return -1; + } + pos += count; + if(pos>=64*1024) break; //only clear first 64kb + } + + NVolSetLogFileEmpty(na->ni->vol); + + return 0; +} diff --git a/lib/libntfs/src/source/logfile.h b/lib/libntfs/src/source/logfile.h new file mode 100644 index 0000000..798d562 --- /dev/null +++ b/lib/libntfs/src/source/logfile.h @@ -0,0 +1,394 @@ +/* + * logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LOGFILE_H +#define _NTFS_LOGFILE_H + +#include "types.h" +#include "endians.h" +#include "layout.h" + +/* + * Journal ($LogFile) organization: + * + * Two restart areas present in the first two pages (restart pages, one restart + * area in each page). When the volume is dismounted they should be identical, + * except for the update sequence array which usually has a different update + * sequence number. + * + * These are followed by log records organized in pages headed by a log record + * header going up to log file size. Not all pages contain log records when a + * volume is first formatted, but as the volume ages, all records will be used. + * When the log file fills up, the records at the beginning are purged (by + * modifying the oldest_lsn to a higher value presumably) and writing begins + * at the beginning of the file. Effectively, the log file is viewed as a + * circular entity. + * + * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept + * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We + * probably only want to support 1.1 as this seems to be the current version + * and we don't know how that differs from the older versions. The only + * exception is if the journal is clean as marked by the two restart pages + * then it doesn't matter whether we are on an earlier version. We can just + * reinitialize the logfile and start again with version 1.1. + */ + +/* Some $LogFile related constants. */ +#define MaxLogFileSize 0x100000000ULL +#define DefaultLogPageSize 4096 +#define MinLogRecordPages 48 + +/** + * struct RESTART_PAGE_HEADER - Log file restart page header. + * + * Begins the restart area. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ +/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ +/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ +/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + +/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by + chkdsk. Only used when the magic is changed + to "CHKD". Otherwise this is zero. */ +/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file + was created, has to be >= 512 and a power of + 2. Use this to calculate the required size + of the usa (usa_count) and add it to usa_ofs. + Then verify that the result is less than the + value of the restart_area_offset. */ +/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= + 512 and a power of 2. The default is 4096 + and is used when the system page size is + between 4096 and 8192. Otherwise this is + set to the system page size instead. */ +/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to + the RESTART_AREA. Value has to be aligned + to 8-byte boundary. When creating, set this + to be after the usa. */ +/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major + version is 1. */ +/* 28*/ sle16 major_ver; /* Log file major version. We only support + version 1.1. */ +/* sizeof() = 30 (0x1e) bytes */ +} __attribute__((__packed__)) RESTART_PAGE_HEADER; + +/* + * Constant for the log client indices meaning that there are no client records + * in this particular client array. Also inside the client records themselves, + * this means that there are no client records preceding or following this one. + */ +#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) +#define LOGFILE_NO_CLIENT_CPU 0xffff + +/* + * These are the so far known RESTART_AREA_* flags (16-bit) which contain + * information about the log file in which they are present. + */ +enum { + RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ +} __attribute__((__packed__)); + +typedef le16 RESTART_AREA_FLAGS; + +/** + * struct RESTART_AREA - Log file restart area record. + * + * The offset of this record is found by adding the offset of the + * RESTART_PAGE_HEADER to the restart_area_offset value found in it. + * See notes at restart_area_offset above. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log + when the restart area was last written. + This happens often but what is the interval? + Is it just fixed time or is it every time a + check point is written or something else? + On create set to 0. */ +/* 8*/ le16 log_clients; /* Number of log client records in the array of + log client records which follows this + restart area. Must be 1. */ +/* 10*/ le16 client_free_list; /* The index of the first free log client record + in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + free log client records in the array. + If != LOGFILE_NO_CLIENT, check that + log_clients > client_free_list. On Win2k + and presumably earlier, on a clean volume + this is != LOGFILE_NO_CLIENT, and it should + be 0, i.e. the first (and only) client + record is free and thus the logfile is + closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be LOGFILE_NO_CLIENT. On WinXP + and presumably later, the logfile is always + open, even on clean shutdown so this should + always be LOGFILE_NO_CLIENT. */ +/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client + record in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + in-use log client records in the array. If + != LOGFILE_NO_CLIENT check that log_clients + > client_in_use_list. On Win2k and + presumably earlier, on a clean volume this + is LOGFILE_NO_CLIENT, i.e. there are no + client records in use and thus the logfile + is closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be != LOGFILE_NO_CLIENT, and it + should be 0, i.e. the first (and only) + client record is in use. On WinXP and + presumably later, the logfile is always + open, even on clean shutdown so this should + always be 0. */ +/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k + and presumably earlier this is always 0. On + WinXP and presumably later, if the logfile + was shutdown cleanly, the second bit, + RESTART_VOLUME_IS_CLEAN, is set. This bit + is cleared when the volume is mounted by + WinXP and set when the volume is dismounted, + thus if the logfile is dirty, this bit is + clear. Thus we don't need to check the + Windows version to determine if the logfile + is clean. Instead if the logfile is closed, + we know it must be clean. If it is open and + this bit is set, we also know it must be + clean. If on the other hand the logfile is + open and this bit is clear, we can be almost + certain that the logfile is dirty. */ +/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence + number. This is calculated as 67 - the + number of bits required to store the logfile + size in bytes and this can be used in with + the specified file_size as a consistency + check. */ +/* 20*/ le16 restart_area_length;/* Length of the restart area including the + client array. Following checks required if + version matches. Otherwise, skip them. + restart_area_offset + restart_area_length + has to be <= system_page_size. Also, + restart_area_length has to be >= + client_array_offset + (log_clients * + sizeof(log client record)). */ +/* 22*/ le16 client_array_offset;/* Offset from the start of this record to + the first log client record if versions are + matched. When creating, set this to be + after this restart area structure, aligned + to 8-bytes boundary. If the versions do not + match, this is ignored and the offset is + assumed to be (sizeof(RESTART_AREA) + 7) & + ~7, i.e. rounded up to first 8-byte + boundary. Either way, client_array_offset + has to be aligned to an 8-byte boundary. + Also, restart_area_offset + + client_array_offset has to be <= 510. + Finally, client_array_offset + (log_clients + * sizeof(log client record)) has to be <= + system_page_size. On Win2k and presumably + earlier, this is 0x30, i.e. immediately + following this record. On WinXP and + presumably later, this is 0x40, i.e. there + are 16 extra bytes between this record and + the client array. This probably means that + the RESTART_AREA record is actually bigger + in WinXP and later. */ +/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the + restart_area_offset + the offset of the + file_size are > 510 then corruption has + occurred. This is the very first check when + starting with the restart_area as if it + fails it means that some of the above values + will be corrupted by the multi sector + transfer protection. The file_size has to + be rounded down to be a multiple of the + log_page_size in the RESTART_PAGE_HEADER and + then it has to be at least big enough to + store the two restart pages and 48 (0x30) + log record pages. */ +/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including + the log record header. On create set to + 0. */ +/* 36*/ le16 log_record_header_length;/* Byte size of the log record header. + If the version matches then check that the + value of log_record_header_length is a + multiple of 8, i.e. + (log_record_header_length + 7) & ~7 == + log_record_header_length. When creating set + it to sizeof(LOG_RECORD_HEADER), aligned to + 8 bytes. */ +/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record + page. Must be a multiple of 8. On create + set it to immediately after the update + sequence array of the log record page. */ +/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every + time the logfile is restarted which happens + at mount time when the logfile is opened. + When creating set to a random value. Win2k + sets it to the low 32 bits of the current + system time in NTFS format (see time.h). */ +/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ +/* sizeof() = 48 (0x30) bytes */ +} __attribute__((__packed__)) RESTART_AREA; + +/** + * struct LOG_CLIENT_RECORD - Log client record. + * + * The offset of this record is found by adding the offset of the + * RESTART_AREA to the client_array_offset value found in it. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create + set to 0. */ +/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart + the volume, i.e. the current position within + the log file. At present, if clean this + should = current_lsn in restart area but it + probably also = current_lsn when dirty most + of the time. At create set to 0. */ +/* 16*/ le16 prev_client; /* The offset to the previous log client record + in the array of log client records. + LOGFILE_NO_CLIENT means there is no previous + client record, i.e. this is the first one. + This is always LOGFILE_NO_CLIENT. */ +/* 18*/ le16 next_client; /* The offset to the next log client record in + the array of log client records. + LOGFILE_NO_CLIENT means there are no next + client records, i.e. this is the last one. + This is always LOGFILE_NO_CLIENT. */ +/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set + to zero every time the logfile is restarted + and it is incremented when the logfile is + closed at dismount time. Thus it is 0 when + dirty and 1 when clean. On WinXP and + presumably later, this is always 0. */ +/* 22*/ u8 reserved[6]; /* Reserved/alignment. */ +/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should + always be 8. */ +/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should + always be "NTFS" with the remaining bytes + set to 0. */ +/* sizeof() = 160 (0xa0) bytes */ +} __attribute__((__packed__)) LOG_CLIENT_RECORD; + +/** + * struct RECORD_PAGE_HEADER - Log page record page header. + * + * Each log page begins with this header and is followed by several LOG_RECORD + * structures, starting at offset 0x40 (the size of this structure and the + * following update sequence array and then aligned to 8 byte boundary, but is + * this specified anywhere?). + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ + u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ + u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + + union { + LSN last_lsn; + s64 file_offset; + } __attribute__((__packed__)) copy; + u32 flags; + u16 page_count; + u16 page_position; + union { + struct { + u16 next_record_offset; + u8 reserved[6]; + LSN last_end_lsn; + } __attribute__((__packed__)) packed; + } __attribute__((__packed__)) header; +} __attribute__((__packed__)) RECORD_PAGE_HEADER; + +/** + * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. + * + * (Or is it log record pages?) + */ +typedef enum { + LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, + /* This has nothing to do with the log record. It is only so + gcc knows to make the flags 16-bit. */ +} __attribute__((__packed__)) LOG_RECORD_FLAGS; + +/** + * struct LOG_CLIENT_ID - The log client id structure identifying a log client. + */ +typedef struct { + u16 seq_number; + u16 client_index; +} __attribute__((__packed__)) LOG_CLIENT_ID; + +/** + * struct LOG_RECORD - Log record header. + * + * Each log record seems to have a constant size of 0x70 bytes. + */ +typedef struct { + LSN this_lsn; + LSN client_previous_lsn; + LSN client_undo_next_lsn; + u32 client_data_length; + LOG_CLIENT_ID client_id; + u32 record_type; + u32 transaction_id; + u16 flags; + u16 reserved_or_alignment[3]; +/* Now are at ofs 0x30 into struct. */ + u16 redo_operation; + u16 undo_operation; + u16 redo_offset; + u16 redo_length; + u16 undo_offset; + u16 undo_length; + u16 target_attribute; + u16 lcns_to_follow; /* Number of lcn_list entries + following this entry. */ +/* Now at ofs 0x40. */ + u16 record_offset; + u16 attribute_offset; + u32 alignment_or_reserved; + VCN target_vcn; +/* Now at ofs 0x50. */ + struct { /* Only present if lcns_to_follow + is not 0. */ + LCN lcn; + } __attribute__((__packed__)) lcn_list[0]; +} __attribute__((__packed__)) LOG_RECORD; + +extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); +extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); +extern int ntfs_empty_logfile(ntfs_attr *na); + +#endif /* defined _NTFS_LOGFILE_H */ diff --git a/lib/libntfs/src/source/logging.c b/lib/libntfs/src/source/logging.c new file mode 100644 index 0000000..ccccbb8 --- /dev/null +++ b/lib/libntfs/src/source/logging.c @@ -0,0 +1,637 @@ +/** + * logging.c - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include "logging.h" +#include "misc.h" + +#ifndef PATH_SEP +#define PATH_SEP '/' +#endif + +#ifdef DEBUG +static int tab; +#endif + +/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ +#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) +# define BROKEN_GCC_FORMAT_ATTRIBUTE +#else +# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) +#endif + +/** + * struct ntfs_logging - Control info for the logging system + * @levels: Bitfield of logging levels + * @flags: Flags which affect the output style + * @handler: Function to perform the actual logging + */ +struct ntfs_logging { + u32 levels; + u32 flags; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; +}; + +/** + * ntfs_log + * This struct controls all the logging within the library and tools. + */ +static struct ntfs_logging ntfs_log = { +#ifdef DEBUG + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | +#endif + NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | + NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | + NTFS_LOG_LEVEL_PROGRESS, + NTFS_LOG_FLAG_ONLYNAME, +#ifdef DEBUG + ntfs_log_handler_outerr +#else + ntfs_log_handler_null +#endif +}; + + +/** + * ntfs_log_get_levels - Get a list of the current logging levels + * + * Find out which logging levels are enabled. + * + * Returns: Log levels in a 32-bit field + */ +u32 ntfs_log_get_levels(void) +{ + return ntfs_log.levels; +} + +/** + * ntfs_log_set_levels - Enable extra logging levels + * @levels: 32-bit field of log levels to set + * + * Enable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_set_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels |= levels; + return old; +} + +/** + * ntfs_log_clear_levels - Disable some logging levels + * @levels: 32-bit field of log levels to clear + * + * Disable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_clear_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels &= (~levels); + return old; +} + + +/** + * ntfs_log_get_flags - Get a list of logging style flags + * + * Find out which logging flags are enabled. + * + * Returns: Logging flags in a 32-bit field + */ +u32 ntfs_log_get_flags(void) +{ + return ntfs_log.flags; +} + +/** + * ntfs_log_set_flags - Enable extra logging style flags + * @flags: 32-bit field of logging flags to set + * + * Enable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_set_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags |= flags; + return old; +} + +/** + * ntfs_log_clear_flags - Disable some logging styles + * @flags: 32-bit field of logging flags to clear + * + * Disable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_clear_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags &= (~flags); + return old; +} + + +/** + * ntfs_log_get_stream - Default output streams for logging levels + * @level: Log level + * + * By default, urgent messages are sent to "stderr". + * Other messages are sent to "stdout". + * + * Returns: "string" Prefix to be used + */ +static FILE * ntfs_log_get_stream(u32 level) +{ + FILE *stream; + + switch (level) { + case NTFS_LOG_LEVEL_INFO: + case NTFS_LOG_LEVEL_QUIET: + case NTFS_LOG_LEVEL_PROGRESS: + case NTFS_LOG_LEVEL_VERBOSE: + stream = stdout; + break; + + case NTFS_LOG_LEVEL_DEBUG: + case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: + case NTFS_LOG_LEVEL_WARNING: + case NTFS_LOG_LEVEL_ERROR: + case NTFS_LOG_LEVEL_CRITICAL: + case NTFS_LOG_LEVEL_PERROR: + default: + stream = stderr; + break; + } + + return stream; +} + +/** + * ntfs_log_get_prefix - Default prefixes for logging levels + * @level: Log level to be prefixed + * + * Prefixing the logging output can make it easier to parse. + * + * Returns: "string" Prefix to be used + */ +static const char * ntfs_log_get_prefix(u32 level) +{ + const char *prefix; + + switch (level) { + case NTFS_LOG_LEVEL_DEBUG: + prefix = "DEBUG: "; + break; + case NTFS_LOG_LEVEL_TRACE: + prefix = "TRACE: "; + break; + case NTFS_LOG_LEVEL_QUIET: + prefix = "QUIET: "; + break; + case NTFS_LOG_LEVEL_INFO: + prefix = "INFO: "; + break; + case NTFS_LOG_LEVEL_VERBOSE: + prefix = "VERBOSE: "; + break; + case NTFS_LOG_LEVEL_PROGRESS: + prefix = "PROGRESS: "; + break; + case NTFS_LOG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case NTFS_LOG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_PERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL: "; + break; + default: + prefix = ""; + break; + } + + return prefix; +} + + +/** + * ntfs_log_set_handler - Provide an alternate logging handler + * @handler: function to perform the logging + * + * This alternate handler will be called for all future logging requests. + * If no @handler is specified, logging will revert to the default handler. + */ +void ntfs_log_set_handler(ntfs_log_handler *handler) +{ + if (handler) { + ntfs_log.handler = handler; +#ifdef HAVE_SYSLOG_H + if (handler == ntfs_log_handler_syslog) + openlog("ntfs-3g", LOG_PID, LOG_USER); +#endif + } else + ntfs_log.handler = ntfs_log_handler_null; +} + +/** + * ntfs_log_redirect - Pass on the request to the real handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @...: Arguments to be formatted + * + * This is just a redirector function. The arguments are simply passed to the + * main logging handler (as defined in the global logging struct @ntfs_log). + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_redirect(const char *function, const char *file, + int line, u32 level, void *data, const char *format, ...) +{ + int olderr = errno; + int ret; + va_list args; + + if (!(ntfs_log.levels & level)) /* Don't log this message */ + return 0; + + va_start(args, format); + errno = olderr; + ret = ntfs_log.handler(function, file, line, level, data, format, args); + va_end(args); + + errno = olderr; + return ret; +} + + +/** + * ntfs_log_handler_syslog - syslog logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple syslog logging handler. Ignores colors. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ + + +#ifdef HAVE_SYSLOG_H + +#define LOG_LINE_LEN 512 + +int ntfs_log_handler_syslog(const char *function __attribute__((unused)), + const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), + const char *format, va_list args) +{ + char logbuf[LOG_LINE_LEN]; + int ret, olderr = errno; + +#ifndef DEBUG + if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) + return 1; +#endif + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); + if (ret < 0) { + vsyslog(LOG_NOTICE, format, args); + ret = 1; + goto out; + } + + if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); + } + + syslog(LOG_NOTICE, "%s", logbuf); +out: + errno = olderr; + return ret; +} +#endif + +/* + * Early logging before the logs are redirected + * + * (not quite satisfactory : this appears before the ntfs-g banner, + * and with a different pid) + */ + +void ntfs_log_early_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); +#ifdef HAVE_SYSLOG_H + openlog("ntfs-3g", LOG_PID, LOG_USER); + ntfs_log_handler_syslog(NULL, NULL, 0, + NTFS_LOG_LEVEL_ERROR, NULL, + format, args); +#else + vfprintf(stderr,format,args); +#endif + va_end(args); +} + +/** + * ntfs_log_handler_fprintf - Basic logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple logging handler. This is where the log line is finally displayed. + * It is more likely that you will want to set the handler to either + * ntfs_log_handler_outerr or ntfs_log_handler_stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, nothing will be displayed. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_fprintf(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ +#ifdef DEBUG + int i; +#endif + int ret = 0; + int olderr = errno; + FILE *stream; + + if (!data) /* Interpret data as a FILE stream. */ + return 0; /* If it's NULL, we can't do anything. */ + stream = (FILE*)data; + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; + } + + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif + if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && + (strchr(file, PATH_SEP))) /* Abbreviate the filename */ + file = strrchr(file, PATH_SEP) + 1; + + if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ + ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); + + if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ + ret += fprintf(stream, "%s ", file); + + if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ + ret += fprintf(stream, "(%d) ", line); + + if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) + ret += fprintf(stream, "%s(): ", function); + + ret += vfprintf(stream, format, args); + + if (level & NTFS_LOG_LEVEL_PERROR) + ret += fprintf(stream, ": %s\n", strerror(olderr)); + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif + fflush(stream); + errno = olderr; + return ret; +} + +/** + * ntfs_log_handler_null - Null logging handler (no output) + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * This handler produces no output. It provides a way to temporarily disable + * logging, without having to change the levels and flags. + * + * Returns: 0 Message wasn't logged + */ +int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), + const char *format __attribute__((unused)), va_list args __attribute__((unused))) +{ + return 0; +} + +/** + * ntfs_log_handler_stdout - All logs go to stdout + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stdout. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stdout(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stdout; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message. The output stream will be determined by the log + * level. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, the function ntfs_log_get_stream will be called + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_outerr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = ntfs_log_get_stream(level); + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_stderr - All logs go to stderr + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stderr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stderr; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + + +/** + * ntfs_log_parse_option - Act upon command line options + * @option: Option flag + * + * Delegate some of the work of parsing the command line. All the options begin + * with "--log-". Options cause log levels to be enabled in @ntfs_log (the + * global logging structure). + * + * Note: The "colour" option changes the logging handler. + * + * Returns: TRUE Option understood + * FALSE Invalid log option + */ +BOOL ntfs_log_parse_option(const char *option) +{ + if (strcmp(option, "--log-debug") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); + return TRUE; + } else if (strcmp(option, "--log-verbose") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + return TRUE; + } else if (strcmp(option, "--log-quiet") == 0) { + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + return TRUE; + } else if (strcmp(option, "--log-trace") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); + return TRUE; + } + + ntfs_log_debug("Unknown logging option '%s'\n", option); + return FALSE; +} + diff --git a/lib/libntfs/src/source/logging.h b/lib/libntfs/src/source/logging.h new file mode 100644 index 0000000..82f39fe --- /dev/null +++ b/lib/libntfs/src/source/logging.h @@ -0,0 +1,121 @@ +/* + * logging.h - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2007-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#include "types.h" + +/* Function prototype for the logging handlers */ +typedef int (ntfs_log_handler)(const char *function, const char *file, int line, + u32 level, void *data, const char *format, va_list args); + +/* Set the logging handler from one of the functions, below. */ +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); + +/* Logging handlers */ +ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0))); + +/* Enable/disable certain log levels */ +u32 ntfs_log_set_levels(u32 levels); +u32 ntfs_log_clear_levels(u32 levels); +u32 ntfs_log_get_levels(void); + +/* Enable/disable certain log flags */ +u32 ntfs_log_set_flags(u32 flags); +u32 ntfs_log_clear_flags(u32 flags); +u32 ntfs_log_get_flags(void); + +/* Turn command-line options into logging flags */ +BOOL ntfs_log_parse_option(const char *option); + +int ntfs_log_redirect(const char *function, const char *file, int line, + u32 level, void *data, const char *format, ...) + __attribute__((format(printf, 6, 7))); + +/* Logging levels - Determine what gets logged */ +#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ +#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ +#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ +#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ +#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ +#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ +#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ +#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ +#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ +#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ + +/* Logging style flags - Manage the style of the output */ +#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ +#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ +#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ +#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ +#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ + +/* Macros to simplify logging. One for each level defined above. + * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. + */ +#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) +#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) +#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) +#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) +#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) +#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) + +/* By default debug and trace messages are compiled into the program, + * but not displayed. + */ +#ifdef DEBUG +#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) +#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS) +#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS) +#else +#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) +#endif /* DEBUG */ + +void ntfs_log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +#endif /* _LOGGING_H_ */ + diff --git a/lib/libntfs/src/source/mem_allocate.h b/lib/libntfs/src/source/mem_allocate.h new file mode 100644 index 0000000..600e5a9 --- /dev/null +++ b/lib/libntfs/src/source/mem_allocate.h @@ -0,0 +1,43 @@ +/** + * mem_allocate.h - Memory allocation and destruction calls. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +static inline void* ntfs_alloc (size_t size) { + return malloc(size); +} + +static inline void* ntfs_align (size_t size) { + #ifdef __wii__ + return memalign(32, size); + #else + return malloc(size); + #endif +} + +static inline void ntfs_free (void* mem) { + free(mem); +} + +#endif /* _MEM_ALLOCATE_H */ diff --git a/lib/libntfs/src/source/mft.c b/lib/libntfs/src/source/mft.c new file mode 100644 index 0000000..0640efe --- /dev/null +++ b/lib/libntfs/src/source/mft.c @@ -0,0 +1,1909 @@ +/** + * mft.c - Mft record handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#include + +#include "compat.h" +#include "types.h" +#include "device.h" +#include "debug.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "volume.h" +#include "layout.h" +#include "lcnalloc.h" +#include "mft.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_mft_records_read - read records from the mft from disk + * @vol: volume to read from + * @mref: starting mft record number to read + * @count: number of mft records to read + * @b: output data buffer + * + * Read @count mft records starting at @mref from volume @vol into buffer + * @b. Return 0 on success or -1 on error, with errno set to the error + * code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * The read mft records are mst deprotected and are hence ready to use. The + * caller should check each record with is_baad_record() in case mst + * deprotection failed. + * + * NOTE: @b has to be at least of size @count * vol->mft_record_size. + */ +int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 br; + VCN m; + + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); + + if (!vol || !vol->mft_na || !b || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); + return -1; + } + m = MREF(mref); + /* Refuse to read non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to read non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (br != count) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); + return -1; + } + return 0; +} + +/** + * ntfs_mft_records_write - write mft records to disk + * @vol: volume to write to + * @mref: starting mft record number to write + * @count: number of mft records to write + * @b: data buffer containing the mft records to write + * + * Write @count mft records starting at @mref from data buffer @b to volume + * @vol. Return 0 on success or -1 on error, with errno set to the error code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * Before the mft records are written, they are mst protected. After the write, + * they are deprotected again, thus resulting in an increase in the update + * sequence number inside the data buffer @b. + * + * If any mft records are written which are also represented in the mft mirror + * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a + * temporary buffer before we do the actual write. Then if at least one mft + * record was successfully written, we write the appropriate mft records from + * the copied buffer to the mft mirror, too. + */ +int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 bw; + VCN m; + void *bmirr = NULL; + int cnt = 0, res = 0; + + if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { + errno = EINVAL; + return -1; + } + m = MREF(mref); + /* Refuse to write non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to write non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + if (m < vol->mftmirr_size) { + if (!vol->mftmirr_na) { + errno = EINVAL; + return -1; + } + cnt = vol->mftmirr_size - m; + if (cnt > count) + cnt = count; + bmirr = ntfs_malloc(cnt * vol->mft_record_size); + if (!bmirr) + return -1; + memcpy(bmirr, b, cnt * vol->mft_record_size); + } + bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (bw != count) { + if (bw != -1) + errno = EIO; + if (bw >= 0) + ntfs_log_debug("Error: partial write while writing $Mft " + "record(s)!\n"); + else + ntfs_log_perror("Error writing $Mft record(s)"); + res = errno; + } + if (bmirr && bw > 0) { + if (bw < cnt) + cnt = bw; + bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, + m << vol->mft_record_size_bits, cnt, + vol->mft_record_size, bmirr); + if (bw != cnt) { + if (bw != -1) + errno = EIO; + ntfs_log_debug("Error: failed to sync $MFTMirr! Run " + "chkdsk.\n"); + res = errno; + } + } + free(bmirr); + if (!res) + return res; + errno = res; + return -1; +} + +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + if (!NVolNoFixupWarn(vol)) + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), + (int)le32_to_cpu(*(le32*)m)); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + +/** + * ntfs_file_record_read - read a FILE record from the mft from disk + * @vol: volume to read from + * @mref: mft reference specifying mft record to read + * @mrec: address of pointer in which to return the mft record + * @attr: address of pointer in which to return the first attribute + * + * Read a FILE record from the mft of @vol from the storage medium. @mref + * specifies the mft record to read, including the sequence number, which can + * be 0 if no sequence number checking is to be performed. + * + * The function allocates a buffer large enough to hold the mft record and + * reads the record into the buffer (mst deprotecting it in the process). + * *@mrec is then set to point to the buffer. + * + * If @attr is not NULL, *@attr is set to point to the first attribute in the + * mft record, i.e. *@attr is a pointer into *@mrec. + * + * Return 0 on success, or -1 on error, with errno set to the error code. + * + * The read mft record is checked for having the magic FILE, + * and for having a matching sequence number (if MSEQNO(*@mref) != 0). + * If either of these fails, -1 is returned and errno is set to EIO. If you get + * this, but you still want to read the mft record (e.g. in order to correct + * it), use ntfs_mft_record_read() directly. + * + * Note: Caller has to free *@mrec when finished. + * + * Note: We do not check if the mft record is flagged in use. The caller can + * check if desired. + */ +int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr) +{ + MFT_RECORD *m; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + + m = *mrec; + if (!m) { + m = ntfs_malloc(vol->mft_record_size); + if (!m) + return -1; + } + if (ntfs_mft_record_read(vol, mref, m)) + goto err_out; + + if (ntfs_mft_record_check(vol, mref, m)) + goto err_out; + + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); + errno = EIO; + goto err_out; + } + *mrec = m; + if (attr) + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + return 0; +err_out: + if (m != *mrec) + free(m); + return -1; +} + +/** + * ntfs_mft_record_layout - layout an mft record into a memory buffer + * @vol: volume to which the mft record will belong + * @mref: mft reference specifying the mft record number + * @mrec: destination buffer of size >= @vol->mft_record_size bytes + * + * Layout an empty, unused mft record with the mft reference @mref into the + * buffer @m. The volume @vol is needed because the mft record structure was + * modified in NTFS 3.1 so we need to know which volume version this mft record + * will be used on. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec) +{ + ATTR_RECORD *a; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); + else { + /* Abort if mref is > 32 bits. */ + if (MREF(mref) & 0x0000ffff00000000ull) { + errno = ERANGE; + ntfs_log_perror("Mft reference exceeds 32 bits"); + return -1; + } + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + mrec->reserved = cpu_to_le16(0); + mrec->mft_record_number = cpu_to_le32(MREF(mref)); + } + mrec->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_BLOCK_SIZE) + mrec->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_BLOCK_SIZE + 1); + else { + mrec->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than MFT record size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the file system created was corrupt. " + "Thank you.\n", NTFS_DEV_LIST); + } + /* Set the update sequence number to 1. */ + *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); + mrec->lsn = cpu_to_le64(0ull); + mrec->sequence_number = cpu_to_le16(1); + mrec->link_count = cpu_to_le16(0); + /* Aligned to 8-byte boundary. */ + mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + + (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); + mrec->flags = cpu_to_le16(0); + /* + * Using attrs_offset plus eight bytes (for the termination attribute), + * aligned to 8-byte boundary. + */ + mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + + 7) & ~7); + mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); + mrec->base_mft_record = cpu_to_le64((MFT_REF)0); + mrec->next_attr_instance = cpu_to_le16(0); + a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + a->type = AT_END; + a->length = cpu_to_le32(0); + /* Finally, clear the unused part of the mft record. */ + memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); + return 0; +} + +/** + * ntfs_mft_record_format - format an mft record on an ntfs volume + * @vol: volume on which to format the mft record + * @mref: mft reference specifying mft record to format + * + * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay + * out an empty, unused mft record in memory and write it to the volume @vol. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) +{ + MFT_RECORD *m; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + m = ntfs_calloc(vol->mft_record_size); + if (!m) + goto out; + + if (ntfs_mft_record_layout(vol, mref, m)) + goto free_m; + + if (ntfs_mft_record_write(vol, mref, m)) + goto free_m; + + ret = 0; +free_m: + free(m); +out: + ntfs_log_leave("\n"); + return ret; +} + +static const char *es = " Leaving inconsistent metadata. Run chkdsk."; + +/** + * ntfs_ffz - Find the first unset (zero) bit in a word + * @word: + * + * Description... + * + * Returns: + */ +static inline unsigned int ntfs_ffz(unsigned int word) +{ + return ffs(~word) - 1; +} + +static int ntfs_is_mft(ntfs_inode *ni) +{ + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; +} + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define RESERVED_MFT_RECORDS 64 + +/** + * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap + * @vol: volume on which to search for a free mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Search for a free mft record in the mft bitmap attribute on the ntfs volume + * @vol. + * + * If @base_ni is NULL start the search at the default allocator position. + * + * If @base_ni is not NULL start the search at the mft record after the base + * mft record @base_ni. + * + * Return the free mft record on success and -1 on error with errno set to the + * error code. An error code of ENOSPC means that there are no free mft + * records in the currently initialized mft bitmap. + */ +static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 pass_end, ll, data_pos, pass_start, ofs, bit; + ntfs_attr *mftbmp_na; + u8 *buf, *byte; + unsigned int size; + u8 pass, b; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + /* + * Set the end of the pass making sure we do not overflow the mft + * bitmap. + */ + size = PAGE_SIZE; + pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; + ll = mftbmp_na->initialized_size << 3; + if (pass_end > ll) + pass_end = ll; + pass = 1; + if (!base_ni) + data_pos = vol->mft_data_pos; + else + data_pos = base_ni->mft_no + 1; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; + if (data_pos >= pass_end) { + data_pos = RESERVED_MFT_RECORDS; + pass = 2; + /* This happens on a freshly formatted volume. */ + if (data_pos >= pass_end) { + errno = ENOSPC; + goto leave; + } + } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } + pass_start = data_pos; + buf = ntfs_malloc(PAGE_SIZE); + if (!buf) + goto leave; + + ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " + "pass_end 0x%llx, data_pos 0x%llx.\n", pass, + (long long)pass_start, (long long)pass_end, + (long long)data_pos); +#ifdef DEBUG + byte = NULL; + b = 0; +#endif + /* Loop until a free mft record is found. */ + for (; pass <= 2; size = PAGE_SIZE) { + /* Cap size to pass_end. */ + ofs = data_pos >> 3; + ll = ((pass_end + 7) >> 3) - ofs; + if (size > ll) + size = ll; + ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); + if (ll < 0) { + ntfs_log_perror("Failed to read $MFT bitmap"); + free(buf); + goto leave; + } + ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); + /* If we read at least one byte, search @buf for a zero bit. */ + if (ll) { + size = ll << 3; + bit = data_pos & 7; + data_pos &= ~7ull; + ntfs_log_debug("Before inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + for (; bit < size && data_pos + bit < pass_end; + bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + + /* Note: ffz() result must be zero based. */ + b = ntfs_ffz((unsigned long)*byte); + if (b < 8 && b >= (bit & 7)) { + free(buf); + ret = data_pos + (bit & ~7ull) + b; + goto leave; + } + } + ntfs_log_debug("After inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + data_pos += size; + /* + * If the end of the pass has not been reached yet, + * continue searching the mft bitmap for a zero bit. + */ + if (data_pos < pass_end) + continue; + } + /* Do the next pass. */ + pass++; + if (pass == 2) { + /* + * Starting the second pass, in which we scan the first + * part of the zone which we omitted earlier. + */ + pass_end = pass_start; + data_pos = pass_start = RESERVED_MFT_RECORDS; + ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " + "0x%llx.\n", pass, (long long)pass_start, + (long long)pass_end); + if (data_pos >= pass_end) + break; + } + } + /* No free mft records in currently initialized mft bitmap. */ +out: + free(buf); + errno = ENOSPC; +leave: + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_mft_attr_extend(ntfs_attr *na) +{ + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); + + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } + + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation + */ +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) +{ + LCN lcn; + s64 ll = 0; /* silence compiler warning */ + ntfs_attr *mftbmp_na; + runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + mftbmp_na = vol->mftbmp_na; + /* + * Determine the last lcn of the mft bitmap. The allocated size of the + * mft bitmap cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >> + vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft bitmap attribute.\n"); + if (rl) + errno = EIO; + return STATUS_ERROR; + } + lcn = rl->lcn + rl->length; + + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); + if (!rl2) { + ntfs_log_error("Failed to allocate a cluster for " + "the mft bitmap.\n"); + return STATUS_ERROR; + } + rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft " + "bitmap.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate " + "cluster.%s\n", es); + free(rl2); + errno = err; + return STATUS_ERROR; + } + mftbmp_na->rl = rl; + ntfs_log_debug("Adding one run to mft bitmap.\n"); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* + * Update the attribute record as well. Note: @rl is the last + * (non-terminator) runlist element of mft bitmap. + */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mftbmp_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft bitmap attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft bitmap attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, mp_size + + le16_to_cpu(a->mapping_pairs_offset))) { + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* Generate the mapping pairs array directly into the attr record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, + NULL)) { + ntfs_log_error("Failed to build mapping pairs array for " + "mft bitmap attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft bitmap allocated_size by one cluster. + * Reflect this in the ntfs_attr structure and the attribute record. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft bitmap attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mftbmp_na->allocated_size += vol->cluster_size; + a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return STATUS_OK; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mftbmp_na->allocated_size += vol->cluster_size; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + return STATUS_ERROR; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); + errno = err; +undo_alloc: + err = errno; + + /* Remove the last run from the runlist. */ + lcn = rl->lcn; + rl->lcn = rl[1].lcn; + rl->length = 0; + + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) + ntfs_log_error("Failed to free cluster.%s\n", es); + else + vol->free_clusters++; + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping " + "pairs array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; +} +/** + * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the initialized portion of the mft bitmap attribute on the ntfs + * volume @vol by 8 bytes. + * + * Note: Only changes initialized_size and data_size, i.e. requires that + * allocated_size is big enough to fit the new initialized_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) +{ + s64 old_data_size, old_initialized_size, ll; + ntfs_attr *mftbmp_na; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int err; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.\n"); + err = errno; + goto put_err_out; + } + a = ctx->attr; + old_data_size = mftbmp_na->data_size; + old_initialized_size = mftbmp_na->initialized_size; + mftbmp_na->initialized_size += 8; + a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size > mftbmp_na->data_size) { + mftbmp_na->data_size = mftbmp_na->initialized_size; + a->data_size = cpu_to_sle64(mftbmp_na->data_size); + } + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* Initialize the mft bitmap attribute value with zeroes. */ + ll = 0; + ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); + if (ll == 8) { + ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); + vol->free_mft_records += (8 * 8); + ret = 0; + goto out; + } + ntfs_log_error("Failed to write to mft bitmap.\n"); + err = errno; + if (ll >= 0) + err = EIO; + /* Try to recover from the error. */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.%s\n", es); +put_err_out: + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + a = ctx->attr; + mftbmp_na->initialized_size = old_initialized_size; + a->initialized_size = cpu_to_sle64(old_initialized_size); + if (mftbmp_na->data_size != old_data_size) { + mftbmp_na->data_size = old_data_size; + a->data_size = cpu_to_sle64(old_data_size); + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); +err_out: + errno = err; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_data_extend_allocation - extend mft data attribute + * @vol: volume on which to extend the mft data attribute + * + * Extend the mft data attribute on the ntfs volume @vol by 16 mft records + * worth of clusters or if not enough space for this by one mft record worth + * of clusters. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) +{ + LCN lcn; + VCN old_last_vcn; + s64 min_nr, nr, ll = 0; /* silence compiler warning */ + ntfs_attr *mft_na; + runlist_element *rl, *rl2; + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + ntfs_log_enter("Extending mft data allocation.\n"); + + mft_na = vol->mft_na; + /* + * Determine the preferred allocation location, i.e. the last lcn of + * the mft data attribute. The allocated size of the mft data + * attribute cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mft_na, + (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft data attribute.\n"); + if (rl) + errno = EIO; + goto out; + } + + lcn = rl->lcn + rl->length; + ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); + /* Minimum allocation is one mft record worth of clusters. */ + min_nr = vol->mft_record_size >> vol->cluster_size_bits; + if (!min_nr) + min_nr = 1; + /* Want to allocate 16 mft records worth of clusters. */ + nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = min_nr; + + old_last_vcn = rl[1].vcn; + do { + rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); + if (rl2) + break; + if (errno != ENOSPC || nr == min_nr) { + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; + } + /* + * There is not enough space to do the allocation, but there + * might be enough space to do a minimal allocation so try that + * before failing. + */ + nr = min_nr; + ntfs_log_debug("Retrying mft data allocation with minimal cluster " + "count %lli.\n", (long long)nr); + } while (1); + + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); + + rl = ntfs_runlists_merge(mft_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft data " + "attribute.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate clusters " + "from the mft data attribute.%s\n", es); + free(rl2); + errno = err; + goto out; + } + mft_na->rl = rl; + + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* Update the attribute record as well. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mft_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft data attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft data attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, + mp_size + le16_to_cpu(a->mapping_pairs_offset))) { + ret = ntfs_mft_attr_extend(vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* + * Generate the mapping pairs array directly into the attribute record. + */ + if (ntfs_mapping_pairs_build(vol, + (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, + rl2, ll, NULL)) { + ntfs_log_error("Failed to build mapping pairs array of " + "mft data attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft data allocated_size by nr clusters. + * Reflect this in the ntfs_attr structure and the attribute record. + * @rl is the last (non-terminator) runlist element of mft data + * attribute. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, + mft_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft data attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mft_na->allocated_size += nr << vol->cluster_size_bits; + a->allocated_size = cpu_to_sle64(mft_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mft_na->allocated_size += nr << vol->cluster_size_bits; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + ret = STATUS_ERROR; + goto out; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(old_last_vcn - 1); + errno = err; +undo_alloc: + err = errno; + if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0) + ntfs_log_error("Failed to free clusters from mft data " + "attribute.%s\n", es); + if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn)) + ntfs_log_error("Failed to truncate mft data attribute " + "runlist.%s\n", es); + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping pairs " + "array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + goto out; +} + + +static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ + + mft_na = vol->mft_na; + + /* + * The mft record is outside the initialized data. Extend the mft data + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume + * is first written to so it optimizes away nicely in the common case. + */ + ntfs_log_debug("Status of mft data before extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + while (size > mft_na->allocated_size) { + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) + goto out; + ntfs_log_debug("Status of mft data after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records along the way. + * Note: We only modify the ntfs_attr structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + while (size > mft_na->initialized_size) { + s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; + mft_na->initialized_size += vol->mft_record_size; + if (mft_na->initialized_size > mft_na->data_size) + mft_na->data_size = mft_na->initialized_size; + ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); + if (ntfs_mft_record_format(vol, ll2) < 0) { + ntfs_log_perror("Failed to format mft record"); + goto undo_data_init; + } + } + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Status of mft data after mft record initialization: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); + + /* Sync MFT to minimize data loss if there won't be clean unmount. */ + if (ntfs_inode_sync(mft_na->ni)) + goto undo_data_init; + + ret = 0; +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + le16 seq_no, usn; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + base_ni = mft_na->ni; + + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; + + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; + +found_free_rec: + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume + * @vol: volume on which to allocate the mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. + * + * If @base_ni is NULL make the mft record a base mft record and allocate it at + * the default allocator position. + * + * If @base_ni is not NULL make the allocated mft record an extent record, + * allocate it starting at the mft record after the base mft record and attach + * the allocated and opened ntfs inode to the base inode @base_ni. + * + * On success return the now opened ntfs (extent) inode of the mft record. + * + * On error return NULL with errno set to the error code. + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place specified by @base_ni or if + * @base_ni is NULL we start where we last stopped and we perform wrap around + * when we reach the end. Note, we do not try to allocate mft records below + * number 24 because numbers 0 to 15 are the defined system files anyway and 16 + * to 24 are special in that they are used for storing extension mft records + * for the $DATA attribute of $MFT. This is required to avoid the possibility + * of creating a run list with a circular dependence which once written to disk + * can never be read in again. Windows will only use records 16 to 24 for + * normal files if the volume is completely out of space. We never use them + * which means that when the volume is really out of space we cannot create any + * more files while Windows can still create up to 8 small files. We can start + * doing this at some later time, it does not matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 24 to number of + * allocated mft records, then we extend the $MFT/$DATA attribute in order to + * create free mft records. We extend the allocated size of $MFT/$DATA by 16 + * records at a time or one cluster, if cluster size is above 16kiB. If there + * is not sufficient space to do this, we try to extend by a single mft record + * or one cluster, if cluster size is above the mft record size, but we only do + * this if there is enough free space, which we know from the values returned + * by the failed cluster allocation function when we tried to do the first + * allocation. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record, incrementing mft data size and initialized size + * accordingly, open an ntfs_inode for it and return it to the caller, unless + * there are less than 24 mft records, in which case we allocate and initialize + * mft records until we reach record 24 which we consider as the first free mft + * record for use by normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superfluous bits are padded with zeroes. + * + * Thus, when we return successfully (return value non-zero), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - open an ntfs_inode for the allocated mft record, and we will + * - return the ntfs_inode. + * + * On error (return value zero), nothing will have changed. If we had changed + * anything before the error occurred, we will have reverted back to the + * starting state before returning to the caller. Thus, except for bugs, we + * should always leave the volume in a consistent state when returning from + * this function. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important that + * the extension mft records used to store the $DATA attribute of $MFT can be + * reached without having to read the information contained inside them, as + * this would make it impossible to find them in the first place after the + * volume is dismounted. $MFT/$BITMAP probably does not need to follow this + * rule because the bitmap is not essential for finding the mft records, but on + * the other hand, handling the bitmap in this special way would make life + * easier because otherwise there might be circular invocations of functions + * when reading the bitmap but if we are careful, we should be able to avoid + * all problems. + */ +ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + int err; + le16 seq_no, usn; + + if (base_ni) + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); + else + ntfs_log_enter("Entering (allocating a base mft record)\n"); + if (!vol || !vol->mft_na || !vol->mftbmp_na) { + errno = EINVAL; + goto out; + } + + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol); + goto out; + } + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; +retry: + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) { + ntfs_log_debug("found free record (#1) at %lld\n", + (long long)bit); + goto found_free_rec; + } + if (errno != ENOSPC) + goto out; + /* + * No free mft records left. If the mft bitmap already covers more + * than the currently used mft records, the next records are all free, + * so we can simply allocate the first unused mft record. + * Note: We also have to make sure that the mft bitmap at least covers + * the first 24 mft records as they are special and whilst they may not + * be in use, we do not allocate from them. + */ + ll = mft_na->initialized_size >> vol->mft_record_size_bits; + if (mftbmp_na->initialized_size << 3 > ll && + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { + bit = ll; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + ntfs_log_debug("found free record (#2) at %lld\n", + (long long)bit); + goto found_free_rec; + } + /* + * The mft bitmap needs to be expanded until it covers the first unused + * mft record that we can allocate. + * Note: The smallest mft record we allocate is mft record 24. + */ + ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { + + int ret = ntfs_mft_bitmap_extend_allocation(vol); + + if (ret == STATUS_ERROR) + goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } + + ntfs_log_debug("Status of mftbmp after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + } + /* + * We now have sufficient allocated space, extend the initialized_size + * as well as the data_size if necessary and fill the new space with + * zeroes. + */ + bit = mftbmp_na->initialized_size << 3; + if (ntfs_mft_bitmap_extend_initialized(vol)) + goto err_out; + ntfs_log_debug("Status of mftbmp after initialized extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); +found_free_rec: + /* @bit is the found free mft record, allocate it in the mft bitmap. */ + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); + goto err_out; + } + + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_record_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto retry; + } + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + if (base_ni) { + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + } + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_debug("allocated %sinode 0x%llx.\n", + base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_free - free an mft record on an ntfs volume + * @vol: volume on which to free the mft record + * @ni: open ntfs inode of the mft record to free + * + * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. + * Note that this function calls ntfs_inode_close() internally and hence you + * cannot use the pointer @ni any more after this function returns success. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) +{ + u64 mft_no; + int err; + u16 seq_no; + le16 old_seq_no; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!vol || !vol->mftbmp_na || !ni) { + errno = EINVAL; + return -1; + } + + /* Cache the mft reference for later. */ + mft_no = ni->mft_no; + + /* Mark the mft record as not in use. */ + ni->mrec->flags &= ~MFT_RECORD_IN_USE; + + /* Increment the sequence number, skipping zero, if it is not zero. */ + old_seq_no = ni->mrec->sequence_number; + seq_no = le16_to_cpu(old_seq_no); + if (seq_no == 0xffff) + seq_no = 1; + else if (seq_no) + seq_no++; + ni->mrec->sequence_number = cpu_to_le16(seq_no); + + /* Set the inode dirty and write it out. */ + ntfs_inode_mark_dirty(ni); + if (ntfs_inode_sync(ni)) { + err = errno; + goto sync_rollback; + } + + /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ + if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { + err = errno; + // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on + // error, this could be changed to goto sync_rollback; + goto bitmap_rollback; + } + + /* Throw away the now freed inode. */ +#if CACHE_NIDATA_SIZE + if (!ntfs_inode_real_close(ni)) { +#else + if (!ntfs_inode_close(ni)) { +#endif + vol->free_mft_records++; + return 0; + } + err = errno; + + /* Rollback what we did... */ +bitmap_rollback: + if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) + ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " + "Leaving inconsistent metadata!\n"); +sync_rollback: + ni->mrec->flags |= MFT_RECORD_IN_USE; + ni->mrec->sequence_number = old_seq_no; + ntfs_inode_mark_dirty(ni); + errno = err; + return -1; +} + +/** + * ntfs_mft_usn_dec - Decrement USN by one + * @mrec: pointer to an mft record + * + * On success return 0 and on error return -1 with errno set. + */ +int ntfs_mft_usn_dec(MFT_RECORD *mrec) +{ + u16 usn; + le16 *usnp; + + if (!mrec) { + errno = EINVAL; + return -1; + } + usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs)); + usn = le16_to_cpup(usnp); + if (usn-- <= 1) + usn = 0xfffe; + *usnp = cpu_to_le16(usn); + + return 0; +} + diff --git a/lib/libntfs/src/source/mft.h b/lib/libntfs/src/source/mft.h new file mode 100644 index 0000000..bb15f0f --- /dev/null +++ b/lib/libntfs/src/source/mft.h @@ -0,0 +1,132 @@ +/* + * mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MFT_H +#define _NTFS_MFT_H + +#include "volume.h" +#include "inode.h" +#include "layout.h" +#include "logging.h" + +extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_read - read a record from the mft + * @vol: volume to read from + * @mref: mft record number to read + * @b: output data buffer + * + * Read the mft record specified by @mref from volume @vol into buffer @b. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * The read mft record is mst deprotected and is hence ready to use. The caller + * should check the record with is_baad_record() in case mst deprotection + * failed. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + +extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr); + +extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_write - write an mft record to disk + * @vol: volume to write to + * @mref: mft record number to write + * @b: data buffer containing the mft record to write + * + * Write the mft record specified by @mref from buffer @b to volume @vol. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * Before the mft record is written, it is mst protected. After the write, it + * is deprotected again, thus resulting in an increase in the update sequence + * number inside the buffer @b. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b + * @m: mft record to get the data size of + * + * Takes the mft record @m and returns the number of bytes used in the record + * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size + * for an mft record as it at least has to have the MFT_RECORD itself and a + * zero length attribute of type AT_END, thus making the minimum size 56 bytes. + * + * Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte + * alignment of the first attribute mask the difference in MFT_RECORD size + * between NTFS 1.x and 3.x. Also, you would expect every mft record to + * contain an update sequence array as well but that could in theory be + * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this + * as corruption in itself though). + */ +static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) +{ + if (!m || !ntfs_is_mft_record(m->magic)) + return 0; + /* Get the number of used bytes and return it. */ + return le32_to_cpu(m->bytes_in_use); +} + +extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec); + +extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); + +extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); + +extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); + +extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); + +#endif /* defined _NTFS_MFT_H */ + diff --git a/lib/libntfs/src/source/misc.c b/lib/libntfs/src/source/misc.c new file mode 100644 index 0000000..b2e17cb --- /dev/null +++ b/lib/libntfs/src/source/misc.c @@ -0,0 +1,61 @@ +/** + * misc.c : miscellaneous : + * - dealing with errors in memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "types.h" +#include "misc.h" +#include "logging.h" + +/** + * ntfs_calloc + * + * Return a pointer to the allocated memory or NULL if the request fails. + */ +void *ntfs_calloc(size_t size) +{ + void *p; + + p = calloc(1, size); + if (!p) + ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); + return p; +} + +void *ntfs_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); + return p; +} diff --git a/lib/libntfs/src/source/misc.h b/lib/libntfs/src/source/misc.h new file mode 100644 index 0000000..a03e964 --- /dev/null +++ b/lib/libntfs/src/source/misc.h @@ -0,0 +1,30 @@ +/* + * misc.h : miscellaneous exports + * - memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MISC_H_ +#define _NTFS_MISC_H_ + +void *ntfs_calloc(size_t size); +void *ntfs_malloc(size_t size); + +#endif /* _NTFS_MISC_H_ */ + diff --git a/lib/libntfs/src/source/mst.c b/lib/libntfs/src/source/mst.c new file mode 100644 index 0000000..b7937b7 --- /dev/null +++ b/lib/libntfs/src/source/mst.c @@ -0,0 +1,247 @@ +/** + * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "mst.h" +#include "logging.h" + +/** + * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * @size: size in bytes of @b + * + * Perform the necessary post read multi sector transfer fixups and detect the + * presence of incomplete multi sector transfers. - In that case, overwrite the + * magic of the ntfs record header being processed with "BAAD" (in memory only!) + * and abort processing. + * + * Return 0 on success and -1 on error, with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid arguments or invalid NTFS record in buffer @b. + * EIO Multi sector transfer error was detected. Magic of the NTFS + * record in @b will have been set to "BAAD". + */ +int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + if (warn) { + ntfs_log_perror("%s: magic: 0x%08lx size: %ld " + " usa_ofs: %d usa_count: %u", + __FUNCTION__, + (long)le32_to_cpu(*(le32 *)b), + (long)size, (int)usa_ofs, + (unsigned int)usa_count); + } + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + /* + * The update sequence number which has to be equal to each of the + * u16 values before they are fixed up. Note no need to care for + * endianness since we are comparing and moving data for on disk + * structures which means the data is consistent. - If it is + * consistency the wrong endianness it doesn't make any difference. + */ + usn = *usa_pos; + /* + * Position in protected data of first u16 that needs fixing up. + */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* + * Check for incomplete multi sector transfer(s). + */ + while (usa_count--) { + if (*data_pos != usn) { + /* + * Incomplete multi sector transfer detected! )-: + * Set the magic to "BAAD" and return failure. + * Note that magic_BAAD is already converted to le32. + */ + errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; + return -1; + } + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + /* Re-setup the variables. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/* + * Deprotect multi sector transfer protected data + * with a warning if an error is found. + */ + +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +{ + return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); +} + +/** + * ntfs_mst_pre_write_fixup - apply multi sector transfer protection + * @b: pointer to the data to protect + * @size: size in bytes of @b + * + * Perform the necessary pre write multi sector transfer fixup on the data + * pointer to by @b of @size. + * + * Return 0 if fixups applied successfully or -1 if no fixups were performed + * due to errors. In that case errno i set to the error code (EINVAL). + * + * NOTE: We consider the absence / invalidity of an update sequence array to + * mean error. This means that you have to create a valid update sequence + * array header in the ntfs record before calling this function, otherwise it + * will fail (the header needs to contain the position of the update sequence + * array together with the number of elements in the array). You also need to + * initialise the update sequence number before calling this function + * otherwise a random word will be used (whatever was in the record at that + * position at that time). + */ +int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Sanity check + only fixup if it makes sense. */ + if (!b || ntfs_is_baad_record(b->magic) || + ntfs_is_hole_record(b->magic)) { + errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); + return -1; + } + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)((u8*)b + usa_ofs); + /* + * Cyclically increment the update sequence number + * (skipping 0 and -1, i.e. 0xffff). + */ + usn = le16_to_cpup(usa_pos) + 1; + if (usn == 0xffff || !usn) + usn = 1; + usn = cpu_to_le16(usn); + *usa_pos = usn; + /* Position in data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment the position in the usa and save the + * original data from the data buffer into the usa. + */ + *(++usa_pos) = *data_pos; + /* Apply fixup to data. */ + *data_pos = usn; + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/** + * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * + * Perform the necessary post write multi sector transfer fixup, not checking + * for any errors, because we assume we have just used + * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never + * have gotten here. + */ +void ntfs_mst_post_write_fixup(NTFS_RECORD *b) +{ + u16 *usa_pos, *data_pos; + + u16 usa_ofs = le16_to_cpu(b->usa_ofs); + u16 usa_count = le16_to_cpu(b->usa_count) - 1; + + ntfs_log_trace("Entering\n"); + + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + + /* Position in protected data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } +} + diff --git a/lib/libntfs/src/source/mst.h b/lib/libntfs/src/source/mst.h new file mode 100644 index 0000000..d6ca6f2 --- /dev/null +++ b/lib/libntfs/src/source/mst.h @@ -0,0 +1,37 @@ +/* + * mst.h - Exports for multi sector transfer fixup functions. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MST_H +#define _NTFS_MST_H + +#include "types.h" +#include "layout.h" +#include "volume.h" + +extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn); +extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); +extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); + +#endif /* defined _NTFS_MST_H */ + diff --git a/lib/libntfs/src/source/ntfs.c b/lib/libntfs/src/source/ntfs.c new file mode 100644 index 0000000..5c03a3a --- /dev/null +++ b/lib/libntfs/src/source/ntfs.c @@ -0,0 +1,659 @@ +/** + * ntfs.c - Simple functionality for startup, mounting and unmounting of NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfs.h" +#include "ntfsinternal.h" +#include "ntfsfile.h" +#include "ntfsdir.h" +#include "gekko_io.h" +#include "cache.h" + +// NTFS device driver devoptab +static const devoptab_t devops_ntfs = { + NULL, /* Device name */ + sizeof (ntfs_file_state), + ntfs_open_r, + ntfs_close_r, + ntfs_write_r, + ntfs_read_r, + ntfs_seek_r, + ntfs_fstat_r, + ntfs_stat_r, + ntfs_link_r, + ntfs_unlink_r, + ntfs_chdir_r, + ntfs_rename_r, + ntfs_mkdir_r, + sizeof (ntfs_dir_state), + ntfs_diropen_r, + ntfs_dirreset_r, + ntfs_dirnext_r, + ntfs_dirclose_r, + ntfs_statvfs_r, + ntfs_ftruncate_r, + ntfs_fsync_r, + NULL /* Device data */ +}; + +void ntfsInit (void) +{ + static bool isInit = false; + + // Initialise ntfs-3g (if not already done so) + if (!isInit) { + isInit = true; + + // Set the log handler + #ifdef NTFS_ENABLE_LOG + ntfs_log_set_handler(ntfs_log_handler_stderr); + #else + ntfs_log_set_handler(ntfs_log_handler_null); + #endif + } + + return; +} + +int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) +{ + MASTER_BOOT_RECORD mbr; + PARTITION_RECORD *partition = NULL; + sec_t partition_starts[NTFS_MAX_PARTITIONS] = {0}; + int partition_count = 0; + sec_t part_lba = 0; + int i; + + union { + u8 buffer[MAX_SECTOR_SIZE]; + MASTER_BOOT_RECORD mbr; + EXTENDED_BOOT_RECORD ebr; + NTFS_BOOT_SECTOR boot; + } sector; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + if (!partitions) + return 0; + + // Initialise ntfs-3g + ntfsInit(); + + // Start the device and check that it is inserted + if (!interface->startup()) { + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + return 0; + } + + // Read the first sector on the device + if (!interface->readSectors(0, 1, §or.buffer)) { + errno = EIO; + return -1; + } + + // If this is the devices master boot record + if (sector.mbr.signature == MBR_SIGNATURE) { + memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); + ntfs_log_debug("Valid Master Boot Record found\n"); + + // Search the partition table for all NTFS partitions (max. 4 primary partitions) + for (i = 0; i < 4; i++) { + partition = &mbr.partitions[i]; + part_lba = le32_to_cpu(mbr.partitions[i].lba_start); + + ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, + partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + part_lba, partition->type); + + // Figure out what type of partition this is + switch (partition->type) { + + // Ignore empty partitions + case PARTITION_TYPE_EMPTY: + continue; + + // NTFS partition + case PARTITION_TYPE_NTFS: { + ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); + + // Read and validate the NTFS partition + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } else { + ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); + } + } + + break; + + } + + // DOS 3.3+ or Windows 95 extended partition + case PARTITION_TYPE_DOS33_EXTENDED: + case PARTITION_TYPE_WIN95_EXTENDED: { + ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); + + // Walk the extended partition chain, finding all NTFS partitions within it + sec_t ebr_lba = part_lba; + sec_t next_erb_lba = 0; + do { + + // Read and validate the extended boot record + if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { + if (sector.ebr.signature == EBR_SIGNATURE) { + ntfs_log_debug("Logical Partition @ %d: %s type 0x%x\n", ebr_lba + next_erb_lba, + sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + sector.ebr.partition.type); + + // Get the start sector of the current partition + // and the next extended boot record in the chain + part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); + next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start); + + // Check if this partition has a valid NTFS boot record + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba); + if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } + } + + } else { + next_erb_lba = 0; + } + } + + } while (next_erb_lba); + + break; + + } + + // Unknown or unsupported partition type + default: { + + // Check if this partition has a valid NTFS boot record anyway, + // it might be misrepresented due to a lazy partition editor + if (interface->readSectors(part_lba, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); + if(partition->type != PARTITION_TYPE_NTFS) { + ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS); + } + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = part_lba; + partition_count++; + } + } + } + + break; + + } + + } + + } + + // Else it is assumed this device has no master boot record + } else { + ntfs_log_debug("No Master Boot Record was found!\n"); + + // As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions + for (i = 0; i < 64; i++) { + if (interface->readSectors(i, 1, §or)) { + if (sector.boot.oem_id == NTFS_OEM_ID) { + ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i); + if (partition_count < NTFS_MAX_PARTITIONS) { + partition_starts[partition_count] = i; + partition_count++; + } + } + } + } + + } + + // Return the found partitions (if any) + if (partition_count > 0) { + *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); + if (*partitions) { + memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count); + return partition_count; + } + } + + return 0; +} + +int ntfsMountAll (ntfs_md **mounts, u32 flags) +{ + const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); + const INTERFACE_ID *disc = NULL; + ntfs_md mount_points[NTFS_MAX_MOUNTS]; + sec_t *partitions = NULL; + int mount_count = 0; + int partition_count = 0; + char name[128]; + int i, j, k; + + // Initialise ntfs-3g + ntfsInit(); + + // Find and mount all NTFS partitions on all known devices + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { + disc = &discs[i]; + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { + + // Find the next unused mount name + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); + errno = EADDRNOTAVAIL; + return -1; + } + } while (ntfsGetDevice(name, false)); + + // Mount the partition + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); + mount_points[mount_count].interface = disc->interface; + mount_points[mount_count].startSector = partitions[j]; + mount_count++; + } + } + + } + ntfs_free(partitions); + } + } + + // Return the mounts (if any) + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); + return mount_count; + } + } + + return 0; +} + +int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags) +{ + const INTERFACE_ID *discs = ntfsGetDiscInterfaces(); + const INTERFACE_ID *disc = NULL; + ntfs_md mount_points[NTFS_MAX_MOUNTS]; + sec_t *partitions = NULL; + int mount_count = 0; + int partition_count = 0; + char name[128]; + int i, j, k; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + + // Initialise ntfs-3g + ntfsInit(); + + // Find the specified device then find and mount all NTFS partitions on it + for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { + if (discs[i].interface == interface) { + disc = &discs[i]; + partition_count = ntfsFindPartitions(disc->interface, &partitions); + if (partition_count > 0 && partitions) { + for (j = 0, k = 0; j < partition_count; j++) { + + // Find the next unused mount name + do { + sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); + if (k >= NTFS_MAX_MOUNTS) { + ntfs_free(partitions); + errno = EADDRNOTAVAIL; + return -1; + } + } while (ntfsGetDevice(name, false)); + + // Mount the partition + if (mount_count < NTFS_MAX_MOUNTS) { + if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { + strcpy(mount_points[mount_count].name, name); + mount_points[mount_count].interface = disc->interface; + mount_points[mount_count].startSector = partitions[j]; + mount_count++; + } + } + + } + ntfs_free(partitions); + } + break; + } + } + + // If we couldn't find the device then return with error status + if (!disc) { + errno = ENODEV; + return -1; + } + + // Return the mounts (if any) + if (mount_count > 0 && mounts) { + *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); + if (*mounts) { + memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count); + return mount_count; + } + } + + return 0; +} + +bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) +{ + ntfs_vd *vd = NULL; + gekko_fd *fd = NULL; + + // Sanity check + if (!name || !interface) { + errno = EINVAL; + return false; + } + + // Initialise ntfs-3g + ntfsInit(); + + // Check that the requested mount name is free + if (ntfsGetDevice(name, false)) { + errno = EADDRINUSE; + return false; + } + + // Check that we can at least read from this device + if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { + errno = EPERM; + return false; + } + + // Allocate the volume descriptor + vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); + if (!vd) { + errno = ENOMEM; + return false; + } + + // Setup the volume descriptor + vd->id = interface->ioType; + vd->flags = 0; + vd->uid = 0; + vd->gid = 0; + vd->fmask = 0; + vd->dmask = 0; + vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); + vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); + vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); + + // Allocate the device driver descriptor + fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); + if (!fd) { + ntfs_free(vd); + errno = ENOMEM; + return false; + } + + // Setup the device driver descriptor + fd->interface = interface; + fd->startSector = startSector; + fd->sectorSize = 0; + fd->sectorCount = 0; + fd->cachePageCount = cachePageCount; + fd->cachePageSize = cachePageSize; + + // Allocate the device driver + vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); + if (!vd->dev) { + ntfs_free(fd); + ntfs_free(vd); + return false; + } + + // Build the mount flags + if (flags & NTFS_READ_ONLY) + vd->flags |= MS_RDONLY; + else + { + if (!(interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_RDONLY; + if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE)) + vd->flags |= MS_EXCLUSIVE; + } + if (flags & NTFS_RECOVER) + vd->flags |= MS_RECOVER; + if (flags & NTFS_IGNORE_HIBERFILE) + vd->flags |= MS_IGNORE_HIBERFILE; + + if (vd->flags & MS_RDONLY) + ntfs_log_debug("Mounting \"%s\" as read-only\n", name); + + // Mount the device + vd->vol = ntfs_device_mount(vd->dev, vd->flags); + if (!vd->vol) { + switch(ntfs_volume_error(errno)) { + case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break; + case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break; + case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break; + default: errno = EINVAL; break; + } + ntfs_device_free(vd->dev); + ntfs_free(vd); + return false; + } + + if (flags & NTFS_IGNORE_CASE) + ntfs_set_ignore_case(vd->vol); + + // Initialise the volume descriptor + if (ntfsInitVolume(vd)) { + ntfs_umount(vd->vol, true); + ntfs_free(vd); + return false; + } + + // Add the device to the devoptab table + if (ntfsAddDevice(name, vd)) { + ntfsDeinitVolume(vd); + ntfs_umount(vd->vol, true); + ntfs_free(vd); + return false; + } + + return true; +} + +void ntfsUnmount (const char *name, bool force) +{ + ntfs_vd *vd = NULL; + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) + return; + + // Remove the device from the devoptab table + ntfsRemoveDevice(name); + + // Deinitialise the volume descriptor + ntfsDeinitVolume(vd); + + // Unmount the volume + ntfs_umount(vd->vol, force); + + // Free the volume descriptor + ntfs_free(vd); + + return; +} + +const char *ntfsGetVolumeName (const char *name) +{ + ntfs_vd *vd = NULL; + + // Sanity check + if (!name) { + errno = EINVAL; + return NULL; + } + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) { + errno = ENODEV; + return NULL; + } + return vd->vol->vol_name; +} + +bool ntfsSetVolumeName (const char *name, const char *volumeName) +{ + ntfs_vd *vd = NULL; + ntfs_attr *na = NULL; + ntfschar *ulabel = NULL; + int ulabel_len; + + // Sanity check + if (!name) { + errno = EINVAL; + return false; + } + + // Get the devices volume descriptor + vd = ntfsGetVolume(name); + if (!vd) { + errno = ENODEV; + return false; + } + + // Lock + ntfsLock(vd); + + // Convert the new volume name to unicode + ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); + if (ulabel_len < 0) { + ntfsUnlock(vd); + errno = EINVAL; + return false; + } + + // Check if the volume name attribute exists + na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); + if (na) { + + // It does, resize it to match the length of the new volume name + if (ntfs_attr_truncate(na, ulabel_len)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + // Write the new volume name + if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + } else { + + // It doesn't, create it now + if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + } + + // Reset the volumes name cache (as it has now been changed) + vd->name[0] = '\0'; + + // Close the volume name attribute + if (na) + ntfs_attr_close(na); + + // Sync the volume node + if (ntfs_inode_sync(vd->vol->vol_ni)) { + free(ulabel); + ntfsUnlock(vd); + return false; + } + + // Clean up + free(ulabel); + + // Unlock + ntfsUnlock(vd); + + return true; +} + +const devoptab_t *ntfsGetDevOpTab (void) +{ + return &devops_ntfs; +} diff --git a/lib/libntfs/src/source/ntfsdir.c b/lib/libntfs/src/source/ntfsdir.c new file mode 100644 index 0000000..ad54b51 --- /dev/null +++ b/lib/libntfs/src/source/ntfsdir.c @@ -0,0 +1,636 @@ +/** + * ntfs_dir.c - devoptab directory routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsdir.h" +#include "device.h" +#include + +#define STATE(x) ((ntfs_dir_state*)(x)->dirStruct) + +void ntfsCloseDir (ntfs_dir_state *dir) +{ + // Sanity check + if (!dir || !dir->vd) + return; + + // Free the directory entries (if any) + while (dir->first) { + ntfs_dir_entry *next = dir->first->next; + ntfs_free(dir->first->name); + ntfs_free(dir->first); + dir->first = next; + } + + // Close the directory (if open) + if (dir->ni) + ntfsCloseEntry(dir->vd, dir->ni); + + // Reset the directory state + dir->ni = NULL; + dir->first = NULL; + dir->current = NULL; + + return; +} + +int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + // Short circuit cases were we don't actually have to do anything + if (!st || !path) + return 0; + + ntfs_log_trace("path %s, st %p\n", path, st); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFDIR; + return 0; + } + + // Lock + ntfsLock(vd); + + // Find the entry + ni = ntfsOpenEntry(vd, path); + if (!ni) { + r->_errno = errno; + ntfsUnlock(vd); + return -1; + } + + // Get the entry stats + int ret = ntfsStat(vd, ni, st); + if (ret) + r->_errno = errno; + + // Close the entry + ntfsCloseEntry(vd, ni); + + ntfsUnlock(vd); + + return 0; +} + +int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + ntfs_log_trace("existing %s, newLink %s\n", existing, newLink); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(existing); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Create a symbolic link between the two paths + ni = ntfsCreate(vd, existing, S_IFLNK, newLink); + if (!ni) { + ntfsUnlock(vd); + r->_errno = errno; + return -1; + } + + // Close the symbolic link + ntfsCloseEntry(vd, ni); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_unlink_r (struct _reent *r, const char *name) +{ + ntfs_log_trace("name %s\n", name); + + // Unlink the entry + int ret = ntfsUnlink(ntfsGetVolume(name), name); + if (ret) + r->_errno = errno; + + return ret; +} + +int ntfs_chdir_r (struct _reent *r, const char *name) +{ + ntfs_log_trace("name %s\n", name); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Find the directory + ni = ntfsOpenEntry(vd, name); + if (!ni) { + ntfsUnlock(vd); + r->_errno = ENOENT; + return -1; + } + + // Ensure that this directory is indeed a directory + if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); + r->_errno = ENOTDIR; + return -1; + } + + // Close the old current directory (if any) + if (vd->cwd_ni) + ntfsCloseEntry(vd, vd->cwd_ni); + + // Set the new current directory + vd->cwd_ni = ni; + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + ntfs_log_trace("oldName %s, newName %s\n", oldName, newName); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(oldName); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // You cannot rename between devices + if(vd != ntfsGetVolume(newName)) { + ntfsUnlock(vd); + r->_errno = EXDEV; + return -1; + } + + // Check that there is no existing entry with the new name + ni = ntfsOpenEntry(vd, newName); + if (ni) { + ntfsCloseEntry(vd, ni); + ntfsUnlock(vd); + r->_errno = EEXIST; + return -1; + } + + // Link the old entry with the new one + if (ntfsLink(vd, oldName, newName)) { + ntfsUnlock(vd); + return -1; + } + + // Unlink the old entry + if (ntfsUnlink(vd, oldName)) { + if (ntfsUnlink(vd, newName)) { + ntfsUnlock(vd); + return -1; + } + ntfsUnlock(vd); + return -1; + } + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_mkdir_r (struct _reent *r, const char *path, int mode) +{ + ntfs_log_trace("path %s, mode %i\n", path, mode); + + ntfs_vd *vd = NULL; + ntfs_inode *ni = NULL; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(vd); + + // Create the directory + ni = ntfsCreate(vd, path, S_IFDIR, NULL); + if (!ni) { + ntfsUnlock(vd); + r->_errno = errno; + return -1; + } + + // Close the directory + ntfsCloseEntry(vd, ni); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + ntfs_log_trace("path %s, buf %p\n", path, buf); + + ntfs_vd *vd = NULL; + s64 size; + int delta_bits; + + // Get the volume descriptor for this path + vd = ntfsGetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Lock + ntfsLock(vd); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + if(ntfs_volume_get_free_space(vd->vol) < 0) + { + ntfsUnlock(vd); + return -1; + } + + // File system block size + buf->f_bsize = vd->vol->cluster_size; + + // Fundamental file system block size + buf->f_frsize = vd->vol->cluster_size; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = vd->vol->nr_clusters; + + // Free blocks available for all and for non-privileged processes + size = MAX(vd->vol->free_clusters, 0); + buf->f_bfree = buf->f_bavail = size; + + // Free inodes on the free space + delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits; + if (delta_bits >= 0) + size <<= delta_bits; + else + size >>= -delta_bits; + + // Number of inodes at this point in time + buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size; + + // Free inodes available for all and for non-privileged processes + size += vd->vol->free_mft_records; + buf->f_ffree = buf->f_favail = MAX(size, 0); + + // File system id + buf->f_fsid = vd->id; + + // Bit mask of f_flag values. + buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0); + + // Maximum length of filenames + buf->f_namemax = NTFS_MAX_NAME_LEN; + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +/** + * PRIVATE: Callback for directory walking + */ +int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type, + const s64 pos, const MFT_REF mref, const unsigned dt_type) +{ + ntfs_dir_state *dir = STATE(dirState); + ntfs_dir_entry *entry = NULL; + char *entry_name = NULL; + + // Sanity check + if (!dir || !dir->vd) { + errno = EINVAL; + return -1; + } + + // Ignore DOS file names + if (name_type == FILE_NAME_DOS) { + return 0; + } + + // Preliminary check that this entry can be enumerated (as described by the volume descriptor) + if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || dir->vd->showSystemFiles) { + + // Convert the entry name to our current local + if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) { + return -1; + } + + if(dir->first && dir->first->mref == FILE_root && + MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0) + { + return 0; + } + + // If this is not the parent or self directory reference + if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) { + + // Open the entry + ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name); + if (!ni) + return -1; + + // Double check that this entry can be emuerated (as described by the volume descriptor) + if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) || + ((ni->flags & FILE_ATTR_SYSTEM) && !dir->vd->showSystemFiles)) { + ntfs_inode_close(ni); + return 0; + } + + // Close the entry + ntfs_inode_close(ni); + + } + + // Allocate a new directory entry + entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry)); + if (!entry) + return -1; + + // Setup the entry + entry->name = entry_name; + entry->next = NULL; + entry->mref = MREF(mref); + + // Link the entry to the directory + if (!dir->first) { + dir->first = entry; + } else { + ntfs_dir_entry *last = dir->first; + while (last->next) last = last->next; + last->next = entry; + } + + } + + return 0; +} + +DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + ntfs_log_trace("dirState %p, path %s\n", dirState, path); + + ntfs_dir_state* dir = STATE(dirState); + s64 position = 0; + + // Get the volume descriptor for this path + dir->vd = ntfsGetVolume(path); + if (!dir->vd) { + r->_errno = ENODEV; + return NULL; + } + + // Lock + ntfsLock(dir->vd); + + // Find the directory + dir->ni = ntfsOpenEntry(dir->vd, path); + if (!dir->ni) { + ntfsUnlock(dir->vd); + r->_errno = ENOENT; + return NULL; + } + + // Ensure that this directory is indeed a directory + if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(dir->vd, dir->ni); + ntfsUnlock(dir->vd); + r->_errno = ENOTDIR; + return NULL; + } + + // Read the directory + dir->first = dir->current = NULL; + if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) { + ntfsCloseDir(dir); + ntfsUnlock(dir->vd); + r->_errno = errno; + return NULL; + } + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Insert the directory into the double-linked FILO list of open directories + if (dir->vd->firstOpenDir) { + dir->nextOpenDir = dir->vd->firstOpenDir; + dir->vd->firstOpenDir->prevOpenDir = dir; + } else { + dir->nextOpenDir = NULL; + } + dir->prevOpenDir = NULL; + dir->vd->cwd_ni = dir->ni; + dir->vd->firstOpenDir = dir; + dir->vd->openDirCount++; + + // Unlock + ntfsUnlock(dir->vd); + + return dirState; +} + +int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + ntfs_log_trace("dirState %p\n", dirState); + + ntfs_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} + +int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); + + ntfs_dir_state* dir = STATE(dirState); + ntfs_inode *ni = NULL; + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Check that there is a entry waiting to be fetched + if (!dir->current) { + ntfsUnlock(dir->vd); + r->_errno = ENOENT; + return -1; + } + + // Fetch the current entry + strcpy(filename, dir->current->name); + if(filestat != NULL) + { + if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) + { + memset(filestat, 0, sizeof(struct stat)); + filestat->st_mode = S_IFDIR; + } + else + { + ni = ntfsOpenEntry(dir->vd, dir->current->name); + if (ni) { + ntfsStat(dir->vd, ni, filestat); + ntfsCloseEntry(dir->vd, ni); + } + } + } + + // Move to the next entry in the directory + dir->current = dir->current->next; + + // Update directory times + ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} + +int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + ntfs_log_trace("dirState %p\n", dirState); + + ntfs_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(dir->vd); + + // Close the directory + ntfsCloseDir(dir); + + // Remove the directory from the double-linked FILO list of open directories + dir->vd->openDirCount--; + if (dir->nextOpenDir) + dir->nextOpenDir->prevOpenDir = dir->prevOpenDir; + if (dir->prevOpenDir) + dir->prevOpenDir->nextOpenDir = dir->nextOpenDir; + else + dir->vd->firstOpenDir = dir->nextOpenDir; + + // Unlock + ntfsUnlock(dir->vd); + + return 0; +} diff --git a/lib/libntfs/src/source/ntfsdir.h b/lib/libntfs/src/source/ntfsdir.h new file mode 100644 index 0000000..0550592 --- /dev/null +++ b/lib/libntfs/src/source/ntfsdir.h @@ -0,0 +1,68 @@ +/** + * ntfs_dir.c - devoptab directory routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSDIR_H +#define _NTFSDIR_H + +#include "ntfsinternal.h" +#include + +/** + * ntfs_dir_entry - Directory entry + */ +typedef struct _ntfs_dir_entry { + char *name; + u64 mref; + struct _ntfs_dir_entry *next; +} ntfs_dir_entry; + +/** + * ntfs_dir_state - Directory state + */ +typedef struct _ntfs_dir_state { + ntfs_vd *vd; /* Volume this directory belongs to */ + ntfs_inode *ni; /* Directory descriptor */ + ntfs_dir_entry *first; /* The first entry in the directory */ + ntfs_dir_entry *current; /* The current entry in the directory */ + struct _ntfs_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */ + struct _ntfs_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */ +} ntfs_dir_state; + +/* Directory state routines */ +void ntfsCloseDir (ntfs_dir_state *file); + +/* Gekko devoptab directory routines for NTFS-based devices */ +extern int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st); +extern int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink); +extern int ntfs_unlink_r (struct _reent *r, const char *name); +extern int ntfs_chdir_r (struct _reent *r, const char *name); +extern int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName); +extern int ntfs_mkdir_r (struct _reent *r, const char *path, int mode); +extern int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* Gekko devoptab directory walking routines for NTFS-based devices */ +extern DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path); +extern int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState); + +#endif /* _NTFSDIR_H */ + diff --git a/lib/libntfs/src/source/ntfsfile.c b/lib/libntfs/src/source/ntfsfile.c new file mode 100644 index 0000000..f361812 --- /dev/null +++ b/lib/libntfs/src/source/ntfsfile.c @@ -0,0 +1,526 @@ +/** + * ntfsfile.c - devoptab file routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsfile.h" + +#define STATE(x) ((ntfs_file_state*)x) + +void ntfsCloseFile (ntfs_file_state *file) +{ + // Sanity check + if (!file || !file->vd) + return; + + // Special case fix ups for compressed and/or encrypted files + if (file->compressed) + ntfs_attr_pclose(file->data_na); +#ifdef HAVE_SETXATTR + if (file->encrypted) + ntfs_efs_fixup_attribute(NULL, file->data_na); +#endif + // Close the file data attribute (if open) + if (file->data_na) + ntfs_attr_close(file->data_na); + + // Sync the file (and its attributes) to disc + if(file->write) + { + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); + ntfsSync(file->vd, file->ni); + } + + if (file->read) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + + // Close the file (if open) + if (file->ni) + ntfsCloseEntry(file->vd, file->ni); + + // Reset the file state + file->ni = NULL; + file->data_na = NULL; + file->flags = 0; + file->read = false; + file->write = false; + file->append = false; + file->pos = 0; + file->len = 0; + + return; +} + +int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", (void *) fileStruct, path, flags, mode); + + ntfs_file_state* file = STATE(fileStruct); + + // Get the volume descriptor for this path + file->vd = ntfsGetVolume(path); + if (!file->vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Determine which mode the file is opened for + file->flags = flags; + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + } else { + r->_errno = EACCES; + ntfsUnlock(file->vd); + return -1; + } + + // Try and find the file and (if found) ensure that it is not a directory + file->ni = ntfsOpenEntry(file->vd, path); + if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EISDIR; + return -1; + } + + // Are we creating this file? + if ((flags & O_CREAT) && !file->ni) { + + // Create the file + file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); + if (!file->ni) { + ntfsUnlock(file->vd); + return -1; + } + + } + + // Sanity check, the file should be open by now + if (!file->ni) { + ntfsUnlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + // Open the files data attribute + file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); + if(!file->data_na) { + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + return -1; + } + + // Determine if this files data is compressed and/or encrypted + file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); + file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); + + // We cannot read/write encrypted files + if (file->encrypted) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // Make sure we aren't trying to write to a read-only file + if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = EROFS; + return -1; + } + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write) { + if (ntfs_attr_truncate(file->data_na, 0)) { + ntfs_attr_close(file->data_na); + ntfsCloseEntry(file->vd, file->ni); + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } + + // Set the files current position and length + file->pos = 0; + file->len = file->data_na->data_size; + + ntfs_log_trace("file->len %llu\n", file->len); + + // Update file times + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + + // Insert the file into the double-linked FILO list of open files + if (file->vd->firstOpenFile) { + file->nextOpenFile = file->vd->firstOpenFile; + file->vd->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + file->vd->firstOpenFile = file; + file->vd->openFileCount++; + + // Unlock + ntfsUnlock(file->vd); + + return (int)fileStruct; +} + +int ntfs_close_r (struct _reent *r, int fd) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Close the file + ntfsCloseFile(file); + + // Remove the file from the double-linked FILO list of open files + file->vd->openFileCount--; + if (file->nextOpenFile) + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + if (file->prevOpenFile) + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + else + file->vd->firstOpenFile = file->nextOpenFile; + + // Unlock + ntfsUnlock(file->vd); + + return 0; +} + +ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) +{ + ntfs_log_trace("fd %p, ptr %p, len %u\n", (void *) fd, ptr, len); + + ntfs_file_state* file = STATE(fd); + ssize_t written = 0; + off_t old_pos = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // If we are in append mode, backup the current position and move to the end of the file + if (file->append) { + old_pos = file->pos; + file->pos = file->len; + } + + // Write to the files data atrribute + while (len) { + ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); + if (ret <= 0) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + len -= ret; + file->pos += ret; + written += ret; + } + + // If we are in append mode, restore the current position to were it was prior to this write + if (file->append) { + file->pos = old_pos; + } + + // Mark the file for archiving (if we actually wrote something) + if (written) + file->ni->flags |= FILE_ATTR_ARCHIVE; + + // Update the files data length + file->len = file->data_na->data_size; + + // Unlock + ntfsUnlock(file->vd); + + return written; +} + +ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + ntfs_log_trace("fd %p, ptr %p, len %u\n", (void *) fd, ptr, len); + + ntfs_file_state* file = STATE(fd); + ssize_t read = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to read from this file + if (!file->read) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // Don't read past the end of file + if (file->pos + len > file->len) { + r->_errno = EOVERFLOW; + len = file->len - file->pos; + ntfs_log_trace("EOVERFLOW"); + } + + ntfs_log_trace("file->pos:%d, len:%d, file->len:%d \n", (u32)file->pos, (u32)len, (u32)file->len); + + // Read from the files data attribute + while (len) { + ssize_t ret = ntfs_attr_pread(file->data_na, file->pos, len, ptr); + if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + ptr += ret; + len -= ret; + file->pos += ret; + read += ret; + } + //ntfs_log_trace("file->pos: %d \n", (u32)file->pos); + // Update file times (if we actually read something) + + // Unlock + ntfsUnlock(file->vd); + + return read; +} + +off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + ntfs_log_trace("fd %p, pos %llu, dir %i\n", (void *) fd, pos, dir); + + ntfs_file_state* file = STATE(fd); + off_t position = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Set the files current position + switch(dir) { + case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break; + case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break; + case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break; + } + + // Unlock + ntfsUnlock(file->vd); + + return position; +} +int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Get the file stats + ret = ntfsStat(file->vd, file->ni, st); + if (ret) + r->_errno = errno; + + return ret; +} + +int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len) +{ + ntfs_log_trace("fd %p, len %llu\n", (void *) fd, (u64) len); + + ntfs_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ntfsUnlock(file->vd); + r->_errno = EACCES; + return -1; + } + + // For compressed files, only deleting and expanding contents are implemented + if (file->compressed && + len > 0 && + len < file->data_na->initialized_size) { + ntfsUnlock(file->vd); + r->_errno = EOPNOTSUPP; + return -1; + } + + // Resize the files data attribute, either by expanding or truncating + if (file->compressed && len > file->data_na->initialized_size) { + char zero = 0; + if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } else { + if (ntfs_attr_truncate(file->data_na, len)) { + ntfsUnlock(file->vd); + r->_errno = errno; + return -1; + } + } + + // Mark the file for archiving (if we actually changed something) + if (file->len != file->data_na->data_size) + file->ni->flags |= FILE_ATTR_ARCHIVE; + + // Update file times (if we actually changed something) + if (file->len != file->data_na->data_size) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); + + // Update the files data length + file->len = file->data_na->data_size; + + // Sync the file (and its attributes) to disc + ntfsSync(file->vd, file->ni); + + // Unlock + ntfsUnlock(file->vd); + + return 0; +} + +int ntfs_fsync_r (struct _reent *r, int fd) +{ + ntfs_log_trace("fd %p\n", (void *) fd); + + ntfs_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(file->vd); + + // Sync the file (and its attributes) to disc + ret = ntfsSync(file->vd, file->ni); + if (ret) + r->_errno = errno; + + // Unlock + ntfsUnlock(file->vd); + + return ret; +} diff --git a/lib/libntfs/src/source/ntfsfile.h b/lib/libntfs/src/source/ntfsfile.h new file mode 100644 index 0000000..8e5ffcc --- /dev/null +++ b/lib/libntfs/src/source/ntfsfile.h @@ -0,0 +1,65 @@ +/** + * ntfsfile.c - devoptab file routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSFILE_H +#define _NTFSFILE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ntfsinternal.h" +#include + +/** + * ntfs_file_state - File state + */ +typedef struct _ntfs_file_state { + ntfs_vd *vd; /* Volume this file belongs to */ + ntfs_inode *ni; /* File descriptor */ + ntfs_attr *data_na; /* File data descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ + bool compressed; /* True if file data is compressed */ + bool encrypted; /* True if file data is encryted */ + off_t pos; /* Current position within the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ + struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} ntfs_file_state; + +/* File state routines */ +void ntfsCloseFile (ntfs_file_state *file); + +/* Gekko devoptab file routines for NTFS-based devices */ +extern int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +extern int ntfs_close_r (struct _reent *r, int fd); +extern ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len); +extern ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len); +extern off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir); +extern int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st); +extern int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len); +extern int ntfs_fsync_r (struct _reent *r, int fd); + +#endif /* _NTFSFILE_H */ + diff --git a/lib/libntfs/src/source/ntfsfile_frag.c b/lib/libntfs/src/source/ntfsfile_frag.c new file mode 100644 index 0000000..b93a5fb --- /dev/null +++ b/lib/libntfs/src/source/ntfsfile_frag.c @@ -0,0 +1,121 @@ +/** + * ntfsfile.c - devoptab file routines for NTFS-based devices. + * Copyright (c) 2010 Miigotu + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsfile.h" +#include "ntfs.h" +#include "ntfs_frag.h" + +s64 ntfs_attr_getfragments(ntfs_attr *na, const s64 pos, s64 count, u64 offset, + _ntfs_frag_append_t append_fragment, void *callback_data); + +static inline u8 size_to_shift(u32 size) +{ + u8 ret = 0; + while (size) + { + ret++; + size >>= 1; + } + return ret - 1; +} + +int _NTFS_get_fragments (const char *path, + _ntfs_frag_append_t append_fragment, void *callback_data) +{ + struct _reent r; + ntfs_file_state file_st, *file = &file_st; + ssize_t read = 0; + int ret_val = -11; + + // Open File + r._errno = 0; + int fd = ntfs_open_r(&r, file, path, O_RDONLY, 0); + if (fd != (int)file) return -12; + + + // Sanity check + if (!file || !file->vd || !file->ni || !file->data_na) { + //r->_errno = EINVAL; + return -13; + } + + // Lock + ntfsLock(file->vd); + + + u64 offset = 0; + u64 len = file->len; + + // Read from the files data attribute + while (len) { + s64 ret = ntfs_attr_getfragments(file->data_na, file->pos, + len, offset, append_fragment, callback_data); + if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); + //r->_errno = errno; + ret_val = -14; + if (ret < 0) ret_val = ret; + goto out; + } + offset += ret; + len -= ret; + file->pos += ret; + read += ret; + } + + u32 shift = size_to_shift(file->ni->vol->sector_size); + + // set file size + append_fragment(callback_data, file->len >> shift, 0, 0); + // success + ret_val = 0; + + +out: + // Unlock + ntfsUnlock(file->vd); + // Close the file + ntfs_close_r (&r, fd); + + return ret_val; +} + diff --git a/lib/libntfs/src/source/ntfsinternal.c b/lib/libntfs/src/source/ntfsinternal.c new file mode 100644 index 0000000..c2de094 --- /dev/null +++ b/lib/libntfs/src/source/ntfsinternal.c @@ -0,0 +1,868 @@ +/** + * ntfsinternal.h - Internal support routines for NTFS-based devices. + * + * Copyright (c) 2010 Dimok + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ntfsinternal.h" +#include "ntfsdir.h" +#include "ntfsfile.h" + +#if defined(__wii__) +#include +#include +#include + +const INTERFACE_ID ntfs_disc_interfaces[] = { + { "sd", &__io_wiisd }, + { "usb", &__io_usbstorage }, + { "carda", &__io_gcsda }, + { "cardb", &__io_gcsdb }, + { NULL, NULL } +}; + +#elif defined(__gamecube__) +#include + +const INTERFACE_ID ntfs_disc_interfaces[] = { + { "carda", &__io_gcsda }, + { "cardb", &__io_gcsdb }, + { NULL, NULL } +}; + +#endif + +int ntfsAddDevice (const char *name, void *deviceData) +{ + const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); + devoptab_t *dev = NULL; + char *devname = NULL; + int i; + + // Sanity check + if (!name || !deviceData || !devoptab_ntfs) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return false; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // Setup the devoptab + memcpy(dev, devoptab_ntfs, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = deviceData; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 0; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0] && i != 0) { + devoptab_list[i] = dev; + return 0; + } + } + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +void ntfsRemoveDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + ntfs_free((devoptab_t*)devoptab); + break; + } + } + } + + return; +} + +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return devoptab; + } + } + } + + // If we reach here then we couldn't find the device name, + // chances are that this path has no device name in it. + // Call GetDeviceOpTab to get our default device (chdir). + if (useDefaultDevice) + return GetDeviceOpTab(""); + + return NULL; +} + +const INTERFACE_ID *ntfsGetDiscInterfaces (void) +{ + // Get all know disc interfaces on the host system + return ntfs_disc_interfaces; +} + +ntfs_vd *ntfsGetVolume (const char *path) +{ + // Get the volume descriptor from the paths associated devoptab (if found) + const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab(); + const devoptab_t *devoptab = ntfsGetDevice(path, true); + if (devoptab && devoptab_ntfs && (devoptab->open_r == devoptab_ntfs->open_r)) + return (ntfs_vd*)devoptab->deviceData; + + return NULL; +} + +int ntfsInitVolume (ntfs_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Initialise the volume lock + LWP_MutexInit(&vd->lock, false); + + // Reset the volumes name cache + vd->name[0] = '\0'; + + // Reset the volumes current directory + vd->cwd_ni = NULL; + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + return 0; +} + +void ntfsDeinitVolume (ntfs_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ntfsLock(vd); + + // Close any directories which are still open (lazy programmers!) + ntfs_dir_state *nextDir = vd->firstOpenDir; + while (nextDir) { + ntfs_log_warning("Cleaning up orphaned directory @ %p\n", nextDir); + ntfsCloseDir(nextDir); + nextDir = nextDir->nextOpenDir; + } + + // Close any files which are still open (lazy programmers!) + ntfs_file_state *nextFile = vd->firstOpenFile; + while (nextFile) { + ntfs_log_warning("Cleaning up orphaned file @ %p\n", nextFile); + ntfsCloseFile(nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + // Close the volumes current directory (if any) + //if (vd->cwd_ni) { + //ntfsCloseEntry(vd, vd->cwd_ni); + //vd->cwd_ni = NULL; + //} + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // Unlock + ntfsUnlock(vd); + + // Deinitialise the volume lock + LWP_MutexDestroy(vd->lock); + + return; +} + +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path) +{ + return ntfsParseEntry(vd, path, 1); +} + +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel) +{ + ntfs_inode *ni = NULL; + char *target = NULL; + int attr_size; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // Get the actual path of the entry + path = ntfsRealPath(path); + if (!path) { + errno = EINVAL; + return NULL; + } else if (path[0] == '\0') { + path = "."; + } + + // Find the entry, taking into account our current directory (if any) + if (path[0] != PATH_SEP) + ni = ntfs_pathname_to_inode(vd->vol, vd->cwd_ni, path++); + else + ni = ntfs_pathname_to_inode(vd->vol, NULL, path); + + // If the entry was found and it has reparse data then parse its true path; + // this resolves the true location of symbolic links and directory junctions + if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) { + if (ntfs_possible_symlink(ni)) { + + // Sanity check, give up if we are parsing to deep + if (reparseLevel > NTFS_MAX_SYMLINK_DEPTH) { + ntfsCloseEntry(vd, ni); + errno = ELOOP; + return NULL; + } + + // Get the target path of this entry + target = ntfs_make_symlink(ni, path, &attr_size); + if (!target) { + ntfsCloseEntry(vd, ni); + return NULL; + } + + // Close the entry (we are no longer interested in it) + ntfsCloseEntry(vd, ni); + + // Parse the entries target + ni = ntfsParseEntry(vd, target, reparseLevel++); + + // Clean up + // use free because the value was not allocated with ntfs_alloc + free(target); + + } + } + + return ni; +} + +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ntfsLock(vd); + + // Sync the entry (if it is dirty) + if (NInoDirty(ni)) + ntfsSync(vd, ni); + + // Close the entry + ntfs_inode_close(ni); + + // Unlock + ntfsUnlock(vd); + + return; +} + + +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL, *utarget = NULL; + int uname_len, utarget_len; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // You cannot link between devices + if(target) { + if(vd != ntfsGetVolume(target)) { + errno = EXDEV; + return NULL; + } + } + + // Get the actual paths of the entry + path = ntfsRealPath(path); + target = ntfsRealPath(target); + if (!path) { + errno = EINVAL; + return NULL; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible, clean it up + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if(name) + { + name++; + name[0] = 0; + } + + // Open the entries parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + goto cleanup; + } + + // Create the entry + switch (type) { + + // Symbolic link + case S_IFLNK: + if (!target) { + errno = EINVAL; + goto cleanup; + } + utarget_len = ntfsLocalToUnicode(target, &utarget); + if (utarget_len < 0) { + errno = EINVAL; + goto cleanup; + } + ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); + break; + + // Directory or file + case S_IFDIR: + case S_IFREG: + ni = ntfs_create(dir_ni, 0, uname, uname_len, type); + break; + + } + + // If the entry was created + if (ni) { + + // Mark the entry for archiving + ni->flags |= FILE_ATTR_ARCHIVE; + + // Mark the entry as dirty + NInoSetDirty(ni); + + // Sync the entry to disc + ntfsSync(vd, ni); + + // Update parent directories times + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); + + } + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + // use free because the value was not allocated with ntfs_alloc + if(utarget) + free(utarget); + + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return ni; +} + +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL; + int uname_len; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // You cannot link between devices + if(vd != ntfsGetVolume(new_path)) { + errno = EXDEV; + return -1; + } + + // Get the actual paths of the entry + old_path = ntfsRealPath(old_path); + new_path = ntfsRealPath(new_path); + if (!old_path || !new_path) { + errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible, clean it up + dir = strdup(new_path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + *name = 0; + + // Find the entry + ni = ntfsOpenEntry(vd, old_path); + if (!ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Open the entries new parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Link the entry to its new parent + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -1; + goto cleanup; + } + + // Update entry times + ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); + + // Sync the entry to disc + ntfsSync(vd, ni); + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + if(ni) + ntfsCloseEntry(vd, ni); + + // use free because the value was not allocated with ntfs_alloc + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return res; +} + +int ntfsUnlink (ntfs_vd *vd, const char *path) +{ + ntfs_inode *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + ntfschar *uname = NULL; + int uname_len; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Get the actual path of the entry + path = ntfsRealPath(path); + if (!path) { + errno = EINVAL; + return -1; + } + + // Lock + ntfsLock(vd); + + // Get the unicode name for the entry and find its parent directory + // TODO: This looks horrible + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if (name) + name++; + else + name = dir; + uname_len = ntfsLocalToUnicode(name, &uname); + if (uname_len < 0) { + errno = EINVAL; + goto cleanup; + } + name = strrchr(dir, '/'); + if(name) + { + name++; + name[0] = 0; + } + + // Find the entry + ni = ntfsOpenEntry(vd, path); + if (!ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Open the entries parent directory + dir_ni = ntfsOpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + res = -1; + goto cleanup; + } + + // Unlink the entry from its parent + if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) { + res = -1; + } + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore + dir_ni = ni = NULL; + +cleanup: + + if(dir_ni) + ntfsCloseEntry(vd, dir_ni); + + if(ni) + ntfsCloseEntry(vd, ni); + + // use free because the value was not allocated with ntfs_alloc + if(uname) + free(uname); + + if(dir) + ntfs_free(dir); + + // Unlock + ntfsUnlock(vd); + + return 0; +} + +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni) +{ + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Lock + ntfsLock(vd); + + // Sync the entry + res = ntfs_inode_sync(ni); + + // Force the underlying device to sync + ntfs_device_sync(vd->dev); + + // Unlock + ntfsUnlock(vd); + + return res; + +} + +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st) +{ + ntfs_attr *na = NULL; + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Lock + ntfsLock(vd); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + // Is this entry a directory + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + st->st_mode = S_IFDIR | (0777 & ~vd->dmask); + st->st_nlink = 1; + + // Open the directories index allocation table attribute + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (na) { + st->st_size = na->data_size; + st->st_blocks = na->allocated_size >> 9; + ntfs_attr_close(na); + } + + // Else it must be a file + } else { + st->st_mode = S_IFREG | (0777 & ~vd->fmask); + st->st_size = ni->data_size; + st->st_blocks = (ni->allocated_size + 511) >> 9; + st->st_nlink = le16_to_cpu(ni->mrec->link_count); + } + + // Fill in the generic entry stats + st->st_dev = vd->id; + st->st_uid = vd->uid; + st->st_gid = vd->gid; + st->st_ino = ni->mft_no; + st->st_atime = ni->last_access_time; + st->st_ctime = ni->last_mft_change_time; + st->st_mtime = ni->last_data_change_time; + + // Update entry times + ntfsUpdateTimes(vd, ni, NTFS_UPDATE_ATIME); + + // Unlock + ntfsUnlock(vd); + + return res; +} + +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask) +{ + // Run the access time update strategy against the device driver settings first + if (vd && vd->atime == ATIME_DISABLED) + mask &= ~NTFS_UPDATE_ATIME; + + // Update entry times + if (ni && mask) + ntfs_inode_update_times(ni, mask); + + return; +} + +const char *ntfsRealPath (const char *path) +{ + // Sanity check + if (!path) + return NULL; + + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; + } + if (strchr(path, ':') != NULL) { + return NULL; + } + + return path; +} + +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len) +{ + int len = 0; + int i; + + // Sanity check + if (!ins || !ins_len || !outs) + return 0; + + char * ucstombs_out = NULL; + + // Convert the unicode string to our current local + len = ntfs_ucstombs(ins, ins_len, &ucstombs_out, outs_len); + + if(ucstombs_out) + { + //use proper allocation + *outs = (char *) ntfs_alloc(strlen(ucstombs_out) + 1); + if(!*outs) + { + errno = ENOMEM; + return -1; + } + strcpy(*outs, ucstombs_out); + free(ucstombs_out); + ucstombs_out = NULL; + } + + if (len == -1 && errno == EILSEQ) + { + // The string could not be converted to the current local, + // do it manually by replacing non-ASCII characters with underscores + if (!*outs || outs_len >= ins_len) + { + if (!*outs) + { + *outs = (char *) ntfs_alloc(ins_len + 1); + if (!*outs) { + errno = ENOMEM; + return -1; + } + } + for (i = 0; i < ins_len; i++) { + ntfschar uc = le16_to_cpu(ins[i]); + if (uc > 0xff) + uc = (ntfschar)'_'; + *outs[i] = (char)uc; + } + *outs[ins_len] = (ntfschar)'\0'; + len = ins_len; + } + } + + return len; +} + +int ntfsLocalToUnicode (const char *ins, ntfschar **outs) +{ + // Sanity check + if (!ins || !outs) + return 0; + + // Convert the local string to unicode + return ntfs_mbstoucs(ins, outs); +} diff --git a/lib/libntfs/src/source/ntfsinternal.h b/lib/libntfs/src/source/ntfsinternal.h new file mode 100644 index 0000000..11dfb8f --- /dev/null +++ b/lib/libntfs/src/source/ntfsinternal.h @@ -0,0 +1,178 @@ +/** + * ntfsinternal.h - Internal support routines for NTFS-based devices. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2006 Michael "Chishm" Chisholm + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSINTERNAL_H +#define _NTFSINTERNAL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "types.h" +#include "compat.h" +#include "logging.h" +#include "layout.h" +#include "device.h" +#include "volume.h" +#include "dir.h" +#include "inode.h" +#include "attrib.h" +#include "reparse.h" +#include "security.h" +#include "efs.h" +#include "unistr.h" + +#include +#include +#include + +#define NTFS_MOUNT_PREFIX "ntfs" /* Device name prefix to use when auto-mounting */ +#define NTFS_MAX_PARTITIONS 32 /* Maximum number of partitions that can be found */ +#define NTFS_MAX_MOUNTS 10 /* Maximum number of mounts available at one time */ +#define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links */ + +#define NTFS_OEM_ID cpu_to_le64(0x202020205346544eULL) /* "NTFS " */ + +#define MBR_SIGNATURE cpu_to_le16(0xAA55) +#define EBR_SIGNATURE cpu_to_le16(0xAA55) + +#define PARTITION_STATUS_NONBOOTABLE 0x00 /* Non-bootable */ +#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */ + +#define PARTITION_TYPE_EMPTY 0x00 /* Empty */ +#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */ +#define PARTITION_TYPE_NTFS 0x07 /* Windows NT NTFS */ +#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */ + +/* Forward declarations */ +struct _ntfs_file_state; +struct _ntfs_dir_state; + +/** + * PRIMARY_PARTITION - Block device partition record + */ +typedef struct _PARTITION_RECORD { + u8 status; /* Partition status; see above */ + u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */ + u8 type; /* Partition type; see above */ + u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */ + u32 lba_start; /* Local block address to first sector of partition */ + u32 block_count; /* Number of blocks in partition */ +} __attribute__((__packed__)) PARTITION_RECORD; + +/** + * MASTER_BOOT_RECORD - Block device master boot record + */ +typedef struct _MASTER_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partitions[4]; /* 4 primary partitions */ + u16 signature; /* MBR signature; 0xAA55 */ +} __attribute__((__packed__)) MASTER_BOOT_RECORD; + +/** + * EXTENDED_PARTITION - Block device extended boot record + */ +typedef struct _EXTENDED_BOOT_RECORD { + u8 code_area[446]; /* Code area; normally empty */ + PARTITION_RECORD partition; /* Primary partition */ + PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */ + u8 reserved[32]; /* Normally empty */ + u16 signature; /* EBR signature; 0xAA55 */ +} __attribute__((__packed__)) EXTENDED_BOOT_RECORD; + +/** + * INTERFACE_ID - Disc interface identifier + */ +typedef struct _INTERFACE_ID { + const char *name; /* Interface name */ + const DISC_INTERFACE *interface; /* Disc interface */ +} INTERFACE_ID; + +/** + * ntfs_atime_t - File access time update strategies + */ +typedef enum { + ATIME_ENABLED, /* Update access times */ + ATIME_DISABLED /* Don't update access times */ +} ntfs_atime_t; + +/** + * ntfs_vd - NTFS volume descriptor + */ +typedef struct _ntfs_vd { + struct ntfs_device *dev; /* NTFS device handle */ + ntfs_volume *vol; /* NTFS volume handle */ + mutex_t lock; /* Volume lock mutex */ + s64 id; /* Filesystem id */ + u32 flags; /* Mount flags */ + char name[128]; /* Volume name (cached) */ + u16 uid; /* User id for entry creation */ + u16 gid; /* Group id for entry creation */ + u16 fmask; /* Unix style permission mask for file creation */ + u16 dmask; /* Unix style permission mask for directory creation */ + ntfs_atime_t atime; /* Entry access time update strategy */ + bool showHiddenFiles; /* If true, show hidden files when enumerating directories */ + bool showSystemFiles; /* If true, show system files when enumerating directories */ + ntfs_inode *cwd_ni; /* Current directory */ + struct _ntfs_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */ + struct _ntfs_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */ + u16 openDirCount; /* The total number of directories currently open in this volume */ + u16 openFileCount; /* The total number of files currently open in this volume */ +} ntfs_vd; + +/* Lock volume */ +static inline void ntfsLock (ntfs_vd *vd) +{ + LWP_MutexLock(vd->lock); +} + +/* Unlock volume */ +static inline void ntfsUnlock (ntfs_vd *vd) +{ + LWP_MutexUnlock(vd->lock); +} + +/* Gekko device related routines */ +int ntfsAddDevice (const char *name, void *deviceData); +void ntfsRemoveDevice (const char *path); +const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice); +const devoptab_t *ntfsGetDevOpTab (void); +const INTERFACE_ID* ntfsGetDiscInterfaces (void); + +/* Miscellaneous helper/support routines */ +int ntfsInitVolume (ntfs_vd *vd); +void ntfsDeinitVolume (ntfs_vd *vd); +ntfs_vd *ntfsGetVolume (const char *path); +ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path); +ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel); +void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni); +ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target); +int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path); +int ntfsUnlink (ntfs_vd *vd, const char *path); +int ntfsSync (ntfs_vd *vd, ntfs_inode *ni); +int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st); +void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask); + +const char *ntfsRealPath (const char *path); +int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len); +int ntfsLocalToUnicode (const char *ins, ntfschar **outs); + +#endif /* _NTFSINTERNAL_H */ diff --git a/lib/libntfs/src/source/ntfstime.h b/lib/libntfs/src/source/ntfstime.h new file mode 100644 index 0000000..21c222d --- /dev/null +++ b/lib/libntfs/src/source/ntfstime.h @@ -0,0 +1,133 @@ +/* + * ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_NTFSTIME_H +#define _NTFS_NTFSTIME_H + +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_GETTIMEOFDAY +#include +#endif + +#include "types.h" + +#ifndef GEKKO +/* + * assume "struct timespec" is not defined if st_mtime is not defined + */ +#if !defined(st_mtime) & !defined(__timespec_defined) +struct timespec { + time_t tv_sec; + long tv_nsec; +} ; +#endif +#endif + +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + +#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) + +/** + * ntfs2timespec - Convert an NTFS time to Unix time + * @ntfs_time: An NTFS time in 100ns units since 1601 + * + * NTFS stores times as the number of 100ns intervals since January 1st 1601 at + * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. + * + * Return: A Unix time (number of seconds since 1970, and nanoseconds) + */ +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) +{ + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); +} + +/** + * timespec2ntfs - Convert Linux time to NTFS time + * @utc_time: Linux time to convert to NTFS + * + * Convert the Linux time @utc_time to its corresponding NTFS time. + * + * Linux stores time in a long at present and measures it as the number of + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value + * + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is + * measured as the number of 100 nano-second intervals since 1st January 1601, + * 00:00:00 UTC. + * + * Return: An NTFS time (100ns units since Jan 1601) + */ +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) +{ + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); +} + +#endif /* _NTFS_NTFSTIME_H */ diff --git a/lib/libntfs/src/source/object_id.c b/lib/libntfs/src/source/object_id.c new file mode 100644 index 0000000..059e882 --- /dev/null +++ b/lib/libntfs/src/source/object_id.c @@ -0,0 +1,639 @@ +/** + * object_id.c - Processing of object ids + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "object_id.h" +#include "logging.h" +#include "misc.h" + +/* + * Endianness considerations + * + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. + * + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. + * + * --------------------- begin from RFC 4122 ---------------------- + * Consider each field of the UUID to be an unsigned integer as shown + * in the table in section Section 4.1.2. Then, to compare a pair of + * UUIDs, arithmetically compare the corresponding fields from each + * UUID in order of significance and according to their data type. + * Two UUIDs are equal if and only if all the corresponding fields + * are equal. + * + * UUIDs, as defined in this document, can also be ordered + * lexicographically. For a pair of UUIDs, the first one follows the + * second if the most significant field in which the UUIDs differ is + * greater for the first UUID. The second precedes the first if the + * most significant field in which the UUIDs differ is greater for + * the second UUID. + * + * The fields are encoded as 16 octets, with the sizes and order of the + * fields defined above, and with each field encoded with the Most + * Significant Byte first (known as network byte order). Note that the + * field names, particularly for multiplexed fields, follow historical + * practice. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_low | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_mid | time_hi_and_version | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |clk_seq_hi_res | clk_seq_low | node (0-1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | node (2-5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ---------------------- end from RFC 4122 ----------------------- + */ + +typedef struct { + union { + /* alignment may be needed to evaluate collations */ + u32 alignment; + GUID guid; + } object_id; +} OBJECT_ID_INDEX_KEY; + +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; +} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA + +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; +} ; + +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for a new object id + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) +{ + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); + + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open the $Extend/$ObjId file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Merge object_id data stored in the index into + * a full object_id struct. + * + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error + */ + +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; + + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove an object id index entry if attribute present + * + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno + */ + +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the object id and index + * + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) +{ + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; + + res = 0; + + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Add a (dummy) object id to an inode if it does not exist + * + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) + */ + +static int add_object_id(ntfs_inode *ni, int flags) +{ + int res; + u8 dummy; + + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete an object_id index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_object_id_index(ntfs_inode *ni) +{ + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs object id into an extended attribute + * + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) + * + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) +{ + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; + + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); +} + +/* + * Set the object id from an extended attribute + * + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; + + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the object id + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; + + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } + + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/src/source/object_id.h b/lib/libntfs/src/source/object_id.h new file mode 100644 index 0000000..31af9fd --- /dev/null +++ b/lib/libntfs/src/source/object_id.h @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); + +int ntfs_delete_object_id_index(ntfs_inode *ni); + +#endif /* OBJECT_ID_H */ diff --git a/lib/libntfs/src/source/param.h b/lib/libntfs/src/source/param.h new file mode 100644 index 0000000..c3675b7 --- /dev/null +++ b/lib/libntfs/src/source/param.h @@ -0,0 +1,119 @@ +/* + * param.h - Parameter values for ntfs-3g + * + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_PARAM_H +#define _NTFS_PARAM_H + +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ +#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ + +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +/* + * Parameters for compression + */ + + /* default option for compression */ +#define DEFAULT_COMPRESSION FALSE + /* (log2 of) number of clusters in a compression block for new files */ +#define STANDARD_COMPRESSION_UNIT 4 + /* maximum cluster size for allowing compression for new files */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +/* + * Use of big write buffers + * + * With small volumes, the cluster allocator may fail to allocate + * enough clusters when the volume is nearly full. At most a run + * can be allocated per bitmap chunk. So, there is a danger when the + * number of chunks (capacity/(32768*clsiz)) is less than the number + * of clusters in the biggest write buffer (131072/clsiz). Hence + * a safe minimal capacity is 4GB + */ + +#define SAFE_CAPACITY_FOR_BIG_WRITES 0x100000000LL + +/* + * Parameters for runlists + */ + + /* only update the final extent of a runlist when appending data */ +#define PARTIAL_RUNLIST_UPDATING 0 + +/* + * Parameters for user and xattr mappings + */ + +#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */ + +/* + * Parameters for path canonicalization + */ + +#define MAPPERNAMELTH 256 + +/* + * Permission checking modes for high level and low level + * + * The choices for high and low lowel are independent, they have + * no effect on the library + * + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security with internal fuse or external + * fuse older than 2.8 + * + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs + * + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs + * + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. + */ + +#define HPERMSCONFIG 1 +#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) +#define LPERMSCONFIG 5 +#else +#define LPERMSCONFIG 3 +#endif + +#endif /* defined _NTFS_PARAM_H */ diff --git a/lib/libntfs/src/source/realpath.c b/lib/libntfs/src/source/realpath.c new file mode 100644 index 0000000..a93bc69 --- /dev/null +++ b/lib/libntfs/src/source/realpath.c @@ -0,0 +1,103 @@ +/* + * realpath.c - realpath() aware of device mapper + * Originated from the util-linux project. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include "param.h" +#include "realpath.h" + +/* If there is no realpath() on the system, provide a dummy one. */ +#ifndef HAVE_REALPATH +char *ntfs_realpath(const char *path, char *resolved_path) +{ + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; +} +#endif + + +#ifdef linux + +/* + * Converts private "dm-N" names to "/dev/mapper/" + * + * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs + * provides the real DM device names in /sys/block//dm/name + */ +static char * +canonicalize_dm_name(const char *ptname, char *canonical) +{ + FILE *f; + size_t sz; + char path[MAPPERNAMELTH + 24]; + char name[MAPPERNAMELTH + 16]; + char *res = NULL; + + snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); + if (!(f = fopen(path, "r"))) + return NULL; + + /* read "\n" from sysfs */ + if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { + name[sz - 1] = '\0'; + snprintf(path, sizeof(path), "/dev/mapper/%s", name); + res = strcpy(canonical, path); + } + fclose(f); + return res; +} + +/* + * Canonicalize a device path + * + * Workaround from "basinilya" for fixing device mapper paths. + * + * Background (Phillip Susi, 2011-04-09) + * - ntfs-3g canonicalizes the device name so that if you mount with + * /dev/mapper/foo, the device name listed in mtab is /dev/dm-n, + * so you can not umount /dev/mapper/foo + * - umount won't even recognize and translate /dev/dm-n to the mount + * point, apparently because of the '-' involved. Editing mtab and + * removing the '-' allows you to umount /dev/dmn successfully. + * + * This code restores the devmapper name after canonicalization, + * until a proper fix is implemented. + */ + +char *ntfs_realpath_canonicalize(const char *path, char *canonical) +{ + char *p; + + if (path == NULL) + return NULL; + + if (!ntfs_realpath(path, canonical)) + return NULL; + + p = strrchr(canonical, '/'); + if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { + p = canonicalize_dm_name(p+1, canonical); + if (p) + return p; + } + + return canonical; +} + +#endif diff --git a/lib/libntfs/src/source/realpath.h b/lib/libntfs/src/source/realpath.h new file mode 100644 index 0000000..970d2af --- /dev/null +++ b/lib/libntfs/src/source/realpath.h @@ -0,0 +1,24 @@ +/* + * realpath.h - realpath() aware of device mapper + */ + +#ifndef REALPATH_H +#define REALPATH_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_REALPATH +#define ntfs_realpath realpath +#else +extern char *ntfs_realpath(const char *path, char *resolved_path); +#endif + +#ifdef linux +extern char *ntfs_realpath_canonicalize(const char *path, char *resolved_path); +#else +#define ntfs_realpath_canonicalize ntfs_realpath +#endif + +#endif /* REALPATH_H */ diff --git a/lib/libntfs/src/source/reparse.c b/lib/libntfs/src/source/reparse.c new file mode 100644 index 0000000..05490bf --- /dev/null +++ b/lib/libntfs/src/source/reparse.c @@ -0,0 +1,1227 @@ +/** + * reparse.c - Processing of reparse points + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" +#include "reparse.h" + +/* the definitions in layout.h are wrong, we use names defined in + http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx +*/ + +#define IO_REPARSE_TAG_DFS const_cpu_to_le32(0x8000000A) +#define IO_REPARSE_TAG_DFSR const_cpu_to_le32(0x80000012) +#define IO_REPARSE_TAG_HSM const_cpu_to_le32(0xC0000004) +#define IO_REPARSE_TAG_HSM2 const_cpu_to_le32(0x80000006) +#define IO_REPARSE_TAG_MOUNT_POINT const_cpu_to_le32(0xA0000003) +#define IO_REPARSE_TAG_SIS const_cpu_to_le32(0x80000007) +#define IO_REPARSE_TAG_SYMLINK const_cpu_to_le32(0xA000000C) + +struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + le32 flags; /* 1 for full target, otherwise 0 */ + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ + INDEX_ENTRY_HEADER header; + REPARSE_INDEX_KEY key; + le32 filling; +} ; + +static const ntfschar dir_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\') +} ; + +static const ntfschar vol_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\'), + const_cpu_to_le16('V'), + const_cpu_to_le16('o'), + const_cpu_to_le16('l'), + const_cpu_to_le16('u'), + const_cpu_to_le16('m'), + const_cpu_to_le16('e'), + const_cpu_to_le16('{'), +} ; + +static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('R') }; + +static const char mappingdir[] = ".NTFS-3G/"; + +/* + * Fix a file name with doubtful case in some directory index + * and return the name with the casing used in directory. + * + * Should only be used to translate paths stored with case insensitivity + * (such as directory junctions) when no case conflict is expected. + * If there some ambiguity, the name which collates first is returned. + * + * The name is converted to upper case and searched the usual way. + * The collation rules for file names are such that we should get the + * first candidate if any. + */ + +static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, + int uname_len) +{ + ntfs_volume *vol = dir_ni->vol; + ntfs_index_context *icx; + u64 mref; + le64 lemref; + int lkup; + int olderrno; + int i; + u32 cpuchar; + INDEX_ENTRY *entry; + FILE_NAME_ATTR *found; + struct { + FILE_NAME_ATTR attr; + ntfschar file_name[NTFS_MAX_NAME_LEN + 1]; + } find; + + mref = (u64)-1; /* default return (not found) */ + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + if (uname_len > NTFS_MAX_NAME_LEN) + uname_len = NTFS_MAX_NAME_LEN; + find.attr.file_name_length = uname_len; + for (i=0; iupcase_len) + && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar)) + find.attr.file_name[i] = vol->upcase[cpuchar]; + else + find.attr.file_name[i] = uname[i]; + } + olderrno = errno; + lkup = ntfs_index_lookup((char*)&find, uname_len, icx); + if (errno == ENOENT) + errno = olderrno; + /* + * We generally only get the first matching candidate, + * so we still have to check whether this is a real match + */ + if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END)) + /* get next entry if reaching end of block */ + entry = ntfs_index_next(icx->entry, icx); + else + entry = icx->entry; + if (entry) { + found = &entry->key.file_name; + if (lkup + && ntfs_names_are_equal(find.attr.file_name, + find.attr.file_name_length, + found->file_name, found->file_name_length, + IGNORE_CASE, + vol->upcase, vol->upcase_len)) + lkup = 0; + if (!lkup) { + /* + * name found : + * fix original name and return inode + */ + lemref = entry->indexed_file; + mref = le64_to_cpu(lemref); + if (NVolCaseSensitive(vol) || !vol->locase) { + for (i=0; ifile_name_length; i++) + uname[i] = found->file_name[i]; + } else { + for (i=0; ifile_name_length; i++) + uname[i] = vol->locase[found->file_name[i]]; + } + } + } + ntfs_index_ctx_put(icx); + } + return (mref); +} + +/* + * Search for a directory junction or a symbolic link + * along the target path, with target defined as a full absolute path + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_absolute(ntfs_volume *vol, ntfschar *path, + int count, BOOL isdir) +{ + ntfs_inode *ni; + u64 inum; + char *target; + int start; + int len; + + target = (char*)NULL; /* default return */ + ni = ntfs_inode_open(vol, (MFT_REF)FILE_root); + if (ni) { + start = 0; + do { + len = 0; + while (((start + len) < count) + && (path[start + len] != const_cpu_to_le16('\\'))) + len++; + inum = ntfs_fix_file_name(ni, &path[start], len); + ntfs_inode_close(ni); + ni = (ntfs_inode*)NULL; + if (inum != (u64)-1) { + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + start += len; + if (start < count) + path[start++] = const_cpu_to_le16('/'); + } + } while (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && (start < count)); + if (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)) + if (ntfs_ucstombs(path, count, &target, 0) < 0) { + if (target) { + free(target); + target = (char*)NULL; + } + } + if (ni) + ntfs_inode_close(ni); + } + return (target); +} + +/* + * Search for a symbolic link along the target path, + * with the target defined as a relative path + * + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) +{ + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + int max = 32; /* safety */ + + pos = 0; + ok = TRUE; + curni = ntfs_dir_parent_inode(ni); + while (curni && ok && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + } else { + pos += lth; + if ((ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; + } + } + } + } + } + } + + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; + } + return (target); +} + +/* + * Check whether a drive letter has been defined in .NTFS-3G + * + * Returns 1 if found, + * 0 if not found, + * -1 if there was an error (described by errno) + */ + +static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) +{ + char defines[NTFS_MAX_NAME_LEN + 5]; + char *drive; + int ret; + int sz; + int olderrno; + ntfs_inode *ni; + + ret = -1; + drive = (char*)NULL; + sz = ntfs_ucstombs(&letter, 1, &drive, 0); + if (sz > 0) { + strcpy(defines,mappingdir); + if ((*drive >= 'a') && (*drive <= 'z')) + *drive += 'A' - 'a'; + strcat(defines,drive); + strcat(defines,":"); + olderrno = errno; + ni = ntfs_pathname_to_inode(vol, NULL, defines); + if (ni && !ntfs_inode_close(ni)) + ret = 1; + else + if (errno == ENOENT) { + ret = 0; + /* avoid errno pollution */ + errno = olderrno; + } + } + if (drive) + free(drive); + return (ret); +} + +/* + * Do some sanity checks on reparse data + * + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + * + */ + +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) +{ + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; + + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); +} + +/* + * Check and translate the target of a junction point or + * a full absolute symbolic link. + * + * A full target definition begins with "\??\" or "\\?\" + * + * The fully defined target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a valid directory junction we want \??\x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 7) + && !memcmp(junction,dir_junction_head,8) + && junction[4] + && (junction[5] == const_cpu_to_le16(':')) + && (junction[6] == const_cpu_to_le16('\\'))) + kind = DIR_JUNCTION; + else + /* + * For a valid volume junction we want \\?\Volume{ + * and a final \ (where \ is an individual char) + */ + if ((count >= 12) + && !memcmp(junction,vol_junction_head,22) + && (junction[count-1] == const_cpu_to_le16('\\'))) + kind = VOL_JUNCTION; + else + kind = NO_JUNCTION; + /* + * Directory junction with an explicit path and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume + */ + if ((kind == DIR_JUNCTION) + && (count >= 7) + && junction[7] + && !ntfs_drive_letter(vol, junction[4])) { + target = search_absolute(vol,&junction[7],count - 7, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * Volume junctions or directory junctions with + * target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if (((kind == DIR_JUNCTION) && !fulltarget) + || (kind == VOL_JUNCTION)) { + sz = ntfs_ucstombs(&junction[4], + (kind == VOL_JUNCTION ? count - 5 : count - 4), + &target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of an absolute symbolic link. + * + * An absolute target definition begins with "\" or "x:\" + * + * The absolute target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a full valid path we want x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 3) + && junction[0] + && (junction[1] == const_cpu_to_le16(':')) + && (junction[2] == const_cpu_to_le16('\\'))) + kind = FULL_PATH; + else + /* + * For an absolute path we want an initial \ + */ + if ((count >= 0) + && (junction[0] == const_cpu_to_le16('\\'))) + kind = ABS_PATH; + else + kind = REJECTED_PATH; + /* + * Full path, with a drive letter and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume. + * Do the same for an abs path with no drive letter. + */ + if (((kind == FULL_PATH) + && (count >= 3) + && junction[3] + && !ntfs_drive_letter(vol, junction[0])) + || (kind == ABS_PATH)) { + if (kind == ABS_PATH) + target = search_absolute(vol, &junction[1], + count - 1, isdir); + else + target = search_absolute(vol, &junction[3], + count - 3, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * full path with target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if ((kind == FULL_PATH) && !fulltarget) { + sz = ntfs_ucstombs(&junction[0], + count,&target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of a relative symbolic link. + * + * A relative target definition does not begin with "\" + * + * The original definition of relative target is kept, it is just + * translated to a case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) +{ + char *target; + + target = search_relative(ni,junction,count); + return (target); +} + +/* + * Get the target for a junction point or symbolic link + * Should only be called for files or directories with reparse data + * + * returns the target converted to a relative path, or NULL + * if some error occurred, as described by errno + * errno is EOPNOTSUPP if the reparse point is not a valid + * symbolic link or directory junction + */ + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size) +{ + s64 attr_size = 0; + char *target; + unsigned int offs; + unsigned int lth; + ntfs_volume *vol; + REPARSE_POINT *reparse_attr; + struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + struct SYMLINK_REPARSE_DATA *symlink_data; + enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; + ntfschar *p; + BOOL bad; + BOOL isdir; + + target = (char*)NULL; + bad = TRUE; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + vol = ni->vol; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + p = (ntfschar*)&symlink_data->path_buffer[offs]; + /* + * Predetermine the kind of target, + * the called function has to make a full check + */ + if (*p++ == const_cpu_to_le16('\\')) { + if ((*p == const_cpu_to_le16('?')) + || (*p == const_cpu_to_le16('\\'))) + kind = FULL_TARGET; + else + kind = ABS_TARGET; + } else + if (*p == const_cpu_to_le16(':')) + kind = ABS_TARGET; + else + kind = REL_TARGET; + p--; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; + } + break; + } + free(reparse_attr); + } + *pattr_size = attr_size; + if (bad) + errno = EOPNOTSUPP; + return (target); +} + +/* + * Check whether a reparse point looks like a junction point + * or a symbolic link. + * Should only be called for files or directories with reparse data + * + * The validity of the target is not checked. + */ + +BOOL ntfs_possible_symlink(ntfs_inode *ni) +{ + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + BOOL possible; + + possible = FALSE; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + case IO_REPARSE_TAG_SYMLINK : + possible = TRUE; + default : ; + } + free(reparse_attr); + } + return (possible); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for new reparse data + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, + le32 reparse_tag) +{ + struct REPARSE_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(REPARSE_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16(0); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct REPARSE_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(REPARSE_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = const_cpu_to_le32(0); + ntfs_index_ctx_reinit(xr); + return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove a reparse data index entry if attribute present + * + * Returns the size of existing reparse data + * (the existing reparse tag is returned) + * -1 if failure, explained by errno + */ + +static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, + le32 *preparse_tag) +{ + REPARSE_INDEX_KEY key; + u64 file_id_cpu; + le64 file_id; + s64 size; + le16 seqn; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing reparse_tag */ + size = ntfs_attr_pread(na, 0, 4, preparse_tag); + if (size == 4) { + seqn = na->ni->mrec->sequence_number; + file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +/* + * Open the $Extend/$Reparse file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_reparse_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xr; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); + if (!xr) { + ntfs_inode_close(ni); + } + } else + xr = (ntfs_index_context*)NULL; + return (xr); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the reparse data and index + * + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, + const char *value, size_t size) +{ + int res; + int written; + int oldsize; + ntfs_attr *na; + le32 reparse_tag; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* remove the existing reparse data */ + oldsize = remove_reparse_index(na,xr,&reparse_tag); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + if (!res + && set_reparse_index(ni,xr, + ((const REPARSE_POINT*)value)->reparse_tag) + && (oldsize > 0)) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index reparse data." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete a reparse index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_reparse_index(ntfs_inode *ni) +{ + ntfs_index_context *xr; + ntfs_inode *xrni; + ntfs_attr *na; + le32 reparse_tag; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr,&reparse_tag) < 0) + res = -1; + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs reparse data into an extended attribute + * + * Returns the reparse data size + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) +{ + REPARSE_POINT *reparse_attr; + s64 attr_size; + + attr_size = 0; /* default to no data and no error */ + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,reparse_attr, + attr_size); + else + errno = EINVAL; + } + free(reparse_attr); + } + } else + errno = ENODATA; + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Set the reparse data from an extended attribute + * + * Warning : the new data is not checked + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + int res; + u8 dummy; + ntfs_inode *xrni; + ntfs_index_context *xr; + + res = 0; + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + xr = open_reparse_index(ni->vol); + if (xr) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, + AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, + AT_REPARSE_POINT, + AT_UNNAMED,0,&dummy, + (s64)0); + if (!res) { + ni->flags |= + FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } else { + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; + } + } + if (!res) { + /* update value and index */ + res = update_reparse_data(ni,xr,value,size); + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the reparse data + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xrni; + ntfs_index_context *xr; + le32 reparse_tag; + + res = 0; + if (ni) { + /* + * open and delete the reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + AT_UNNAMED,0); + if (na) { + /* first remove index (reparse data needed) */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr, + &reparse_tag) < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (!res) { + ni->flags &= + ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } else { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_reparse_index(ni, xr, + reparse_tag); + ntfs_log_error( + "Failed to remove reparse data." + " Possible corruption.\n"); + } + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/src/source/reparse.h b/lib/libntfs/src/source/reparse.h new file mode 100644 index 0000000..35f4aa4 --- /dev/null +++ b/lib/libntfs/src/source/reparse.h @@ -0,0 +1,39 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * This program is free software; you can 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef REPARSE_H +#define REPARSE_H + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); +BOOL ntfs_possible_symlink(ntfs_inode *ni); + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); + +int ntfs_delete_reparse_index(ntfs_inode *ni); + +#endif /* REPARSE_H */ diff --git a/lib/libntfs/src/source/runlist.c b/lib/libntfs/src/source/runlist.c new file mode 100644 index 0000000..7e158d4 --- /dev/null +++ b/lib/libntfs/src/source/runlist.c @@ -0,0 +1,2174 @@ +/** + * runlist.c - Run list handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "layout.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_rl_mm - runlist memmove + * @base: + * @dst: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mm(runlist_element *base, int dst, int src, int size) +{ + if ((dst != src) && (size > 0)) + memmove(base + dst, base + src, size * sizeof(*base)); +} + +/** + * ntfs_rl_mc - runlist memory copy + * @dstbase: + * @dst: + * @srcbase: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mc(runlist_element *dstbase, int dst, + runlist_element *srcbase, int src, int size) +{ + if (size > 0) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); +} + +/** + * ntfs_rl_realloc - Reallocate memory for runlists + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for + * + * As the runlists grow, more memory will be required. To prevent large + * numbers of small reallocations of memory, this function returns a 4kiB block + * of memory. + * + * N.B. If the new allocation doesn't require a different number of 4kiB + * blocks in memory, the function will return the original pointer. + * + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return NULL with errno set to the error code. + */ +static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, + int new_size) +{ + old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + if (old_size == new_size) + return rl; + return realloc(rl, new_size); +} + +/* + * Extend a runlist by some entry count + * The runlist may have to be reallocated + * + * Returns the reallocated runlist + * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails + */ + +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) +{ + runlist_element *newrl; + int last; + int irl; + + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else { + na->rl = newrl; + rl = &newrl[irl]; + } + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } + return (rl); +} + +/** + * ntfs_rl_are_mergeable - test if two runlists can be joined together + * @dst: original runlist + * @src: new runlist to test for mergeability with @dst + * + * Test if two runlists can be joined together. For this, their VCNs and LCNs + * must be adjacent. + * + * Return: TRUE Success, the runlists can be merged. + * FALSE Failure, the runlists cannot be merged. + */ +static BOOL ntfs_rl_are_mergeable(runlist_element *dst, runlist_element *src) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " + "pointer!\n"); + return FALSE; + } + + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return TRUE; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) + return FALSE; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return TRUE; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return TRUE; + /* Cannot merge. */ + return FALSE; +} + +/** + * __ntfs_rl_merge - merge two runlists without testing if they can be merged + * @dst: original, destination runlist + * @src: new runlist to merge with @dst + * + * Merge the two runlists, writing into the destination runlist @dst. The + * caller must make sure the runlists can be merged or this will corrupt the + * destination runlist. + */ +static void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) +{ + dst->length += src->length; +} + +/** + * ntfs_rl_append - append a runlist after a given element + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: runlist to be inserted into @dst + * @ssize: number of elements in @src (excluding end marker) + * @loc: append the new runlist @src after this element in @dst + * + * Append the runlist @src after element @loc in @dst. Merge the right end of + * the new runlist, if necessary. Adjust the size of the hole before the + * appended runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL right = FALSE; /* Right end of @src needs merging */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, check if the right hand end needs merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the right hand end, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + + /* marker - First run after the @src runs that have been inserted */ + marker = loc + ssize + 1; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the preceding hole. */ + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + + /* We may have changed the length of the file, so fix the end marker */ + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; + + return dst; +} + +/** + * ntfs_rl_insert - insert a runlist into another + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: insert the new runlist @src before this element in @dst + * + * Insert the runlist @src before element @loc in the runlist @dst. Merge the + * left end of the new runlist, if necessary. Adjust the size of the hole + * after the inserted runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL disc = FALSE; /* Discontinuity between @dst and @src */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "notmapped" run. + */ + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; + + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; + + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); + } + + /* Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlist. + */ + + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); + + /* Adjust the VCN of the first run after the insertion ... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* ... and the length. */ + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; + + /* Writing beyond the end of the file and there's a discontinuity. */ + if (disc) { + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; + } + dst[loc].lcn = LCN_RL_NOT_MAPPED; + } + return dst; +} + +/** + * ntfs_rl_replace - overwrite a runlist element with another runlist + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst to overwrite with @src + * + * Replace the runlist element @dst at @loc with @src. Merge the left and + * right ends of the inserted runlist, if necessary. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, + runlist_element *src, int ssize, + int loc) +{ + signed delta; + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL right = FALSE; /* Right end of @src needs merging */ + int tail; /* Start of tail of @dst */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + if (loc > 0) + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + /* Allocate some space. We'll need less if the left, right, or both + * ends get merged. The -1 accounts for the run being replaced. + */ + delta = ssize - 1 - left - right; + if (delta > 0) { + dst = ntfs_rl_realloc(dst, dsize, dsize + delta); + if (!dst) + return NULL; + } + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the left and right ends, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * tail - Offset of the tail of @dst + * Nominally: @tail = @loc + 1 (location, skipping the replaced run) + * If "right", then one of @dst's runs is already merged into @src. + */ + tail = loc + right + 1; + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: @marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + */ + marker = loc + ssize - left; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, tail, dsize - tail); + ntfs_rl_mc(dst, loc, src, left, ssize - left); + + /* We may have changed the length of the file, so fix the end marker */ + if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + + return dst; +} + +/** + * ntfs_rl_split - insert a runlist into the centre of a hole + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst at which to split and insert @src + * + * Split the runlist @dst at @loc into two and insert @new in between the two + * fragments. No merging of runlists is necessary. Adjust the size of the + * holes either side. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (!dst) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; + + return dst; +} + + +/** + * ntfs_runlists_merge_i - see ntfs_runlists_merge + */ +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) +{ + int di, si; /* Current index into @[ds]rl. */ + int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ + int dins; /* Index into @drl at which to insert @srl. */ + int dend, send; /* Last index into @[ds]rl. */ + int dfinal, sfinal; /* The last index into @[ds]rl with + lcn >= LCN_HOLE. */ + int marker = 0; + VCN marker_vcn = 0; + + ntfs_log_debug("dst:\n"); + ntfs_debug_runlist_dump(drl); + ntfs_log_debug("src:\n"); + ntfs_debug_runlist_dump(srl); + + /* Check for silly calling... */ + if (!srl) + return drl; + + /* Check for the case where the first mapping is being done now. */ + if (!drl) { + drl = srl; + /* Complete the source runlist if necessary. */ + if (drl[0].vcn) { + /* Scan to the end of the source runlist. */ + for (dend = 0; drl[dend].length; dend++) + ; + dend++; + drl = ntfs_rl_realloc(drl, dend, dend + 1); + if (!drl) + return drl; + /* Insert start element at the front of the runlist. */ + ntfs_rl_mm(drl, 1, 0, dend); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; + } + goto finished; + } + + si = di = 0; + + /* Skip any unmapped start element(s) in the source runlist. */ + while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) + si++; + + /* Can't have an entirely unmapped source runlist. */ + if (!srl[si].length) { + errno = EINVAL; + ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); + return NULL; + } + + /* Record the starting points. */ + sstart = si; + + /* + * Skip forward in @drl until we reach the position where @srl needs to + * be inserted. If we reach the end of @drl, @srl just needs to be + * appended to @drl. + */ + for (; drl[di].length; di++) { + if (drl[di].vcn + drl[di].length > srl[sstart].vcn) + break; + } + dins = di; + + /* Sanity check for illegal overlaps. */ + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + errno = ERANGE; + ntfs_log_perror("Run lists overlap. Cannot merge"); + return NULL; + } + + /* Scan to the end of both runlists in order to know their sizes. */ + for (send = si; srl[send].length; send++) + ; + for (dend = di; drl[dend].length; dend++) + ; + + if (srl[send].lcn == (LCN)LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; + + /* Scan to the last element with lcn >= LCN_HOLE. */ + for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) + ; + for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) + ; + + { + BOOL start; + BOOL finish; + int ds = dend + 1; /* Number of elements in drl & srl */ + int ss = sfinal - sstart + 1; + + start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ + (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ + finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ + ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ + (srl[send - 1].vcn + srl[send - 1].length))); + + /* Or we'll lose an end marker */ + if (finish && !drl[dins].length) + ss++; + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) + finish = FALSE; + + ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); + ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); + ntfs_log_debug("start = %i, finish = %i\n", start, finish); + ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); + + if (start) { + if (finish) + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); + } else { + if (finish) + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); + } + if (!drl) { + ntfs_log_perror("Merge failed"); + return drl; + } + free(srl); + if (marker) { + ntfs_log_debug("Triggering marker code.\n"); + for (ds = dend; drl[ds].length; ds++) + ; + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; + + if (drl[ds].vcn == marker_vcn) { + ntfs_log_debug("Old marker = %lli, replacing with " + "LCN_ENOENT.\n", + (long long)drl[ds].lcn); + drl[ds].lcn = (LCN)LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped runlist element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == (LCN)LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { + /* Add an unmapped runlist element. */ + if (!slots) { + /* FIXME/TODO: We need to have the + * extra memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 2); + if (!drl) + goto critical_error; + slots = 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + /* FIXME/TODO: We need to have the extra + * memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 1); + if (!drl) + goto critical_error; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = (LCN)LCN_ENOENT; + drl[ds].length = (s64)0; + } + } + } + +finished: + /* The merge was completed successfully. */ + ntfs_log_debug("Merged runlist:\n"); + ntfs_debug_runlist_dump(drl); + return drl; + +critical_error: + /* Critical error! We cannot afford to fail here. */ + ntfs_log_perror("libntfs: Critical error"); + ntfs_log_debug("Forcing segmentation fault!\n"); + marker_vcn = ((runlist*)NULL)->lcn; + return drl; +} + +/** + * ntfs_runlists_merge - merge two runlists into one + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. + */ +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) +{ + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; +} + +/** + * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist + * @vol: ntfs volume on which the attribute resides + * @attr: attribute record whose mapping pairs array to decompress + * @old_rl: optional runlist in which to insert @attr's runlist + * + * Decompress the attribute @attr's mapping pairs array into a runlist. On + * success, return the decompressed runlist. + * + * If @old_rl is not NULL, decompressed runlist is inserted into the + * appropriate place in @old_rl and the resultant, combined runlist is + * returned. The original @old_rl is deallocated. + * + * On error, return NULL with errno set to the error code. @old_rl is left + * unmodified in that case. + * + * The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EIO Corrupt runlist. + * EINVAL Invalid parameters were passed in. + * ERANGE The two runlists overlap. + * + * FIXME: For now we take the conceptionally simplest approach of creating the + * new runlist disregarding the already existing one and then splicing the + * two into one, if that is possible (we check for overlap and discard the new + * runlist if overlap present before returning NULL, with errno = ERANGE). + */ +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + VCN vcn; /* Current vcn. */ + LCN lcn; /* Current lcn. */ + s64 deltaxcn; /* Change in [vl]cn. */ + runlist_element *rl; /* The output runlist. */ + const u8 *buf; /* Current position in mapping pairs array. */ + const u8 *attr_end; /* End of attribute. */ + int err, rlsize; /* Size of runlist buffer. */ + u16 rlpos; /* Current runlist position in units of + runlist_elements. */ + u8 b; /* Current byte offset in buf. */ + + ntfs_log_trace("Entering for attr 0x%x.\n", + (unsigned)le32_to_cpu(attr->type)); + /* Make sure attr exists and is non-resident. */ + if (!attr || !attr->non_resident || + sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { + errno = EINVAL; + return NULL; + } + /* Start at vcn = lowest_vcn and lcn 0. */ + vcn = sle64_to_cpu(attr->lowest_vcn); + lcn = 0; + /* Get start of the mapping pairs array. */ + buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); + attr_end = (const u8*)attr + le32_to_cpu(attr->length); + if (buf < (const u8*)attr || buf > attr_end) { + ntfs_log_debug("Corrupt attribute.\n"); + errno = EIO; + return NULL; + } + /* Current position in runlist array. */ + rlpos = 0; + /* Allocate first 4kiB block and set current runlist size to 4kiB. */ + rlsize = 0x1000; + rl = ntfs_malloc(rlsize); + if (!rl) + return NULL; + /* Insert unmapped starting element if necessary. */ + if (vcn) { + rl->vcn = (VCN)0; + rl->lcn = (LCN)LCN_RL_NOT_MAPPED; + rl->length = vcn; + rlpos++; + } + while (buf < attr_end && *buf) { + /* + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. + */ + if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { + runlist_element *rl2; + + rlsize += 0x1000; + rl2 = realloc(rl, rlsize); + if (!rl2) { + int eo = errno; + free(rl); + errno = eo; + return NULL; + } + rl = rl2; + } + /* Enter the current vcn into the current runlist element. */ + rl[rlpos].vcn = vcn; + /* + * Get the change in vcn, i.e. the run length in clusters. + * Doing it this way ensures that we signextend negative values. + * A negative run length doesn't make any sense, but hey, I + * didn't make up the NTFS specs and Windows NT4 treats the run + * length as a signed value so that's how it is... + */ + b = *buf & 0xf; + if (b) { + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + } else { /* The length entry is compulsory. */ + ntfs_log_debug("Missing length entry in mapping pairs " + "array.\n"); + deltaxcn = (s64)-1; + } + /* + * Assume a negative length to indicate data corruption and + * hence clean-up and return NULL. + */ + if (deltaxcn < 0) { + ntfs_log_debug("Invalid length in mapping pairs array.\n"); + goto err_out; + } + /* + * Enter the current run length into the current runlist + * element. + */ + rl[rlpos].length = deltaxcn; + /* Increment the current vcn by the current run length. */ + vcn += deltaxcn; + /* + * There might be no lcn change at all, as is the case for + * sparse clusters on NTFS 3.0+, in which case we set the lcn + * to LCN_HOLE. + */ + if (!(*buf & 0xf0)) + rl[rlpos].lcn = (LCN)LCN_HOLE; + else { + /* Get the lcn change which really can be negative. */ + u8 b2 = *buf & 0xf; + b = b2 + ((*buf >> 4) & 0xf); + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b > b2; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + /* Change the current lcn to it's new value. */ + lcn += deltaxcn; +#ifdef DEBUG + /* + * On NTFS 1.2-, apparently can have lcn == -1 to + * indicate a hole. But we haven't verified ourselves + * whether it is really the lcn or the deltaxcn that is + * -1. So if either is found give us a message so we + * can investigate it further! + */ + if (vol->major_ver < 3) { + if (deltaxcn == (LCN)-1) + ntfs_log_debug("lcn delta == -1\n"); + if (lcn == (LCN)-1) + ntfs_log_debug("lcn == -1\n"); + } +#endif + /* Check lcn is not below -1. */ + if (lcn < (LCN)-1) { + ntfs_log_debug("Invalid LCN < -1 in mapping pairs " + "array.\n"); + goto err_out; + } + /* Enter the current lcn into the runlist element. */ + rl[rlpos].lcn = lcn; + } + /* Get to the next runlist element. */ + rlpos++; + /* Increment the buffer position to the next mapping pair. */ + buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; + } + if (buf >= attr_end) + goto io_error; + /* + * If there is a highest_vcn specified, it must be equal to the final + * vcn in the runlist - 1, or something has gone badly wrong. + */ + deltaxcn = sle64_to_cpu(attr->highest_vcn); + if (deltaxcn && vcn - 1 != deltaxcn) { +mpa_err: + ntfs_log_debug("Corrupt mapping pairs array in non-resident " + "attribute.\n"); + goto err_out; + } + /* Setup not mapped runlist element if this is the base extent. */ + if (!attr->lowest_vcn) { + VCN max_cluster; + + max_cluster = ((sle64_to_cpu(attr->allocated_size) + + vol->cluster_size - 1) >> + vol->cluster_size_bits) - 1; + /* + * A highest_vcn of zero means this is a single extent + * attribute so simply terminate the runlist with LCN_ENOENT). + */ + if (deltaxcn) { + /* + * If there is a difference between the highest_vcn and + * the highest cluster, the runlist is either corrupt + * or, more likely, there are more extents following + * this one. + */ + if (deltaxcn < max_cluster) { + ntfs_log_debug("More extents to follow; deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - deltaxcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + rlpos++; + } else if (deltaxcn > max_cluster) { + ntfs_log_debug("Corrupt attribute. deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + goto mpa_err; + } + } + rl[rlpos].lcn = (LCN)LCN_ENOENT; + } else /* Not the base extent. There may be more extents to follow. */ + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + + /* Setup terminating runlist element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; + /* If no existing runlist was specified, we are done. */ + if (!old_rl) { + ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); + ntfs_debug_runlist_dump(rl); + return rl; + } + /* Now combine the new and old runlists checking for overlaps. */ + old_rl = ntfs_runlists_merge(old_rl, rl); + if (old_rl) + return old_rl; + err = errno; + free(rl); + ntfs_log_debug("Failed to merge runlists.\n"); + errno = err; + return NULL; +io_error: + ntfs_log_debug("Corrupt attribute.\n"); +err_out: + free(rl); + errno = EIO; + return NULL; +} + +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; +} + +/** + * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist + * @rl: runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @rl to map vcns to their + * corresponding lcns. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ================================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been + * inserted into the runlist yet. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + */ +LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) +{ + int i; + + if (vcn < (VCN)0) + return (LCN)LCN_EINVAL; + /* + * If rl is NULL, assume that we have found an unmapped runlist. The + * caller can then attempt to map it and fail appropriately if + * necessary. + */ + if (!rl) + return (LCN)LCN_RL_NOT_MAPPED; + + /* Catch out of lower bounds vcn. */ + if (vcn < rl[0].vcn) + return (LCN)LCN_ENOENT; + + for (i = 0; rl[i].length; i++) { + if (vcn < rl[i+1].vcn) { + if (rl[i].lcn >= (LCN)0) + return rl[i].lcn + (vcn - rl[i].vcn); + return rl[i].lcn; + } + } + /* + * The terminator element is setup to the correct value, i.e. one of + * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. + */ + if (rl[i].lcn < (LCN)0) + return rl[i].lcn; + /* Just in case... We could replace this with BUG() some day. */ + return (LCN)LCN_ENOENT; +} + +/** + * ntfs_rl_pread - gather read from disk + * @vol: ntfs volume to read from + * @rl: runlist specifying where to read the data from + * @pos: byte position within runlist @rl at which to begin the read + * @count: number of bytes to read + * @b: data buffer into which to read from disk + * + * This function will read @count bytes from the volume @vol to the data buffer + * @b gathering the data as specified by the runlist @rl. The read begins at + * offset @pos into the runlist @rl. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + * + * NOTE: If we encounter EOF while reading we return EIO because we assume that + * the run list must point to valid locations within the ntfs volume. + */ +s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b) +{ + s64 bytes_read, to_read, ofs, total; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to read runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + return -1; + } + if (!count) + return count; + /* Seek in @rl to the run containing @pos. */ + for (ofs = 0; rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos); rl++) + ofs += (rl->length << vol->cluster_size_bits); + /* Offset in the run at which to begin reading. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + /* It is a hole. Just fill buffer @b with zeroes. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update counters and proceed with next run. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it from the volume. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + bytes_read = ntfs_pread(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (bytes_read > 0) { + total += bytes_read; + count -= bytes_read; + b = (u8*)b + bytes_read; + continue; + } + /* If the syscall was interrupted, try again. */ + if (bytes_read == (s64)-1 && errno == EINTR) + goto retry; + if (bytes_read == (s64)-1) + err = errno; + goto rl_err_out; + } + /* Finally, return the number of bytes read. */ + return total; +rl_err_out: + if (total) + return total; + errno = err; + return -1; +} + +/** + * ntfs_rl_pwrite - scatter write to disk + * @vol: ntfs volume to write to + * @rl: runlist entry specifying where to write the data to + * @ofs: offset in file for runlist element indicated in @rl + * @pos: byte position from runlist beginning at which to begin the write + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the volume @vol + * scattering the data as specified by the runlist @rl. The write begins at + * offset @pos into the runlist @rl. If a run is sparse then the related buffer + * data is ignored which means that the caller must ensure they are consistent. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case + * of invalid arguments. + */ +s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b) +{ + s64 written, to_write, total = 0; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to write runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + goto errno_set; + } + if (!count) + goto out; + /* Seek in @rl to the run containing @pos. */ + while (rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos)) { + ofs += (rl->length << vol->cluster_size_bits); + rl++; + } + /* Offset in the run at which to begin writing. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + + to_write = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + + total += to_write; + count -= to_write; + b = (u8*)b + to_write; + continue; + } + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + if (!NVolReadOnly(vol)) + written = ntfs_pwrite(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, + to_write, b); + else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + b = (u8*)b + written; + continue; + } + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (written == (s64)-1) + err = errno; + goto rl_err_out; + } +out: + return total; +rl_err_out: + if (total) + goto out; + errno = err; +errno_set: + total = -1; + goto out; +} + +/** + * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number + * @n: number for which to get the number of bytes for + * + * Return the number of bytes required to store @n unambiguously as + * a signed number. + * + * This is used in the context of the mapping pairs array to determine how + * many bytes will be needed in the array to store a given logical cluster + * number (lcn) or a specific run length. + * + * Return the number of bytes written. This function cannot fail. + */ +int ntfs_get_nr_significant_bytes(const s64 n) +{ + u64 l; + int i; + + l = (n < 0 ? ~n : n); + i = 1; + if (l >= 128) { + l >>= 7; + do { + i++; + l >>= 8; + } while (l); + } + return i; +} + +/** + * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array + * @vol: ntfs volume (needed for the ntfs version) + * @rl: runlist for which to determine the size of the mapping pairs + * @start_vcn: vcn at which to start the mapping pairs array + * + * Walk the runlist @rl and calculate the size in bytes of the mapping pairs + * array corresponding to the runlist @rl, starting at vcn @start_vcn. This + * for example allows us to allocate a buffer of the right size when building + * the mapping pairs array. + * + * If @rl is NULL, just return 1 (for the single terminator byte). + * + * Return the calculated size in bytes on success. On error, return -1 with + * errno set to the error code. The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + */ +int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size) +{ + LCN prev_lcn; + int rls; + + if (start_vcn < 0) { + ntfs_log_trace("start_vcn %lld (should be >= 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + if (!rl) { + if (start_vcn) { + ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + rls = 1; + goto out; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { + errno = EINVAL; + goto errno_set; + } + prev_lcn = 0; + /* Always need the terminating zero byte. */ + rls = 1; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(prev_lcn); + } + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && (rls <= max_size); rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(rl->lcn - + prev_lcn); + prev_lcn = rl->lcn; + } + } +out: + return rls; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + rls = -1; + goto out; +} + +/** + * ntfs_write_significant_bytes - write the significant bytes of a number + * @dst: destination buffer to write to + * @dst_max: pointer to last byte of destination buffer for bounds checking + * @n: number whose significant bytes to write + * + * Store in @dst, the minimum bytes of the number @n which are required to + * identify @n unambiguously as a signed number, taking care not to exceed + * @dest_max, the maximum position within @dst to which we are allowed to + * write. + * + * This is used when building the mapping pairs array of a runlist to compress + * a given logical cluster number (lcn) or a specific run length to the minimum + * size possible. + * + * Return the number of bytes written on success. On error, i.e. the + * destination buffer @dst is too small, return -1 with errno set ENOSPC. + */ +int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) +{ + s64 l = n; + int i; + + i = 0; + if (dst > dst_max) + goto err_out; + *dst++ = l; + i++; + while ((l > 0x7f) || (l < -0x80)) { + if (dst > dst_max) + goto err_out; + l >>= 8; + *dst++ = l; + i++; + } + return i; +err_out: + errno = ENOSPC; + return -1; +} + +/** + * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist + * @vol: ntfs volume (needed for the ntfs version) + * @dst: destination buffer to which to write the mapping pairs array + * @dst_len: size of destination buffer @dst in bytes + * @rl: runlist for which to build the mapping pairs array + * @start_vcn: vcn at which to start the mapping pairs array + * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error + * + * Create the mapping pairs array from the runlist @rl, starting at vcn + * @start_vcn and save the array in @dst. @dst_len is the size of @dst in + * bytes and it should be at least equal to the value obtained by calling + * ntfs_get_size_for_mapping_pairs(). + * + * If @rl is NULL, just write a single terminator byte to @dst. + * + * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to + * the first vcn outside the destination buffer. Note that on error @dst has + * been filled with all the mapping pairs that will fit, thus it can be treated + * as partial success, in that a new attribute extent needs to be created or the + * next extent has to be used and the mapping pairs build has to be continued + * with @start_vcn set to *@stop_vcn. + * + * Return 0 on success. On error, return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + * ENOSPC - The destination buffer is too small. + */ +int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl) +{ + LCN prev_lcn; + u8 *dst_max, *dst_next; + s8 len_len, lcn_len; + int ret = 0; + + if (start_vcn < 0) + goto val_err; + if (!rl) { + if (start_vcn) + goto val_err; + if (stop_rl) + *stop_rl = rl; + if (dst_len < 1) + goto nospc_err; + goto ok; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) + goto val_err; + /* + * @dst_max is used for bounds checking in + * ntfs_write_significant_bytes(). + */ + dst_max = dst + dst_len - 1; + prev_lcn = 0; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length - delta); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, prev_lcn); + if (lcn_len < 0) + goto size_err; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length; rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, rl->lcn - prev_lcn); + if (lcn_len < 0) + goto size_err; + prev_lcn = rl->lcn; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst += 1 + len_len + lcn_len; + } + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; +ok: + /* Add terminator byte. */ + *dst = 0; +out: + return ret; +size_err: + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; + /* Add terminator byte. */ + *dst = 0; +nospc_err: + errno = ENOSPC; + goto errno_set; +val_err: + errno = EINVAL; + goto errno_set; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + ret = -1; + goto out; +} + +/** + * ntfs_rl_truncate - truncate a runlist starting at a specified vcn + * @arl: address of runlist to truncate + * @start_vcn: first vcn which should be cut off + * + * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory + * buffer holding the runlist. + * + * Return 0 on success and -1 on error with errno set to the error code. + * + * NOTE: @arl is the address of the runlist. We need the address so we can + * modify the pointer to the runlist with the new, reallocated memory buffer. + */ +int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) +{ + runlist *rl; + /* BOOL is_end = FALSE; */ + + if (!arl || !*arl) { + errno = EINVAL; + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); + return -1; + } + + rl = *arl; + + if (start_vcn < rl->vcn) { + errno = EINVAL; + ntfs_log_perror("Start_vcn lies outside front of runlist"); + return -1; + } + + /* Find the starting vcn in the run list. */ + while (rl->length) { + if (start_vcn < rl[1].vcn) + break; + rl++; + } + + if (!rl->length) { + errno = EIO; + ntfs_log_trace("Truncating already truncated runlist?\n"); + return -1; + } + + /* Truncate the run. */ + rl->length = start_vcn - rl->vcn; + + /* + * If a run was partially truncated, make the following runlist + * element a terminator instead of the truncated runlist + * element itself. + */ + if (rl->length) { + ++rl; +/* + if (!rl->length) + is_end = TRUE; +*/ + rl->vcn = start_vcn; + rl->length = 0; + } + rl->lcn = (LCN)LCN_ENOENT; + /** + * Reallocate memory if necessary. + * FIXME: Below code is broken, because runlist allocations must be + * a multiple of 4096. The code caused crashes and corruptions. + */ +/* + if (!is_end) { + size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); + rl = realloc(*arl, new_size); + if (rl) + *arl = rl; + } +*/ + return 0; +} + +/** + * ntfs_rl_sparse - check whether runlist have sparse regions or not. + * @rl: runlist to check + * + * Return 1 if have, 0 if not, -1 on error with errno set to the error code. + */ +int ntfs_rl_sparse(runlist *rl) +{ + runlist *rlc; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + return 1; + } + return 0; +} + +/** + * ntfs_rl_get_compressed_size - calculate length of non sparse regions + * @vol: ntfs volume (need for cluster size) + * @rl: runlist to calculate for + * + * Return compressed size or -1 on error with errno set to the error code. + */ +s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) +{ + runlist *rlc; + s64 ret = 0; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) { + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + } else + ret += rlc->length; + } + return ret << vol->cluster_size_bits; +} + + +#ifdef NTFS_TEST +/** + * test_rl_helper + */ +#define MKRL(R,V,L,S) \ + (R)->vcn = V; \ + (R)->lcn = L; \ + (R)->length = S; +/* +} +*/ +/** + * test_rl_dump_runlist - Runlist test: Display the contents of a runlist + * @rl: + * + * Description... + * + * Returns: + */ +static void test_rl_dump_runlist(const runlist_element *rl) +{ + int abbr = 0; /* abbreviate long lists */ + int len = 0; + int i; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; + + if (!rl) { + printf(" Run list not present.\n"); + return; + } + + if (abbr) + for (len = 0; rl[len].length; len++) ; + + printf(" VCN LCN len\n"); + for (i = 0; ; i++, rl++) { + LCN lcn = rl->lcn; + + if ((abbr) && (len > 20)) { + if (i == 4) + printf(" ...\n"); + if ((i > 3) && (i < (len - 3))) + continue; + } + + if (lcn < (LCN)0) { + int ind = -lcn - 1; + + if (ind > -LCN_ENOENT - 1) + ind = 3; + printf("%8lld %8s %8lld\n", + rl->vcn, lcn_str[ind], rl->length); + } else + printf("%8lld %8lld %8lld\n", + rl->vcn, rl->lcn, rl->length); + if (!rl->length) + break; + } + if ((abbr) && (len > 20)) + printf(" (%d entries)\n", len+1); + printf("\n"); +} + +/** + * test_rl_runlists_merge - Runlist test: Merge two runlists + * @drl: + * @srl: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) +{ + runlist_element *res = NULL; + + printf("dst:\n"); + test_rl_dump_runlist(drl); + printf("src:\n"); + test_rl_dump_runlist(srl); + + res = ntfs_runlists_merge(drl, srl); + + printf("res:\n"); + test_rl_dump_runlist(res); + + return res; +} + +/** + * test_rl_read_buffer - Runlist test: Read a file containing a runlist + * @file: + * @buf: + * @bufsize: + * + * Description... + * + * Returns: + */ +static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) +{ + FILE *fptr; + + fptr = fopen(file, "r"); + if (!fptr) { + printf("open %s\n", file); + return 0; + } + + if (fread(buf, bufsize, 1, fptr) == 99) { + printf("read %s\n", file); + return 0; + } + + fclose(fptr); + return 1; +} + +/** + * test_rl_pure_src - Runlist test: Complicate the simple tests a little + * @contig: + * @multi: + * @vcn: + * @len: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) +{ + runlist_element *result; + int fudge; + + if (contig) + fudge = 0; + else + fudge = 999; + + result = ntfs_malloc(4096); + if (!result) + return NULL; + + if (multi) { + MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) + MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) + MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) + MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) + MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) + } else { + MKRL(result+0, vcn, fudge + vcn + 1000, len) + MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) + } + return result; +} + +/** + * test_rl_pure_test - Runlist test: Perform tests using simple runlists + * @test: + * @contig: + * @multi: + * @vcn: + * @len: + * @file: + * @size: + * + * Description... + * + * Returns: + */ +static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) +{ + runlist_element *src; + runlist_element *dst; + runlist_element *res; + + src = test_rl_pure_src(contig, multi, vcn, len); + dst = ntfs_malloc(4096); + if (!src || !dst) { + printf("Test %2d ---------- FAILED! (no free memory?)\n", test); + return; + } + + memcpy(dst, file, size); + + printf("Test %2d ----------\n", test); + res = test_rl_runlists_merge(dst, src); + + free(res); +} + +/** + * test_rl_pure - Runlist test: Create tests using simple runlists + * @contig: + * @multi: + * + * Description... + * + * Returns: + */ +static void test_rl_pure(char *contig, char *multi) +{ + /* VCN, LCN, len */ + static runlist_element file1[] = { + { 0, -1, 100 }, /* HOLE */ + { 100, 1100, 100 }, /* DATA */ + { 200, -1, 100 }, /* HOLE */ + { 300, 1300, 100 }, /* DATA */ + { 400, -1, 100 }, /* HOLE */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file2[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -1, 100 }, /* HOLE */ + { 200, -3, 0 } /* NOENT */ + }; + static runlist_element file3[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -3, 0 } /* NOENT */ + }; + static runlist_element file4[] = { + { 0, -3, 0 } /* NOENT */ + }; + static runlist_element file5[] = { + { 0, -2, 100 }, /* NOTMAP */ + { 100, 1100, 100 }, /* DATA */ + { 200, -2, 100 }, /* NOTMAP */ + { 300, 1300, 100 }, /* DATA */ + { 400, -2, 100 }, /* NOTMAP */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file6[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -2, 100 }, /* NOTMAP */ + { 200, -3, 0 } /* NOENT */ + }; + BOOL c, m; + + if (strcmp(contig, "contig") == 0) + c = TRUE; + else if (strcmp(contig, "noncontig") == 0) + c = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + if (strcmp(multi, "multi") == 0) + m = TRUE; + else if (strcmp(multi, "single") == 0) + m = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + + test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); + test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); + test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); + test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); + test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); + test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); + test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); + test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); + test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); + test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); + test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); + test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); + test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); + test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); + test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); + test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); + test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); + test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); + test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); + test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); + test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); + test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); + test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); + test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); + test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); + test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); + test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); + test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); + test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); + test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); + test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); + test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); + test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); + test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); +} + +/** + * test_rl_zero - Runlist test: Merge a zero-length runlist + * + * Description... + * + * Returns: + */ +static void test_rl_zero(void) +{ + runlist_element *jim = NULL; + runlist_element *bob = NULL; + + bob = calloc(3, sizeof(runlist_element)); + if (!bob) + return; + + MKRL(bob+0, 10, 99, 5) + MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) + + jim = test_rl_runlists_merge(jim, bob); + if (!jim) + return; + + free(jim); +} + +/** + * test_rl_frag_combine - Runlist test: Perform tests using fragmented files + * @vol: + * @attr1: + * @attr2: + * @attr3: + * + * Description... + * + * Returns: + */ +static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) +{ + runlist_element *run1; + runlist_element *run2; + runlist_element *run3; + + run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); + if (!run1) + return; + + run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); + if (!run2) + return; + + run1 = test_rl_runlists_merge(run1, run2); + + run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); + if (!run3) + return; + + run1 = test_rl_runlists_merge(run1, run3); + + free(run1); +} + +/** + * test_rl_frag - Runlist test: Create tests using very fragmented files + * @test: + * + * Description... + * + * Returns: + */ +static void test_rl_frag(char *test) +{ + ntfs_volume vol; + ATTR_RECORD *attr1 = ntfs_malloc(1024); + ATTR_RECORD *attr2 = ntfs_malloc(1024); + ATTR_RECORD *attr3 = ntfs_malloc(1024); + + if (!attr1 || !attr2 || !attr3) + goto out; + + vol.sb = NULL; + vol.sector_size_bits = 9; + vol.cluster_size = 2048; + vol.cluster_size_bits = 11; + vol.major_ver = 3; + + if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) + goto out; + + if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); + else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); + else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); + else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); + else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); + else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); + else + printf("Frag: No such test '%s'\n", test); + +out: + free(attr1); + free(attr2); + free(attr3); +} + +/** + * test_rl_main - Runlist test: Program start (main) + * @argc: + * @argv: + * + * Description... + * + * Returns: + */ +int test_rl_main(int argc, char *argv[]) +{ + if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); + else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); + else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); + else + printf("rl [zero|frag|pure] {args}\n"); + + return 0; +} + +#endif + diff --git a/lib/libntfs/src/source/runlist.h b/lib/libntfs/src/source/runlist.h new file mode 100644 index 0000000..4b73af9 --- /dev/null +++ b/lib/libntfs/src/source/runlist.h @@ -0,0 +1,90 @@ +/* + * runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_RUNLIST_H +#define _NTFS_RUNLIST_H + +#include "types.h" + +/* Forward declarations */ +typedef struct _runlist_element runlist_element; +typedef runlist_element runlist; + +#include "attrib.h" +#include "volume.h" + +/** + * struct _runlist_element - in memory vcn to lcn mapping array element. + * @vcn: starting vcn of the current array element + * @lcn: starting lcn of the current array element + * @length: length in clusters of the current array element + * + * The last vcn (in fact the last vcn + 1) is reached when length == 0. + * + * When lcn == -1 this means that the count vcns starting at vcn are not + * physically allocated (i.e. this is a hole / data is sparse). + */ +struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ + VCN vcn; /* vcn = Starting virtual cluster number. */ + LCN lcn; /* lcn = Starting logical cluster number. */ + s64 length; /* Run length in clusters. */ +}; + +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); + +extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); + +extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b); +extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b); + +extern runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl); + +extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl); + +extern int ntfs_get_nr_significant_bytes(const s64 n); + +extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size); + +extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, + const s64 n); + +extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl); + +extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); + +extern int ntfs_rl_sparse(runlist *rl); +extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); + +#ifdef NTFS_TEST +int test_rl_main(int argc, char *argv[]); +#endif + +#endif /* defined _NTFS_RUNLIST_H */ + diff --git a/lib/libntfs/src/source/security.c b/lib/libntfs/src/source/security.c new file mode 100644 index 0000000..5e70c21 --- /dev/null +++ b/lib/libntfs/src/source/security.c @@ -0,0 +1,5093 @@ +/** + * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2006 Yura Pakhuchiy + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SETXATTR +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include +#include +#include + +#include "param.h" +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "bitmap.h" +#include "security.h" +#include "acls.h" +#include "cache.h" +#include "misc.h" + +/* + * JPA NTFS constants or structs + * should be moved to layout.h + */ + +#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */ +#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */ +#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */ +#define FIRST_SECURITY_ID 0x100 /* Lowest security id */ + + /* Mask for attributes which can be forced */ +#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ + | FILE_ATTR_HIDDEN \ + | FILE_ATTR_SYSTEM \ + | FILE_ATTR_ARCHIVE \ + | FILE_ATTR_TEMPORARY \ + | FILE_ATTR_OFFLINE \ + | FILE_ATTR_NOT_CONTENT_INDEXED ) + +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; +} ; + +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; + +/* + * A few useful constants + */ + +static ntfschar sii_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('I'), + const_cpu_to_le16('I'), + const_cpu_to_le16(0) }; +static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('H'), + const_cpu_to_le16(0) }; + +/* + * null SID (S-1-0-0) + */ + +extern const SID *nullsid; + +/* + * The zero GUID. + */ + +static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), + const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; +static const GUID *const zero_guid = &__zero_guid; + +/** + * ntfs_guid_is_zero - check if a GUID is zero + * @guid: [IN] guid to check + * + * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID + * and FALSE otherwise. + */ +BOOL ntfs_guid_is_zero(const GUID *guid) +{ + return (memcmp(guid, zero_guid, sizeof(*zero_guid))); +} + +/** + * ntfs_guid_to_mbs - convert a GUID to a multi byte string + * @guid: [IN] guid to convert + * @guid_str: [OUT] string in which to return the GUID (optional) + * + * Convert the GUID pointed to by @guid to a multi byte string of the form + * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) + * needs to be able to store at least 37 bytes. + * + * If @guid_str is not NULL it will contain the converted GUID on return. If + * it is NULL a string will be allocated and this will be returned. The caller + * is responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) +{ + char *_guid_str; + int res; + + if (!guid) { + errno = EINVAL; + return NULL; + } + _guid_str = guid_str; + if (!_guid_str) { + _guid_str = (char*)ntfs_malloc(37); + if (!_guid_str) + return _guid_str; + } + res = snprintf(_guid_str, 37, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned int)le32_to_cpu(guid->data1), + le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), + guid->data4[0], guid->data4[1], + guid->data4[2], guid->data4[3], guid->data4[4], + guid->data4[5], guid->data4[6], guid->data4[7]); + if (res == 36) + return _guid_str; + if (!guid_str) + free(_guid_str); + errno = EINVAL; + return NULL; +} + +/** + * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID + * @sid: [IN] SID for which to determine the maximum string size + * + * Determine the maximum multi byte string size in bytes which is needed to + * store the standard textual representation of the SID pointed to by @sid. + * See ntfs_sid_to_mbs(), below. + * + * On success return the maximum number of bytes needed to store the multi byte + * string and on failure return -1 with errno set to the error code. + */ +int ntfs_sid_to_mbs_size(const SID *sid) +{ + int size, i; + + if (!ntfs_sid_is_valid(sid)) { + errno = EINVAL; + return -1; + } + /* Start with "S-". */ + size = 2; + /* + * Add the SID_REVISION. Hopefully the compiler will optimize this + * away as SID_REVISION is a constant. + */ + for (i = SID_REVISION; i > 0; i /= 10) + size++; + /* Add the "-". */ + size++; + /* + * Add the identifier authority. If it needs to be in decimal, the + * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be + * in hexadecimal, then maximum is 0x665544332211 = 14 characters. + */ + if (!sid->identifier_authority.high_part) + size += 10; + else + size += 14; + /* + * Finally, add the sub authorities. For each we have a "-" followed + * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. + */ + size += (1 + 10) * sid->sub_authority_count; + /* We need the zero byte at the end, too. */ + size++; + return size * sizeof(char); +} + +/** + * ntfs_sid_to_mbs - convert a SID to a multi byte string + * @sid: [IN] SID to convert + * @sid_str: [OUT] string in which to return the SID (optional) + * @sid_str_size: [IN] size in bytes of @sid_str + * + * Convert the SID pointed to by @sid to its standard textual representation. + * @sid_str (if not NULL) needs to be able to store at least + * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of + * @sid_str if @sid_str is not NULL. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * If @sid_str is not NULL it will contain the converted SUID on return. If it + * is NULL a string will be allocated and this will be returned. The caller is + * responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) +{ + u64 u; + le32 leauth; + char *s; + int i, j, cnt; + + /* + * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will + * check @sid, too. 8 is the minimum SID string size. + */ + if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { + errno = EINVAL; + return NULL; + } + /* Allocate string if not provided. */ + if (!sid_str) { + cnt = ntfs_sid_to_mbs_size(sid); + if (cnt < 0) + return NULL; + s = (char*)ntfs_malloc(cnt); + if (!s) + return s; + sid_str = s; + /* So we know we allocated it. */ + sid_str_size = 0; + } else { + s = sid_str; + cnt = sid_str_size; + } + /* Start with "S-R-". */ + i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Add the identifier authority. */ + for (u = i = 0, j = 40; i < 6; i++, j -= 8) + u += (u64)sid->identifier_authority.value[i] << j; + if (!sid->identifier_authority.high_part) + i = snprintf(s, cnt, "%lu", (unsigned long)u); + else + i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Finally, add the sub authorities. */ + for (j = 0; j < sid->sub_authority_count; j++) { + leauth = sid->sub_authority[j]; + i = snprintf(s, cnt, "-%u", (unsigned int) + le32_to_cpu(leauth)); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + } + return sid_str; +err_out: + if (i >= cnt) + i = EMSGSIZE; + else + i = errno; + if (!sid_str_size) + free(sid_str); + errno = i; + return NULL; +} + +/** + * ntfs_generate_guid - generatates a random current guid. + * @guid: [OUT] pointer to a GUID struct to hold the generated guid. + * + * perhaps not a very good random number generator though... + */ +void ntfs_generate_guid(GUID *guid) +{ + unsigned int i; + u8 *p = (u8 *)guid; + + for (i = 0; i < sizeof(GUID); i++) { + p[i] = (u8)(random() & 0xFF); + if (i == 7) + p[7] = (p[7] & 0x0F) | 0x40; + if (i == 8) + p[8] = (p[8] & 0x3F) | 0x80; + } +} + +/** + * ntfs_security_hash - calculate the hash of a security descriptor + * @sd: self-relative security descriptor whose hash to calculate + * @length: size in bytes of the security descritor @sd + * + * Calculate the hash of the self-relative security descriptor @sd of length + * @length bytes. + * + * This hash is used in the $Secure system file as the primary key for the $SDH + * index and is also stored in the header of each security descriptor in the + * $SDS data stream as well as in the index data of both the $SII and $SDH + * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER + * structure. + * + * Return the calculated security hash in little endian. + */ +le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) +{ + const le32 *pos = (const le32*)sd; + const le32 *end = pos + (len >> 2); + u32 hash = 0; + + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } + return cpu_to_le32(hash); +} + +/* + * Get the first entry of current index block + * cut and pasted form ntfs_ie_get_first() in index.c + */ + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset)); +} + +/* + * Stuff a 256KB block into $SDS before writing descriptors + * into the block. + * + * This prevents $SDS from being automatically declared as sparse + * when the second copy of the first security descriptor is written + * 256KB further ahead. + * + * Having $SDS declared as a sparse file is not wrong by itself + * and chkdsk leaves it as a sparse file. It does however complain + * and add a sparse flag (0x0200) into field file_attributes of + * STANDARD_INFORMATION of $Secure. This probably means that a + * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse + * files (FILE_ATTR_SPARSE_FILE). + * + * Windows normally does not convert to sparse attribute or sparse + * file. Stuffing is just a way to get to the same result. + */ + +static int entersecurity_stuff(ntfs_volume *vol, off_t offs) +{ + int res; + int written; + unsigned long total; + char *stuff; + + res = 0; + total = 0; + stuff = (char*)ntfs_malloc(STUFFSZ); + if (stuff) { + memset(stuff, 0, STUFFSZ); + do { + written = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, stuff, STUFFSZ, offs); + if (written == STUFFSZ) { + total += STUFFSZ; + offs += STUFFSZ; + } else { + errno = ENOSPC; + res = -1; + } + } while (!res && (total < ALIGN_SDS_BLOCK)); + free(stuff); + } else { + errno = ENOMEM; + res = -1; + } + return (res); +} + +/* + * Enter a new security descriptor into $Secure (data only) + * it has to be written twice with an offset of 256KB + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_data(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash, le32 keyid, off_t offs, int gap) +{ + int res; + int written1; + int written2; + char *fullattr; + int fullsz; + SECURITY_DESCRIPTOR_HEADER *phsds; + + res = -1; + fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER); + fullattr = (char*)ntfs_malloc(fullsz); + if (fullattr) { + /* + * Clear the gap from previous descriptor + * this could be useful for appending the second + * copy to the end of file. When creating a new + * 256K block, the gap is cleared while writing + * the first copy + */ + if (gap) + memset(fullattr,0,gap); + memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)], + attr,attrsz); + phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap]; + phsds->hash = hash; + phsds->security_id = keyid; + phsds->offset = cpu_to_le64(offs); + phsds->length = cpu_to_le32(fullsz - gap); + written1 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap); + written2 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap + ALIGN_SDS_BLOCK); + if ((written1 == fullsz) + && (written2 == written1)) + res = 0; + else + errno = ENOSPC; + free(fullattr); + } else + errno = ENOMEM; + return (res); +} + +/* + * Enter a new security descriptor in $Secure (indexes only) + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz, + le32 hash, le32 keyid, off_t offs) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int res; + ntfs_index_context *xsii; + ntfs_index_context *xsdh; + struct SII newsii; + struct SDH newsdh; + + res = -1; + /* enter a new $SII record */ + + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + newsii.offs = const_cpu_to_le16(20); + newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20); + newsii.fill1 = const_cpu_to_le32(0); + newsii.indexsz = const_cpu_to_le16(sizeof(struct SII)); + newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY)); + newsii.flags = const_cpu_to_le16(0); + newsii.fill2 = const_cpu_to_le16(0); + newsii.keysecurid = keyid; + newsii.hash = hash; + newsii.securid = keyid; + realign.all = cpu_to_le64(offs); + newsii.dataoffsh = realign.parts.dataoffsh; + newsii.dataoffsl = realign.parts.dataoffsl; + newsii.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) { + + /* enter a new $SDH record */ + + xsdh = vol->secure_xsdh; + ntfs_index_ctx_reinit(xsdh); + newsdh.offs = const_cpu_to_le16(24); + newsdh.size = const_cpu_to_le16( + sizeof(SECURITY_DESCRIPTOR_HEADER)); + newsdh.fill1 = const_cpu_to_le32(0); + newsdh.indexsz = const_cpu_to_le16( + sizeof(struct SDH)); + newsdh.indexksz = const_cpu_to_le16( + sizeof(SDH_INDEX_KEY)); + newsdh.flags = const_cpu_to_le16(0); + newsdh.fill2 = const_cpu_to_le16(0); + newsdh.keyhash = hash; + newsdh.keysecurid = keyid; + newsdh.hash = hash; + newsdh.securid = keyid; + newsdh.dataoffsh = realign.parts.dataoffsh; + newsdh.dataoffsl = realign.parts.dataoffsl; + newsdh.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + /* special filler value, Windows generally */ + /* fills with 0x00490049, sometimes with zero */ + newsdh.fill3 = const_cpu_to_le32(0x00490049); + if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh)) + res = 0; + } + return (res); +} + +/* + * Enter a new security descriptor in $Secure (data and indexes) + * Returns id of entry, or zero if there is a problem. + * (should not be called for NTFS version < 3.0) + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 entersecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + le32 securid; + le32 keyid; + u32 newkey; + off_t offs; + int gap; + int size; + BOOL found; + struct SII *psii; + INDEX_ENTRY *entry; + INDEX_ENTRY *next; + ntfs_index_context *xsii; + int retries; + ntfs_attr *na; + int olderrno; + + /* find the first available securid beyond the last key */ + /* in $Secure:$SII. This also determines the first */ + /* available location in $Secure:$SDS, as this stream */ + /* is always appended to and the id's are allocated */ + /* in sequence */ + + securid = const_cpu_to_le32(0); + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + offs = size = 0; + keyid = const_cpu_to_le32(-1); + olderrno = errno; + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Inconsistency in index $SII"); + psii = (struct SII*)NULL; + } else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsii->entry; + psii = (struct SII*)xsii->entry; + } + if (psii) { + /* + * Get last entry in block, but must get first one + * one first, as we should already be beyond the + * last one. For some reason the search for the last + * entry sometimes does not return the last block... + * we assume this can only happen in root block + */ + if (xsii->is_in_root) + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ir->index); + else + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ib->index); + /* + * All index blocks should be at least half full + * so there always is a last entry but one, + * except when creating the first entry in index root. + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. + */ + keyid = const_cpu_to_le32(0); + retries = 0; + while (entry) { + next = ntfs_index_next(entry,xsii); + if (next) { + psii = (struct SII*)next; + /* save last key and */ + /* available position */ + keyid = psii->keysecurid; + realign.parts.dataoffsh + = psii->dataoffsh; + realign.parts.dataoffsl + = psii->dataoffsl; + offs = le64_to_cpu(realign.all); + size = le32_to_cpu(psii->datasize); + } + entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + } + retries++; + } + } + } + if (!keyid) { + /* + * could not find any entry, before creating the first + * entry, make a double check by making sure size of $SII + * is less than needed for one entry + */ + securid = const_cpu_to_le32(0); + na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4); + if (na) { + if ((size_t)na->data_size < sizeof(struct SII)) { + ntfs_log_error("Creating the first security_id\n"); + securid = const_cpu_to_le32(FIRST_SECURITY_ID); + } + ntfs_attr_close(na); + } + if (!securid) { + ntfs_log_error("Error creating a security_id\n"); + errno = EIO; + } + } else { + newkey = le32_to_cpu(keyid) + 1; + securid = cpu_to_le32(newkey); + } + /* + * The security attr has to be written twice 256KB + * apart. This implies that offsets like + * 0x40000*odd_integer must be left available for + * the second copy. So align to next block when + * the last byte overflows on a wrong block. + */ + + if (securid) { + gap = (-size) & (ALIGN_SDS_ENTRY - 1); + offs += gap + size; + if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + & ALIGN_SDS_BLOCK) { + offs = ((offs + attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + | (ALIGN_SDS_BLOCK - 1)) + 1; + } + if (!(offs & (ALIGN_SDS_BLOCK - 1))) + entersecurity_stuff(vol, offs); + /* + * now write the security attr to storage : + * first data, then SII, then SDH + * If failure occurs while writing SDS, data will never + * be accessed through indexes, and will be overwritten + * by the next allocated descriptor + * If failure occurs while writing SII, the id has not + * recorded and will be reallocated later + * If failure occurs while writing SDH, the space allocated + * in SDS or SII will not be reused, an inconsistency + * will persist with no significant consequence + */ + if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap) + || entersecurity_indexes(vol, attrsz, hash, securid, offs)) + securid = const_cpu_to_le32(0); + } + /* inode now is dirty, synchronize it all */ + ntfs_index_entry_mark_dirty(vol->secure_xsii); + ntfs_index_ctx_reinit(vol->secure_xsii); + ntfs_index_entry_mark_dirty(vol->secure_xsdh); + ntfs_index_ctx_reinit(vol->secure_xsdh); + NInoSetDirty(vol->secure_ni); + if (ntfs_inode_sync(vol->secure_ni)) + ntfs_log_perror("Could not sync $Secure\n"); + return (securid); +} + +/* + * Find a matching security descriptor in $Secure, + * if none, allocate a new id and write the descriptor to storage + * Returns id of entry, or zero if there is a problem. + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 setsecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz) +{ + struct SDH *psdh; /* this is an image of index (le) */ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + BOOL found; + BOOL collision; + size_t size; + size_t rdsize; + s64 offs; + int res; + ntfs_index_context *xsdh; + char *oldattr; + SDH_INDEX_KEY key; + INDEX_ENTRY *entry; + le32 securid; + le32 hash; + int olderrno; + + hash = ntfs_security_hash(attr,attrsz); + oldattr = (char*)NULL; + securid = const_cpu_to_le32(0); + res = 0; + xsdh = vol->secure_xsdh; + if (vol->secure_ni && xsdh && !vol->secure_reentry++) { + ntfs_index_ctx_reinit(xsdh); + /* + * find the nearest key as (hash,0) + * (do not search for partial key : in case of collision, + * it could return a key which is not the first one which + * collides) + */ + key.hash = hash; + key.security_id = const_cpu_to_le32(0); + olderrno = errno; + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + if (!found && (errno != ENOENT)) + ntfs_log_perror("Inconsistency in index $SDH"); + else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsdh->entry; + found = FALSE; + /* + * lookup() may return a node with no data, + * if so get next + */ + if (entry->ie_flags & INDEX_ENTRY_END) + entry = ntfs_index_next(entry,xsdh); + do { + collision = FALSE; + psdh = (struct SDH*)entry; + if (psdh) + size = (size_t) le32_to_cpu(psdh->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + else size = 0; + /* if hash is not the same, the key is not present */ + if (psdh && (size > 0) + && (psdh->keyhash == hash)) { + /* if hash is the same */ + /* check the whole record */ + realign.parts.dataoffsh = psdh->dataoffsh; + realign.parts.dataoffsl = psdh->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + oldattr = (char*)ntfs_malloc(size); + if (oldattr) { + rdsize = ntfs_attr_data_read( + vol->secure_ni, + STREAM_SDS, 4, + oldattr, size, offs); + found = (rdsize == size) + && !memcmp(oldattr,attr,size); + free(oldattr); + /* if the records do not compare */ + /* (hash collision), try next one */ + if (!found) { + entry = ntfs_index_next( + entry,xsdh); + collision = TRUE; + } + } else + res = ENOMEM; + } + } while (collision && entry); + if (found) + securid = psdh->keysecurid; + else { + if (res) { + errno = res; + securid = const_cpu_to_le32(0); + } else { + /* + * no matching key : + * have to build a new one + */ + securid = entersecurityattr(vol, + attr, attrsz, hash); + } + } + } + } + if (--vol->secure_reentry) + ntfs_log_perror("Reentry error, check no multithreading\n"); + return (securid); +} + + +/* + * Update the security descriptor of a file + * Either as an attribute (complying with pre v3.x NTFS version) + * or, when possible, as an entry in $Secure (for NTFS v3.x) + * + * returns 0 if success + */ + +static int update_secur_descr(ntfs_volume *vol, + char *newattr, ntfs_inode *ni) +{ + int newattrsz; + int written; + int res; + ntfs_attr *na; + + newattrsz = ntfs_attr_size(newattr); + +#if !FORCE_FORMAT_v1x + if ((vol->major_ver < 3) || !vol->secure_ni) { +#endif + + /* update for NTFS format v1.x */ + + /* update the old security attribute */ + na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64) newattrsz); + /* overwrite value */ + if (!res) { + written = (int)ntfs_attr_pwrite(na, (s64) 0, + (s64) newattrsz, newattr); + if (written != newattrsz) { + ntfs_log_error("Failed to update " + "a v1.x security descriptor\n"); + errno = EIO; + res = -1; + } + } + + ntfs_attr_close(na); + /* if old security attribute was found, also */ + /* truncate standard information attribute to v1.x */ + /* this is needed when security data is wanted */ + /* as v1.x though volume is formatted for v3.x */ + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + clear_nino_flag(ni, v3_Extensions); + /* + * Truncating the record does not sweep extensions + * from copy in memory. Clear security_id to be safe + */ + ni->security_id = const_cpu_to_le32(0); + res = ntfs_attr_truncate(na, (s64)48); + ntfs_attr_close(na); + clear_nino_flag(ni, v3_Extensions); + } + } else { + /* + * insert the new security attribute if there + * were none + */ + res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, (u8*)newattr, + (s64) newattrsz); + } +#if !FORCE_FORMAT_v1x + } else { + + /* update for NTFS format v3.x */ + + le32 securid; + + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + (s64)newattrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + if (!test_nino_flag(ni, v3_Extensions)) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, + AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + } + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to update " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + } +#endif + + /* mark node as dirty */ + NInoSetDirty(ni); + return (res); +} + +/* + * Upgrade the security descriptor of a file + * This is intended to allow graceful upgrades for files which + * were created in previous versions, with a security attributes + * and no security id. + * + * It will allocate a security id and replace the individual + * security attribute by a reference to the global one + * + * Special files are not upgraded (currently / and files in + * directories /$*) + * + * Though most code is similar to update_secur_desc() it has + * been kept apart to facilitate the further processing of + * special cases or even to remove it if found dangerous. + * + * returns 0 if success, + * 1 if not upgradable. This is not an error. + * -1 if there is a problem + */ + +static int upgrade_secur_desc(ntfs_volume *vol, + const char *attr, ntfs_inode *ni) +{ + int attrsz; + int res; + le32 securid; + ntfs_attr *na; + + /* + * upgrade requires NTFS format v3.x + * also refuse upgrading for special files + * whose number is less than FILE_first_user + */ + + if ((vol->major_ver >= 3) + && (ni->mft_no >= FILE_first_user)) { + attrsz = ntfs_attr_size(attr); + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)attr, + (s64)attrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to upgrade " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + /* mark node as dirty */ + NInoSetDirty(ni); + } else + res = 1; + + return (res); +} + +/* + * Optional simplified checking of group membership + * + * This only takes into account the groups defined in + * /etc/group at initialization time. + * It does not take into account the groups dynamically set by + * setgroups() nor the changes in /etc/group since initialization + * + * This optional method could be useful if standard checking + * leads to a performance concern. + * + * Should not be called for user root, however the group may be root + * + */ + +static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + BOOL ingroup; + int grcnt; + gid_t *groups; + struct MAPPING *user; + + ingroup = FALSE; + if (uid) { + user = scx->mapping[MAPUSERS]; + while (user && ((uid_t)user->xid != uid)) + user = user->next; + if (user) { + groups = user->groups; + grcnt = user->grcnt; + while ((--grcnt >= 0) && (groups[grcnt] != gid)) { } + ingroup = (grcnt >= 0); + } + } + return (ingroup); +} + + +/* + * Check whether current thread owner is member of file group + * + * Should not be called for user root, however the group may be root + * + * As indicated by Miklos Szeredi : + * + * The group list is available in + * + * /proc/$PID/task/$TID/status + * + * and fuse supplies TID in get_fuse_context()->pid. The only problem is + * finding out PID, for which I have no good solution, except to iterate + * through all processes. This is rather slow, but may be speeded up + * with caching and heuristics (for single threaded programs PID = TID). + * + * The following implementation gets the group list from + * /proc/$TID/task/$TID/status which apparently exists and + * contains the same data. + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + static char key[] = "\nGroups:"; + char buf[BUFSZ+1]; + char filename[64]; + enum { INKEY, INSEP, INNUM, INEND } state; + int fd; + char c; + int matched; + BOOL ismember; + int got; + char *p; + gid_t grp; + pid_t tid; + + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/task/%u/status",tid,tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, buf, BUFSZ); + buf[got] = 0; + state = INKEY; + matched = 0; + p = buf; + grp = 0; + /* + * A simple automaton to process lines like + * Groups: 14 500 513 + */ + do { + c = *p++; + if (!c) { + /* refill buffer */ + got = read(fd, buf, BUFSZ); + buf[got] = 0; + p = buf; + c = *p++; /* 0 at end of file */ + } + switch (state) { + case INKEY : + if (key[matched] == c) { + if (!key[++matched]) + state = INSEP; + } else + if (key[0] == c) + matched = 1; + else + matched = 0; + break; + case INSEP : + if ((c >= '0') && (c <= '9')) { + grp = c - '0'; + state = INNUM; + } else + if ((c != ' ') && (c != '\t')) + state = INEND; + break; + case INNUM : + if ((c >= '0') && (c <= '9')) + grp = grp*10 + c - '0'; + else { + ismember = (grp == gid); + if ((c != ' ') && (c != '\t')) + state = INEND; + else + state = INSEP; + } + default : + break; + } + } while (!ismember && c && (state != INEND)); + close(fd); + if (!c) + ntfs_log_error("No group record found in %s\n",filename); + } else + ntfs_log_error("Could not open %s\n",filename); + } + return (ismember); +} + +/* + * Cacheing is done two-way : + * - from uid, gid and perm to securid (CACHED_SECURID) + * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) + * + * CACHED_SECURID data is kept in a most-recent-first list + * which should not be too long to be efficient. Its optimal + * size is depends on usage and is hard to determine. + * + * CACHED_PERMISSIONS data is kept in a two-level indexed array. It + * is optimal at the expense of storage. Use of a most-recent-first + * list would save memory and provide similar performances for + * standard usage, but not for file servers with too many file + * owners + * + * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS + * for legacy directories which were not allocated a security_id + * it is organized in a most-recent-first list. + * + * In main caches, data is never invalidated, as the meaning of + * a security_id only changes when user mapping is changed, which + * current implies remounting. However returned entries may be + * overwritten at next update, so data has to be copied elsewhere + * before another cache update is made. + * In legacy cache, data has to be invalidated when protection is + * changed. + * + * Though the same data may be found in both list, they + * must be kept separately : the interpretation of ACL + * in both direction are approximations which could be non + * reciprocal for some configuration of the user mapping data + * + * During the process of recompiling ntfs-3g from a tgz archive, + * security processing added 7.6% to the cpu time used by ntfs-3g + * and 30% if the cache is disabled. + */ + +static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *cache; + unsigned int index1; + unsigned int i; + + cache = (struct PERMISSIONS_CACHE*)NULL; + /* create the first permissions blocks */ + index1 = securindex >> CACHE_PERMISSIONS_BITS; + cache = (struct PERMISSIONS_CACHE*) + ntfs_malloc(sizeof(struct PERMISSIONS_CACHE) + + index1*sizeof(struct CACHED_PERMISSIONS*)); + if (cache) { + cache->head.last = index1; + cache->head.p_reads = 0; + cache->head.p_hits = 0; + cache->head.p_writes = 0; + *scx->pseccache = cache; + for (i=0; i<=index1; i++) + cache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + } + return (cache); +} + +/* + * Free memory used by caches + * The only purpose is to facilitate the detection of memory leaks + */ + +static void free_caches(struct SECURITY_CONTEXT *scx) +{ + unsigned int index1; + struct PERMISSIONS_CACHE *pseccache; + + pseccache = *scx->pseccache; + if (pseccache) { + for (index1=0; index1<=pseccache->head.last; index1++) + if (pseccache->cachetable[index1]) { +#if POSIXACLS + struct CACHED_PERMISSIONS *cacheentry; + unsigned int index2; + + for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) { + cacheentry = &pseccache->cachetable[index1][index2]; + if (cacheentry->valid + && cacheentry->pxdesc) + free(cacheentry->pxdesc); + } +#endif + free(pseccache->cachetable[index1]); + } + free(pseccache); + } +} + +static int compare(const struct CACHED_SECURID *cached, + const struct CACHED_SECURID *item) +{ +#if POSIXACLS + size_t csize; + size_t isize; + + /* only compare data and sizes */ + csize = (cached->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)cached->variable)->acccnt + + ((struct POSIX_SECURITY*)cached->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + isize = (item->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)item->variable)->acccnt + + ((struct POSIX_SECURITY*)item->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode) + || (csize != isize) + || (csize + && isize + && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl, + &((struct POSIX_SECURITY*)item->variable)->acl, csize))); +#else + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode)); +#endif +} + +static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, + const struct CACHED_PERMISSIONS_LEGACY *item) +{ + return (cached->mft_no != item->mft_no); +} + +/* + * Resize permission cache table + * do not call unless resizing is needed + * + * If allocation fails, the cache size is not updated + * Lack of memory is not considered as an error, the cache is left + * consistent and errno is not set. + */ + +static void resize_cache(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *oldcache; + struct PERMISSIONS_CACHE *newcache; + int newcnt; + int oldcnt; + unsigned int index1; + unsigned int i; + + oldcache = *scx->pseccache; + index1 = securindex >> CACHE_PERMISSIONS_BITS; + newcnt = index1 + 1; + if (newcnt <= ((CACHE_PERMISSIONS_SIZE + + (1 << CACHE_PERMISSIONS_BITS) + - 1) >> CACHE_PERMISSIONS_BITS)) { + /* expand cache beyond current end, do not use realloc() */ + /* to avoid losing data when there is no more memory */ + oldcnt = oldcache->head.last + 1; + newcache = (struct PERMISSIONS_CACHE*) + ntfs_malloc( + sizeof(struct PERMISSIONS_CACHE) + + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + if (newcache) { + memcpy(newcache,oldcache, + sizeof(struct PERMISSIONS_CACHE) + + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + free(oldcache); + /* mark new entries as not valid */ + for (i=newcache->head.last+1; i<=index1; i++) + newcache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + newcache->head.last = index1; + *scx->pseccache = newcache; + } + } +} + +/* + * Enter uid, gid and mode into cache, if possible + * + * returns the updated or created cache entry, + * or NULL if not possible (typically if there is no + * security id associated) + */ + +#if POSIXACLS +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + struct POSIX_SECURITY *pxdesc) +#else +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode) +#endif +{ + struct CACHED_PERMISSIONS *cacheentry; + struct CACHED_PERMISSIONS *cacheblock; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; +#if POSIXACLS + int pxsize; + struct POSIX_SECURITY *pxcached; +#endif + unsigned int index1; + unsigned int index2; + int i; + + /* cacheing is only possible if a security_id has been defined */ + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) { + /* + * Immediately test the most frequent situation + * where the entry exists + */ + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (cacheentry->valid && cacheentry->pxdesc) + free(cacheentry->pxdesc); + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } else { + if (!pcache) { + /* create the first cache block */ + pcache = create_caches(scx, securindex); + } else { + if (index1 > pcache->head.last) { + resize_cache(scx, securindex); + pcache = *scx->pseccache; + } + } + /* allocate block, if cache table was allocated */ + if (pcache && (index1 <= pcache->head.last)) { + cacheblock = (struct CACHED_PERMISSIONS*) + malloc(sizeof(struct CACHED_PERMISSIONS) + << CACHE_PERMISSIONS_BITS); + pcache->cachetable[index1] = cacheblock; + for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++) + cacheblock[i].valid = 0; + cacheentry = &cacheblock[index2]; + if (cacheentry) { + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } + } else + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + } else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; +#if CACHE_LEGACY_SIZE + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.perm.uid = uid; + wanted.perm.gid = gid; +#if POSIXACLS + wanted.perm.mode = pxdesc->mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); +#else + wanted.perm.mode = mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) { + cacheentry = &legacy->perm; +#if POSIXACLS + /* + * give direct access to the cached pxdesc + * in the permissions structure + */ + cacheentry->pxdesc = legacy->variable; +#endif + } + } +#endif + } + return (cacheentry); +} + +/* + * Fetch owner, group and permission of a file, if cached + * + * Beware : do not use the returned entry after a cache update : + * the cache may be relocated making the returned entry meaningless + * + * returns the cache entry, or NULL if not available + */ + +static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni) +{ + struct CACHED_PERMISSIONS *cacheentry; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; + unsigned int index1; + unsigned int index2; + + /* cacheing is only possible if a security_id has been defined */ + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (test_nino_flag(ni, v3_Extensions) + && (ni->security_id)) { + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + /* reject if entry is not valid */ + if (!cacheentry->valid) + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + else + pcache->head.p_hits++; + if (pcache) + pcache->head.p_reads++; + } + } +#if CACHE_LEGACY_SIZE + else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) cacheentry = &legacy->perm; + } + } +#endif +#if POSIXACLS + if (cacheentry && !cacheentry->pxdesc) { + ntfs_log_error("No Posix descriptor in cache\n"); + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } +#endif + return (cacheentry); +} + +/* + * Retrieve a security attribute from $Secure + */ + +static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) +{ + struct SII *psii; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int found; + size_t size; + size_t rdsize; + s64 offs; + ntfs_inode *ni; + ntfs_index_context *xsii; + char *securattr; + + securattr = (char*)NULL; + ni = vol->secure_ni; + xsii = vol->secure_xsii; + if (ni && xsii) { + ntfs_index_ctx_reinit(xsii); + found = + !ntfs_index_lookup((char*)&id, + sizeof(SII_INDEX_KEY), xsii); + if (found) { + psii = (struct SII*)xsii->entry; + size = + (size_t) le32_to_cpu(psii->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + /* work around bad alignment problem */ + realign.parts.dataoffsh = psii->dataoffsh; + realign.parts.dataoffsl = psii->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + + securattr = (char*)ntfs_malloc(size); + if (securattr) { + rdsize = ntfs_attr_data_read( + ni, STREAM_SDS, 4, + securattr, size, offs); + if ((rdsize != size) + || !ntfs_valid_descr(securattr, + rdsize)) { + /* error to be logged by caller */ + free(securattr); + securattr = (char*)NULL; + } + } + } else + if (errno != ENOENT) + ntfs_log_perror("Inconsistency in index $SII"); + } + if (!securattr) { + ntfs_log_error("Failed to retrieve a security descriptor\n"); + errno = EIO; + } + return (securattr); +} + +/* + * Get the security descriptor associated to a file + * + * Either : + * - read the security descriptor attribute (v1.x format) + * - or find the descriptor in $Secure:$SDS (v3.x format) + * + * in both case, sanity checks are done on the attribute and + * the descriptor can be assumed safe + * + * The returned descriptor is dynamically allocated and has to be freed + */ + +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) +{ + SII_INDEX_KEY securid; + char *securattr; + s64 readallsz; + + /* + * Warning : in some situations, after fixing by chkdsk, + * v3_Extensions are marked present (long standard informations) + * with a default security descriptor inserted in an + * attribute + */ + if (test_nino_flag(ni, v3_Extensions) + && vol->secure_ni && ni->security_id) { + /* get v3.x descriptor in $Secure */ + securid.security_id = ni->security_id; + securattr = retrievesecurityattr(vol,securid); + if (!securattr) + ntfs_log_error("Bad security descriptor for 0x%lx\n", + (long)le32_to_cpu(ni->security_id)); + } else { + /* get v1.x security attribute */ + readallsz = 0; + securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, &readallsz); + if (securattr && !ntfs_valid_descr(securattr, readallsz)) { + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); + free(securattr); + securattr = (char*)NULL; + } + } + if (!securattr) { + /* + * in some situations, there is no security + * descriptor, and chkdsk does not detect or fix + * anything. This could be a normal situation. + * When this happens, simulate a descriptor with + * minimum rights, so that a real descriptor can + * be created by chown or chmod + */ + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); + securattr = ntfs_build_descr(0, 0, adminsid, adminsid); + } + return (securattr); +} + +#if POSIXACLS + +/* + * Determine which access types to a file are allowed + * according to the relation of current process to the file + * + * Do not call if default_permissions is set + */ + +static int access_check_posix(struct SECURITY_CONTEXT *scx, + struct POSIX_SECURITY *pxdesc, mode_t request, + uid_t uid, gid_t gid) +{ + struct POSIX_ACE *pxace; + int userperms; + int groupperms; + int mask; + BOOL somegroup; + BOOL needgroups; + mode_t perms; + int i; + + perms = pxdesc->mode; + /* owner and root access */ + if (!scx->uid || (uid == scx->uid)) { + if (!scx->uid) { + /* root access if owner or other execution */ + if (perms & 0101) + perms = 07777; + else { + /* root access if some group execution */ + groupperms = 0; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER_OBJ : + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + groupperms |= pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + default : + break; + } + } + perms = (groupperms & mask & 1) | 6; + } + } else + perms &= 07700; + } else { + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ + userperms = -1; + groupperms = -1; + needgroups = FALSE; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER : + if ((uid_t)pxace->id == scx->uid) + userperms = pxace->perms; + break; + case POSIX_ACL_MASK : + mask = pxace->perms & 7; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; + default : + break; + } + } + /* designated users */ + if (userperms >= 0) + perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; + else { + /* owning group */ + if (!(~(perms >> 3) & request & mask) + && ((gid == scx->gid) + || groupmember(scx, scx->uid, gid))) + perms &= 07070; + else { + /* other groups */ + groupperms = -1; + somegroup = FALSE; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + if ((pxace->tag == POSIX_ACL_GROUP) + && groupmember(scx, uid, pxace->id)) { + if (!(~pxace->perms & request & mask)) + groupperms = pxace->perms; + somegroup = TRUE; + } + } + if (groupperms >= 0) + perms = (perms & 07000) + (groupperms & mask); + else + if (somegroup) + perms = 0; + else + perms &= 07007; + } + } + } + return (perms); +} + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * Do no call if default_permissions is set + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + struct POSIX_SECURITY *pxdesc; + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + perm = access_check_posix(scx,cached->pxdesc,request,uid,gid); + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, pxdesc); + } + if (pxdesc) { + perm = access_check_posix(scx,pxdesc,request,uid,gid); + free(pxdesc); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + } + return (perm); +} + +/* + * Get a Posix ACL + * + * returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + BOOL isdir; + size_t outsize; + + outsize = 0; /* default to error */ + if (!scx->mapping[MAPUSERS]) + errno = ENOTSUP; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) + pxdesc = cached->pxdesc; + else { + securattr = getsecurityattr(scx->vol, ni); + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + + /* + * fetch owner and group for cacheing + */ + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!(pxdesc->mode & 07777) + && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, + securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS) + enter_cache(scx, ni, uid, + gid, pxdesc); + } + free(securattr); + } else + pxdesc = (struct POSIX_SECURITY*)NULL; + } + + if (pxdesc) { + if (ntfs_valid_posix(pxdesc)) { + if (!strcmp(name,"system.posix_acl_default")) { + if (ni->mrec->flags + & MFT_RECORD_IS_DIRECTORY) + outsize = sizeof(struct POSIX_ACL) + + pxdesc->defcnt*sizeof(struct POSIX_ACE); + else { + /* + * getting default ACL from plain file : + * return EACCES if size > 0 as + * indicated in the man, but return ok + * if size == 0, so that ls does not + * display an error + */ + if (size > 0) { + outsize = 0; + errno = EACCES; + } else + outsize = sizeof(struct POSIX_ACL); + } + if (outsize && (outsize <= size)) { + memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL)); + memcpy(&value[sizeof(struct POSIX_ACL)], + &pxdesc->acl.ace[pxdesc->firstdef], + outsize-sizeof(struct POSIX_ACL)); + } + } else { + outsize = sizeof(struct POSIX_ACL) + + pxdesc->acccnt*sizeof(struct POSIX_ACE); + if (outsize <= size) + memcpy(value,&pxdesc->acl,outsize); + } + } else { + outsize = 0; + errno = EIO; + ntfs_log_error("Invalid Posix ACL built\n"); + } + if (!cached) + free(pxdesc); + } else + outsize = 0; + } + return (outsize ? (int)outsize : -errno); +} + +#else /* POSIXACLS */ + + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + BOOL isdir; + uid_t uid; + gid_t gid; + int perm; + + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + uid = cached->uid; + gid = cached->gid; + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, perm); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + if (perm >= 0) { + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm = 07777; + else + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; + } + } + return (perm); +} + +#endif /* POSIXACLS */ + +/* + * Get an NTFS ACL + * + * Returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) +{ + char *securattr; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + outsize = ntfs_attr_size(securattr); + if (outsize <= size) { + memcpy(value,securattr,outsize); + } + free(securattr); + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Get owner, group and permissions in an stat structure + * returns permissions, or -1 if there is a problem + */ + +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, struct stat *stbuf) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + const struct CACHED_PERMISSIONS *cached; + int perm; + BOOL isdir; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + stbuf->st_uid = cached->uid; + stbuf->st_gid = cached->gid; + stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; + } else { + perm = -1; /* default to error */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; +#else + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); +#endif + /* + * fetch owner and group for cacheing + */ + if (perm >= 0) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!perm && ntfs_same_sid(usid, adminsid)) { + stbuf->st_uid = + find_tenant(scx, + securattr); + if (stbuf->st_uid) + perm = 0700; + } else + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + stbuf->st_mode = + (stbuf->st_mode & ~07777) + perm; +#if POSIXACLS + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, pxdesc); + free(pxdesc); +#else + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, perm); +#endif + } + free(securattr); + } + } + } + return (perm); +} + +#if POSIXACLS + +/* + * Get the base for a Posix inheritance and + * build an inherited Posix descriptor + */ + +static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) +{ + const struct CACHED_PERMISSIONS *cached; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pydesc; + char *securattr; + const SID *usid; + const SID *gsid; + uid_t uid; + gid_t gid; + + pydesc = (struct POSIX_SECURITY*)NULL; + /* check whether parent directory is available in cache */ + cached = fetch_cache(scx,dir_ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + pxdesc = cached->pxdesc; + if (pxdesc) { + pydesc = ntfs_build_inherited_posix(pxdesc,mode, + scx->umask,isdir); + } + } else { + securattr = getsecurityattr(scx->vol, dir_ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + if (pxdesc && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(dir_ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, dir_ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(dir_ni, v3_Extensions)) { + enter_cache(scx, dir_ni, uid, + gid, pxdesc); + } + pydesc = ntfs_build_inherited_posix(pxdesc, + mode, scx->umask, isdir); + free(pxdesc); + } + free(securattr); + } + } + return (pydesc); +} + +/* + * Allocate a security_id for a file being created + * + * Returns zero if not possible (NTFS v3.x required) + */ + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + struct POSIX_SECURITY *pxdesc; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = pxdesc->mode & mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + free(pxdesc); + } +#endif + return (securid); +} + +/* + * Apply Posix inheritance to a newly created file + * (for NTFS 1.x only : no securid) + */ + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode) +{ + struct POSIX_SECURITY *pxdesc; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + int res; + + res = -1; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + /* Adjust Windows read-only flag */ + res = update_secur_descr(scx->vol, newattr, ni); + if (!res && !isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = pxdesc; + legacy.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(newattr); + + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + return (res); +} + +#else + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr(mode, isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } +#endif + return (securid); +} + +#endif + +/* + * Update ownership and mode of a file, reusing an existing + * security descriptor when possible + * + * Returns zero if successful + */ + +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode, + struct POSIX_SECURITY *pxdesc) +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode) +#endif +{ + int res; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + + res = 0; + + /* check whether target securid is known in cache */ + + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; +#if POSIXACLS + wanted.variable = (void*)pxdesc; + if (pxdesc) + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + else + wanted.varsize = 0; +#else + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + if (test_nino_flag(ni, v3_Extensions)) { + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) { + ni->security_id = cached->securid; + NInoSetDirty(ni); + } + } else cached = (struct CACHED_SECURID*)NULL; + + if (!cached) { + /* + * Do not use usid and gsid from former attributes, + * but recompute them to get repeatable results + * which can be kept in cache. + */ + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", + uid, gid); + usid = gsid = adminsid; + } +#if POSIXACLS + if (pxdesc) + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#endif + if (newattr) { + res = update_secur_descr(scx->vol, newattr, ni); + if (!res) { + /* adjust Windows read-only flag */ + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } + /* update cache, for subsequent use */ + if (test_nino_flag(ni, v3_Extensions)) { + wanted.securid = ni->security_id; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; +#if POSIXACLS + legacy.variable = wanted.variable; + legacy.varsize = wanted.varsize; +#else + legacy.variable = (void*)NULL; + legacy.varsize = 0; +#endif + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + res = -1; + } + } + return (res); +} + +/* + * Check whether user has ownership rights on a file + * + * Returns TRUE if allowed + * if not, errno tells why + */ + +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) +{ + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + uid_t processuid; + uid_t uid; + BOOL gotowner; + int allowed; + + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; +#endif + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } + } + return (allowed); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +#if POSIXACLS + +/* + * Set a new access or default Posix ACL to a file + * (or remove ACL if no input data) + * Validity of input data is checked after merging + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t processuid; + const SID *usid; + const SID *gsid; + uid_t uid; + uid_t gid; + int res; + BOOL isdir; + BOOL deflt; + BOOL exist; + int count; + struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc; + + /* get the current pxsec, either from cache or from old attribute */ + res = -1; + deflt = !strcmp(name,"system.posix_acl_default"); + if (size) + count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + else + count = 0; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = (struct POSIX_SECURITY*)NULL; + if ((!value + || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION)) + && (!deflt || isdir || (!size && !value))) { + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + oldpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (oldpxdesc) { + if (deflt) + exist = oldpxdesc->defcnt > 0; + else + exist = oldpxdesc->acccnt > 3; + if ((exist && (flags & XATTR_CREATE)) + || (!exist && (flags & XATTR_REPLACE))) { + errno = (exist ? EEXIST : ENODATA); + } else { + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + free(oldpxdesc); + } + free(oldattr); + } + } + } else + errno = EINVAL; + + if (newpxdesc) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) { + newpxdesc->mode &= ~S_ISGID; + } + res = ntfs_set_owner_mode(scx, ni, uid, gid, + newpxdesc->mode, newpxdesc); + } else + errno = EPERM; + free(newpxdesc); + } + return (res ? -1 : 0); +} + +/* + * Remove a default Posix ACL from a file + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) +{ + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); +} + +#endif + +/* + * Set a new NTFS ACL to a file + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + char *attr; + int res; + + res = -1; + if ((size > 0) + && !(flags & XATTR_CREATE) + && ntfs_valid_descr(value,size) + && (ntfs_attr_size(value) == size)) { + /* need copying in order to write */ + attr = (char*)ntfs_malloc(size); + if (attr) { + memcpy(attr,value,size); + res = update_secur_descr(scx->vol, attr, ni); + /* + * No need to invalidate standard caches : + * the relation between a securid and + * the associated protection is unchanged, + * only the relation between a file and + * its securid and protection is changed. + */ +#if CACHE_LEGACY_SIZE + /* + * we must however invalidate the legacy + * cache, which is based on inode numbers. + * For safety, invalidate even if updating + * failed. + */ + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = (char*)NULL; + legacy.varsize = 0; + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(attr); + } else + errno = ENOMEM; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Set new permissions to a file + * Checks user mapping has been defined before request for setting + * + * rejected if request is not originated by owner or root + * + * returns 0 on success + * -1 on failure, with errno = EIO + */ + +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t processuid; + uid_t uid; + uid_t gid; + int res; +#if POSIXACLS + BOOL isdir; + int pxsize; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + + /* get the current owner, either from cache or from old attribute */ + res = 0; + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } else + newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if POSIXACLS + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + + if (!res) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) + mode &= ~S_ISGID; +#if POSIXACLS + if (newpxdesc) { + newpxdesc->mode = mode; + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); + } else + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + errno = EPERM; + res = -1; /* neither owner nor root */ + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + if (newpxdesc) free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Create a default security descriptor for files whose descriptor + * cannot be inherited + */ + +int ntfs_sd_add_everyone(ntfs_inode *ni) +{ + /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + int ret, sd_len; + + /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ + /* + * Calculate security descriptor length. We have 2 sub-authorities in + * owner and group SIDs, but structure SID contain only one, so add + * 4 bytes to every SID. + */ + sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len); + if (!sd) + return -1; + + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; + + sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); + + sid = (SID*)((u8*)sid + sizeof(SID) + 4); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->group = cpu_to_le32((u8*)sid - (u8*)sd); + + acl = (ACL*)((u8*)sid + sizeof(SID) + 4); + acl->revision = ACL_REVISION; + acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); + acl->ace_count = const_cpu_to_le16(1); + sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); + + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); + ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */ + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 1; + ace->sid.sub_authority[0] = const_cpu_to_le32(0); + ace->sid.identifier_authority.value[5] = 1; + + ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd, + sd_len); + if (ret) + ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); + + free(sd); + return ret; +} + +/* + * Check whether user can access a file in a specific way + * + * Returns 1 if access is allowed, including user is root or no + * user mapping defined + * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX + * 0 and sets errno if there is a problem or if access + * is not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, + int accesstype) /* access type required (S_Ixxx values) */ +{ + int perm; + int res; + int allow; + struct stat stbuf; + + /* + * Always allow for root unless execution is requested. + * (was checked by fuse until kernel 2.6.29) + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] + || (!scx->uid + && (!(accesstype & S_IEXEC) + || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) + allow = 1; + else { + perm = ntfs_get_perm(scx, ni, accesstype); + if (perm >= 0) { + res = EACCES; + switch (accesstype) { + case S_IEXEC: + allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + break; + case S_IWRITE: + allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0; + break; + case S_IWRITE + S_IEXEC: + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD: + allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0; + break; + case S_IREAD + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); + break; + case S_IWRITE + S_IEXEC + S_ISVTX: + if (perm & S_ISVTX) { + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid)) + allow = 1; + else + allow = 2; + } else + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + default : + res = EINVAL; + allow = 0; + break; + } + if (!allow) + errno = res; + } else + allow = 0; + } + return (allow); +} + +#if 0 /* not needed any more */ + +/* + * Check whether user can access the parent directory + * of a file in a specific way + * + * Returns true if access is allowed, including user is root and + * no user mapping defined + * + * Sets errno if there is a problem or if not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype) +{ + int allow; + char *dirpath; + char *name; + ntfs_inode *ni; + ntfs_inode *dir_ni; + struct stat stbuf; + + allow = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); + if (dir_ni) { + allow = ntfs_allowed_access(scx, + dir_ni, accesstype); + ntfs_inode_close(dir_ni); + /* + * for an not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allow == 2)) { + ni = ntfs_pathname_to_inode(scx->vol, NULL, + path); + allow = FALSE; + if (ni) { + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid); + ntfs_inode_close(ni); + } + } + } + free(dirpath); + } + return (allow); /* errno is set if not allowed */ +} + +#endif + +/* + * Define a new owner/group to a file + * + * returns zero if successful + */ + +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + mode_t mode; + int perm; + BOOL isdir; + int res; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + BOOL pxdescbuilt = FALSE; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; + mode = cached->mode; +#if POSIXACLS + pxdesc = cached->pxdesc; + if (!pxdesc) + res = -1; +#endif + } else { + fileuid = 0; + filegid = 0; + mode = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (pxdesc) { + pxdescbuilt = TRUE; + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + mode = perm = pxdesc->mode; + } else + res = -1; +#else + mode = perm = ntfs_build_permissions(oldattr, + usid, gsid, isdir); + if (perm >= 0) { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } else + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; + /* clear setuid and setgid if owner has changed */ + /* unless request originated by root */ + if (uid && (fileuid != uid)) + mode &= 01777; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, pxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } +#if POSIXACLS + if (pxdescbuilt) + free(pxdesc); +#endif + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } + return (res ? -1 : 0); +} + +/* + * Define new owner/group and mode to a file + * + * returns zero if successful + */ + +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) +{ + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t fileuid; + uid_t filegid; + int res; +#if POSIXACLS + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const SID *gsid; + BOOL isdir; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } +#endif + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if POSIXACLS + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Build a security id for a descriptor inherited from + * parent directory the Windows way + */ + +static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, + const char *parentattr, BOOL fordir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *pphead; + const ACL *ppacl; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + int offpacl; + int offowner; + int offgroup; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + ACL *pnacl; + int parentattrsz; + char *newattr; + int newattrsz; + int aclsz; + int usidsz; + int gsidsz; + int pos; + le32 securid; + + parentattrsz = ntfs_attr_size(parentattr); + pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr; + if (scx->mapping[MAPUSERS]) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid); + if (!usid) + usid = adminsid; + if (!gsid) + gsid = adminsid; + } else { + /* + * If there is no user mapping, we have to copy owner + * and group from parent directory. + * Windows never has to do that, because it can always + * rely on a user mapping + */ + offowner = le32_to_cpu(pphead->owner); + usid = (const SID*)&parentattr[offowner]; + offgroup = le32_to_cpu(pphead->group); + gsid = (const SID*)&parentattr[offgroup]; + } + /* + * new attribute is smaller than parent's + * except for differences in SIDs which appear in + * owner, group and possible grants and denials in + * generic creator-owner and creator-group ACEs. + * For directories, an ACE may be duplicated for + * access and inheritance, so we double the count. + */ + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + newattrsz = parentattrsz + 3*usidsz + 3*gsidsz; + if (fordir) + newattrsz *= 2; + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + pnhead->control = SE_SELF_RELATIVE; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + /* + * locate and inherit DACL + * do not test SE_DACL_PRESENT (wrong for "DR Watson") + */ + pnhead->dacl = const_cpu_to_le32(0); + if (pphead->dacl) { + offpacl = le32_to_cpu(pphead->dacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->dacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_DACL_PRESENT; + } + } + /* + * locate and inherit SACL + */ + pnhead->sacl = const_cpu_to_le32(0); + if (pphead->sacl) { + offpacl = le32_to_cpu(pphead->sacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); + if (aclsz) { + pnhead->sacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_SACL_PRESENT; + } + } + /* + * inherit or redefine owner + */ + memcpy(&newattr[pos],usid,usidsz); + pnhead->owner = cpu_to_le32(pos); + pos += usidsz; + /* + * inherit or redefine group + */ + memcpy(&newattr[pos],gsid,gsidsz); + pnhead->group = cpu_to_le32(pos); + pos += usidsz; + securid = setsecurityattr(scx->vol, + (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); + free(newattr); + } else + securid = const_cpu_to_le32(0); + return (securid); +} + +/* + * Get an inherited security id + * + * For Windows compatibility, the normal initial permission setting + * may be inherited from the parent directory instead of being + * defined by the creation arguments. + * + * The following creates an inherited id for that purpose. + * + * Note : the owner and group of parent directory are also + * inherited (which is not the case on Windows) if no user mapping + * is defined. + * + * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) + */ + +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir) +{ + struct CACHED_PERMISSIONS *cached; + char *parentattr; + le32 securid; + + securid = const_cpu_to_le32(0); + cached = (struct CACHED_PERMISSIONS*)NULL; + /* + * Try to get inherited id from cache + */ + if (test_nino_flag(dir_ni, v3_Extensions) + && dir_ni->security_id) { + cached = fetch_cache(scx, dir_ni); + if (cached) + securid = (fordir ? cached->inh_dirid + : cached->inh_fileid); + } + /* + * Not cached or not available in cache, compute it all + * Note : if parent directory has no id, it is not cacheable + */ + if (!securid) { + parentattr = getsecurityattr(scx->vol, dir_ni); + if (parentattr) { + securid = build_inherited_id(scx, + parentattr, fordir); + free(parentattr); + /* + * Store the result into cache for further use + */ + if (securid) { + cached = fetch_cache(scx, dir_ni); + if (cached) { + if (fordir) + cached->inh_dirid = securid; + else + cached->inh_fileid = securid; + } + } + } + } + return (securid); +} + +/* + * Link a group to a member of group + * + * Returns 0 if OK, -1 (and errno set) if error + */ + +static int link_single_group(struct MAPPING *usermapping, struct passwd *user, + gid_t gid) +{ + struct group *group; + char **grmem; + int grcnt; + gid_t *groups; + int res; + + res = 0; + group = getgrgid(gid); + if (group && group->gr_mem) { + grcnt = usermapping->grcnt; + groups = usermapping->groups; + grmem = group->gr_mem; + while (*grmem && strcmp(user->pw_name, *grmem)) + grmem++; + if (*grmem) { + if (!grcnt) + groups = (gid_t*)malloc(sizeof(gid_t)); + else + groups = (gid_t*)realloc(groups, + (grcnt+1)*sizeof(gid_t)); + if (groups) + groups[grcnt++] = gid; + else { + res = -1; + errno = ENOMEM; + } + } + usermapping->grcnt = grcnt; + usermapping->groups = groups; + } + return (res); +} + + +/* + * Statically link group to users + * This is based on groups defined in /etc/group and does not take + * the groups dynamically set by setgroups() nor any changes in + * /etc/group into account + * + * Only mapped groups and root group are linked to mapped users + * + * Returns 0 if OK, -1 (and errno set) if error + * + */ + +static int link_group_members(struct SECURITY_CONTEXT *scx) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + struct passwd *user; + int res; + + res = 0; + for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res; + usermapping=usermapping->next) { + usermapping->grcnt = 0; + usermapping->groups = (gid_t*)NULL; + user = getpwuid(usermapping->xid); + if (user && user->pw_name) { + for (groupmapping=scx->mapping[MAPGROUPS]; + groupmapping && !res; + groupmapping=groupmapping->next) { + if (link_single_group(usermapping, user, + groupmapping->xid)) + res = -1; + } + if (!res && link_single_group(usermapping, + user, (gid_t)0)) + res = -1; + } + } + return (res); +} + +/* + * Apply default single user mapping + * returns zero if successful + */ + +static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, const SID *usid) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + SID *sid; + int sidsz; + int res; + + res = -1; + sidsz = ntfs_sid_size(usid); + sid = (SID*)ntfs_malloc(sidsz); + if (sid) { + memcpy(sid,usid,sidsz); + usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (usermapping) { + groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (groupmapping) { + usermapping->sid = sid; + usermapping->xid = uid; + usermapping->next = (struct MAPPING*)NULL; + groupmapping->sid = sid; + groupmapping->xid = gid; + groupmapping->next = (struct MAPPING*)NULL; + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + res = 0; + } + } + } + return (res); +} + +/* + * Make sure there are no ambiguous mapping + * Ambiguous mapping may lead to undesired configurations and + * we had rather be safe until the consequences are understood + */ + +#if 0 /* not activated for now */ + +static BOOL check_mapping(const struct MAPPING *usermapping, + const struct MAPPING *groupmapping) +{ + const struct MAPPING *mapping1; + const struct MAPPING *mapping2; + BOOL ambiguous; + + ambiguous = FALSE; + for (mapping1=usermapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + for (mapping1=groupmapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + return (ambiguous); +} + +#endif + +#if 0 /* not used any more */ + +/* + * Try and apply default single user mapping + * returns zero if successful + */ + +static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + char *securattr; + const SID *usid; + int res; + + res = -1; + ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); + if (ni) { + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; + if (ntfs_is_user_sid(usid)) + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); + free(securattr); + } + ntfs_inode_close(ni); + } + return (res); +} + +#endif + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Build the user mapping + * - according to a mapping file if defined (or default present), + * - or try default single user mapping if possible + * + * The mapping is specific to a mounted device + * No locking done, mounting assumed non multithreaded + * + * returns zero if mapping is successful + * (failure should not be interpreted as an error) + */ + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) +{ + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + ntfs_inode *ni; + int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; + + /* be sure not to map anything until done */ + scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; + scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL; + + if (!usermap_path) usermap_path = MAPPINGFILE; + if (usermap_path[0] == '/') { + fd = open(usermap_path,O_RDONLY); + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } else + firstitem = (struct MAPLIST*)NULL; + } else { + ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path); + if (ni) { + firstitem = ntfs_read_mapping(localread, ni); + ntfs_inode_close(ni); + } else + firstitem = (struct MAPLIST*)NULL; + } + + + if (firstitem) { + usermapping = ntfs_do_user_mapping(firstitem); + groupmapping = ntfs_do_group_mapping(firstitem); + if (usermapping && groupmapping) { + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + } else + ntfs_log_error("There were no valid user or no valid group\n"); + /* now we can free the memory copy of input text */ + /* and rely on internal representation */ + while (firstitem) { + item = firstitem->next; + free(firstitem); + firstitem = item; + } + } else { + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) + ntfs_log_info("Using default user mapping\n"); + } + } + return (!scx->mapping[MAPUSERS] || link_group_members(scx)); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs attribute into an extended attribute + * The attribute is returned according to cpu endianness + */ + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) +{ + u32 attrib; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + outsize = sizeof(FILE_ATTR_FLAGS); + if (size >= outsize) { + if (value) + memcpy(value,&attrib,outsize); + else + errno = EINVAL; + } + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Return the ntfs attribute into an extended attribute + * The attribute is expected according to cpu endianness + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + u32 attrib; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = -1; + if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { + if (!(flags & XATTR_CREATE)) { + /* copy to avoid alignment problems */ + memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); + settable = FILE_ATTR_SETTABLE; + res = 0; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); + } + } else + errno = EEXIST; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open $Secure once for all + * returns zero if it succeeds + * non-zero if it fails. This is not an error (on NTFS v1.x) + */ + + +int ntfs_open_secure(ntfs_volume *vol) +{ + ntfs_inode *ni; + int res; + + res = -1; + vol->secure_ni = (ntfs_inode*)NULL; + vol->secure_xsii = (ntfs_index_context*)NULL; + vol->secure_xsdh = (ntfs_index_context*)NULL; + if (vol->major_ver >= 3) { + /* make sure this is a genuine $Secure inode 9 */ + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (ni && (ni->mft_no == 9)) { + vol->secure_reentry = 0; + vol->secure_xsii = ntfs_index_ctx_get(ni, + sii_stream, 4); + vol->secure_xsdh = ntfs_index_ctx_get(ni, + sdh_stream, 4); + if (ni && vol->secure_xsii && vol->secure_xsdh) { + vol->secure_ni = ni; + res = 0; + } + } + } + return (res); +} + +/* + * Final cleaning + * Allocated memory is freed to facilitate the detection of memory leaks + */ + +void ntfs_close_secure(struct SECURITY_CONTEXT *scx) +{ + ntfs_volume *vol; + + vol = scx->vol; + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsii); + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_inode_close(vol->secure_ni); + + } + ntfs_free_mapping(scx->mapping); + free_caches(scx); +} + +/* + * API for direct access to security descriptors + * based on Win32 API + */ + + +/* + * Selective feeding of a security descriptor into user buffer + * + * Returns TRUE if successful + */ + +static BOOL feedsecurityattr(const char *attr, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + const ACL *pdacl; + const ACL *psacl; + const SID *pusid; + const SID *pgsid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int daclsz; + unsigned int saclsz; + unsigned int usidsz; + unsigned int gsidsz; + unsigned int size; /* size of requested attributes */ + BOOL ok; + unsigned int pos; + unsigned int avail; + le16 control; + + avail = 0; + control = SE_SELF_RELATIVE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + size = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* locate DACL if requested and available */ + if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) { + offdacl = le32_to_cpu(phead->dacl); + pdacl = (const ACL*)&attr[offdacl]; + daclsz = le16_to_cpu(pdacl->size); + size += daclsz; + avail |= DACL_SECURITY_INFORMATION; + } else + offdacl = daclsz = 0; + + /* locate owner if requested and available */ + offowner = le32_to_cpu(phead->owner); + if (offowner && (selection & OWNER_SECURITY_INFORMATION)) { + /* find end of USID */ + pusid = (const SID*)&attr[offowner]; + usidsz = ntfs_sid_size(pusid); + size += usidsz; + avail |= OWNER_SECURITY_INFORMATION; + } else + offowner = usidsz = 0; + + /* locate group if requested and available */ + offgroup = le32_to_cpu(phead->group); + if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) { + /* find end of GSID */ + pgsid = (const SID*)&attr[offgroup]; + gsidsz = ntfs_sid_size(pgsid); + size += gsidsz; + avail |= GROUP_SECURITY_INFORMATION; + } else + offgroup = gsidsz = 0; + + /* locate SACL if requested and available */ + if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) { + /* find end of SACL */ + offsacl = le32_to_cpu(phead->sacl); + psacl = (const ACL*)&attr[offsacl]; + saclsz = le16_to_cpu(psacl->size); + size += saclsz; + avail |= SACL_SECURITY_INFORMATION; + } else + offsacl = saclsz = 0; + + /* + * Check having enough size in destination buffer + * (required size is returned nevertheless so that + * the request can be reissued with adequate size) + */ + if (size > buflen) { + *psize = size; + errno = EINVAL; + ok = FALSE; + } else { + if (selection & OWNER_SECURITY_INFORMATION) + control |= phead->control & SE_OWNER_DEFAULTED; + if (selection & GROUP_SECURITY_INFORMATION) + control |= phead->control & SE_GROUP_DEFAULTED; + if (selection & DACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + if (selection & SACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy header and feed new flags, even if no detailed data + */ + memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf; + pnhead->control = control; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* copy DACL if requested and available */ + if (selection & avail & DACL_SECURITY_INFORMATION) { + pnhead->dacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offdacl],daclsz); + pos += daclsz; + } else + pnhead->dacl = const_cpu_to_le32(0); + + /* copy SACL if requested and available */ + if (selection & avail & SACL_SECURITY_INFORMATION) { + pnhead->sacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offsacl],saclsz); + pos += saclsz; + } else + pnhead->sacl = const_cpu_to_le32(0); + + /* copy owner if requested and available */ + if (selection & avail & OWNER_SECURITY_INFORMATION) { + pnhead->owner = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offowner],usidsz); + pos += usidsz; + } else + pnhead->owner = const_cpu_to_le32(0); + + /* copy group if requested and available */ + if (selection & avail & GROUP_SECURITY_INFORMATION) { + pnhead->group = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offgroup],gsidsz); + pos += gsidsz; + } else + pnhead->group = const_cpu_to_le32(0); + if (pos != size) + ntfs_log_error("Error in security descriptor size\n"); + *psize = size; + ok = TRUE; + } + + return (ok); +} + +/* + * Merge a new security descriptor into the old one + * and assign to designated file + * + * Returns TRUE if successful + */ + +static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr, + const char *newattr, u32 selection, ntfs_inode *ni) +{ + const SECURITY_DESCRIPTOR_RELATIVE *oldhead; + const SECURITY_DESCRIPTOR_RELATIVE *newhead; + SECURITY_DESCRIPTOR_RELATIVE *targhead; + const ACL *pdacl; + const ACL *psacl; + const SID *powner; + const SID *pgroup; + int offdacl; + int offsacl; + int offowner; + int offgroup; + unsigned int size; + le16 control; + char *target; + int pos; + int oldattrsz; + int newattrsz; + BOOL ok; + + ok = FALSE; /* default return */ + oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; + newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr; + oldattrsz = ntfs_attr_size(oldattr); + newattrsz = ntfs_attr_size(newattr); + target = (char*)ntfs_malloc(oldattrsz + newattrsz); + if (target) { + targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + control = SE_SELF_RELATIVE; + /* + * copy new DACL if selected + * or keep old DACL if any + */ + if ((selection & DACL_SECURITY_INFORMATION) ? + newhead->dacl : oldhead->dacl) { + if (selection & DACL_SECURITY_INFORMATION) { + offdacl = le32_to_cpu(newhead->dacl); + pdacl = (const ACL*)&newattr[offdacl]; + } else { + offdacl = le32_to_cpu(oldhead->dacl); + pdacl = (const ACL*)&oldattr[offdacl]; + } + size = le16_to_cpu(pdacl->size); + memcpy(&target[pos], pdacl, size); + targhead->dacl = cpu_to_le32(pos); + pos += size; + } else + targhead->dacl = const_cpu_to_le32(0); + if (selection & DACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_PROTECTED); + if (newhead->control & SE_DACL_AUTO_INHERIT_REQ) + control |= SE_DACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + /* + * copy new SACL if selected + * or keep old SACL if any + */ + if ((selection & SACL_SECURITY_INFORMATION) ? + newhead->sacl : oldhead->sacl) { + if (selection & SACL_SECURITY_INFORMATION) { + offsacl = le32_to_cpu(newhead->sacl); + psacl = (const ACL*)&newattr[offsacl]; + } else { + offsacl = le32_to_cpu(oldhead->sacl); + psacl = (const ACL*)&oldattr[offsacl]; + } + size = le16_to_cpu(psacl->size); + memcpy(&target[pos], psacl, size); + targhead->sacl = cpu_to_le32(pos); + pos += size; + } else + targhead->sacl = const_cpu_to_le32(0); + if (selection & SACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_PROTECTED); + if (newhead->control & SE_SACL_AUTO_INHERIT_REQ) + control |= SE_SACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy new OWNER if selected + * or keep old OWNER if any + */ + if ((selection & OWNER_SECURITY_INFORMATION) ? + newhead->owner : oldhead->owner) { + if (selection & OWNER_SECURITY_INFORMATION) { + offowner = le32_to_cpu(newhead->owner); + powner = (const SID*)&newattr[offowner]; + } else { + offowner = le32_to_cpu(oldhead->owner); + powner = (const SID*)&oldattr[offowner]; + } + size = ntfs_sid_size(powner); + memcpy(&target[pos], powner, size); + targhead->owner = cpu_to_le32(pos); + pos += size; + } else + targhead->owner = const_cpu_to_le32(0); + if (selection & OWNER_SECURITY_INFORMATION) + control |= newhead->control & SE_OWNER_DEFAULTED; + else + control |= oldhead->control & SE_OWNER_DEFAULTED; + /* + * copy new GROUP if selected + * or keep old GROUP if any + */ + if ((selection & GROUP_SECURITY_INFORMATION) ? + newhead->group : oldhead->group) { + if (selection & GROUP_SECURITY_INFORMATION) { + offgroup = le32_to_cpu(newhead->group); + pgroup = (const SID*)&newattr[offgroup]; + control |= newhead->control + & SE_GROUP_DEFAULTED; + } else { + offgroup = le32_to_cpu(oldhead->group); + pgroup = (const SID*)&oldattr[offgroup]; + control |= oldhead->control + & SE_GROUP_DEFAULTED; + } + size = ntfs_sid_size(pgroup); + memcpy(&target[pos], pgroup, size); + targhead->group = cpu_to_le32(pos); + pos += size; + } else + targhead->group = const_cpu_to_le32(0); + if (selection & GROUP_SECURITY_INFORMATION) + control |= newhead->control & SE_GROUP_DEFAULTED; + else + control |= oldhead->control & SE_GROUP_DEFAULTED; + targhead->revision = SECURITY_DESCRIPTOR_REVISION; + targhead->alignment = 0; + targhead->control = control; + ok = !update_secur_descr(vol, target, ni); + free(target); + } + return (ok); +} + +/* + * Return the security descriptor of a file + * This is intended to be similar to GetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI GetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION RequestedInformation, + * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, + * __in DWORD nLength, + * __out LPDWORD lpnLengthNeeded + * ); + * + */ + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + ntfs_inode *ni; + char *attr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attr = getsecurityattr(scapi->security.vol, ni); + if (attr) { + if (feedsecurityattr(attr,selection, + buf,buflen,psize)) { + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(attr); + } + ntfs_inode_close(ni); + } else + errno = ENOENT; + if (!res) *psize = 0; + } else + errno = EINVAL; /* do not clear *psize */ + return (res); +} + + +/* + * Set the security descriptor of a file or directory + * This is intended to be similar to SetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI SetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION SecurityInformation, + * __in PSECURITY_DESCRIPTOR pSecurityDescriptor + * ); + */ + +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + int attrsz; + BOOL missing; + char *oldattr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && attr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + attrsz = ntfs_attr_size(attr); + /* if selected, owner and group must be present or defaulted */ + missing = ((selection & OWNER_SECURITY_INFORMATION) + && !phead->owner + && !(phead->control & SE_OWNER_DEFAULTED)) + || ((selection & GROUP_SECURITY_INFORMATION) + && !phead->group + && !(phead->control & SE_GROUP_DEFAULTED)); + if (!missing + && (phead->control & SE_SELF_RELATIVE) + && ntfs_valid_descr(attr, attrsz)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, + NULL, path); + if (ni) { + oldattr = getsecurityattr(scapi->security.vol, + ni); + if (oldattr) { + if (mergesecurityattr( + scapi->security.vol, + oldattr, attr, + selection, ni)) { + if (test_nino_flag(ni, + v3_Extensions)) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(oldattr); + } + ntfs_inode_close(ni); + } + } else + errno = EINVAL; + } else + errno = EINVAL; + return (res); +} + + +/* + * Return the attributes of a file + * This is intended to be similar to GetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) + * + * The Win32 API is : + * + * DWORD WINAPI GetFileAttributes( + * __in LPCTSTR lpFileName + * ); + */ + +int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path) +{ + ntfs_inode *ni; + s32 attrib; + + attrib = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + + ntfs_inode_close(ni); + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (attrib); +} + + +/* + * Set attributes to a file or directory + * This is intended to be similar to SetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * Only a few flags can be set (same list as Win32) + * + * returns zero if unsuccessful (following Win32 conventions) + * nonzero if successful + * + * The Win32 API is : + * + * BOOL WINAPI SetFileAttributes( + * __in LPCTSTR lpFileName, + * __in DWORD dwFileAttributes + * ); + */ + +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib) +{ + ntfs_inode *ni; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + settable = FILE_ATTR_SETTABLE; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + if (!ntfs_inode_close(ni)) + res = -1; + } else + errno = ENOENT; + } + return (res); +} + + +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context) +{ + ntfs_inode *ni; + BOOL ok; + s64 pos; + + ok = FALSE; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && callback) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + pos = 0; + ntfs_readdir(ni,&pos,context,callback); + ok = !ntfs_inode_close(ni); + } else { + ntfs_inode_close(ni); + errno = ENOTDIR; + } + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (ok); +} + +/* + * read $SDS (for auditing security data) + * + * Returns the number or read bytes, or -1 if there is an error + */ + +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset) +{ + int got; + + got = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + if (scapi->security.vol->secure_ni) + got = ntfs_attr_data_read(scapi->security.vol->secure_ni, + STREAM_SDS, 4, buf, size, offset); + else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (got); +} + +/* + * read $SII (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SII_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsii; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsii = scapi->security.vol->secure_xsii; + if (xsii) { + if (!entry) { + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SII_INDEX_KEY), xsii); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsii->entry; + } else + ret = ntfs_index_next(entry,xsii); + if (!ret) + errno = ENODATA; + } else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (ret); +} + +/* + * read $SDH (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SDH_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsdh; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsdh = scapi->security.vol->secure_xsdh; + if (xsdh) { + if (!entry) { + key.hash = const_cpu_to_le32(0); + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsdh->entry; + } else + ret = ntfs_index_next(entry,xsdh); + if (!ret) + errno = ENODATA; + } else errno = ENOTSUP; + } else + errno = EINVAL; + return (ret); +} + +/* + * Get the mapped user SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf) +{ + const SID *usid; + BIGSID defusid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid); + if (usid) { + size = ntfs_sid_size(usid); + memcpy(buf,usid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the mapped group SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf) +{ + const SID *gsid; + BIGSID defgsid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid); + if (gsid) { + size = ntfs_sid_size(gsid); + memcpy(buf,gsid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the user mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid) +{ + int uid; + + uid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) { + if (ntfs_same_sid(usid,adminsid)) + uid = 0; + else { + uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid); + if (!uid) { + uid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (uid); +} + +/* + * Get the group mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) +{ + int gid; + + gid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) { + if (ntfs_same_sid(gsid,adminsid)) + gid = 0; + else { + gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid); + if (!gid) { + gid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (gid); +} + +/* + * Initializations before calling ntfs_get_file_security() + * ntfs_set_file_security() and ntfs_read_directory() + * + * Only allowed for root + * + * Returns an (obscured) struct SECURITY_API* needed for further calls + * NULL if not root (EPERM) or device is mounted (EBUSY) + */ + +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags) +{ + ntfs_volume *vol; + unsigned long mntflag; + int mnt; + struct SECURITY_API *scapi; + struct SECURITY_CONTEXT *scx; + + scapi = (struct SECURITY_API*)NULL; + mnt = ntfs_check_if_mounted(device, &mntflag); + if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) { + vol = ntfs_mount(device, flags); + if (vol) { + scapi = (struct SECURITY_API*) + ntfs_malloc(sizeof(struct SECURITY_API)); + if (!ntfs_volume_get_free_space(vol) + && scapi) { + scapi->magic = MAGIC_API; + scapi->seccache = (struct PERMISSIONS_CACHE*)NULL; + scx = &scapi->security; + scx->vol = vol; + scx->uid = getuid(); + scx->gid = getgid(); + scx->pseccache = &scapi->seccache; + scx->vol->secure_flags = 0; + /* accept no mapping and no $Secure */ + ntfs_build_mapping(scx,(const char*)NULL,TRUE); + ntfs_open_secure(vol); + } else { + if (scapi) + free(scapi); + else + errno = ENOMEM; + mnt = ntfs_umount(vol,FALSE); + scapi = (struct SECURITY_API*)NULL; + } + } + } else + if (getuid()) + errno = EPERM; + else + errno = EBUSY; + return (scapi); +} + +/* + * Leaving after ntfs_initialize_file_security() + * + * Returns FALSE if FAILED + */ + +BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) +{ + int ok; + ntfs_volume *vol; + + ok = FALSE; + if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { + vol = scapi->security.vol; + ntfs_close_secure(&scapi->security); + free(scapi); + if (!ntfs_umount(vol, 0)) + ok = TRUE; + } + return (ok); +} + diff --git a/lib/libntfs/src/source/security.h b/lib/libntfs/src/source/security.h new file mode 100644 index 0000000..9d7dd33 --- /dev/null +++ b/lib/libntfs/src/source/security.h @@ -0,0 +1,361 @@ +/* + * security.h - Exports for handling security/ACLs in NTFS. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SECURITY_H +#define _NTFS_SECURITY_H + +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "dir.h" + +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif + +typedef u16 be16; +typedef u32 be32; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) +#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) +#else +#define const_cpu_to_be16(x) (x) +#define const_cpu_to_be32(x) (x) +#endif + +/* + * item in the mapping list + */ + +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ +}; + +/* + * Entry in the permissions cache + * Note : this cache is not organized as a generic cache + */ + +struct CACHED_PERMISSIONS { + uid_t uid; + gid_t gid; + le32 inh_fileid; + le32 inh_dirid; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + unsigned int pxdescsize:16; +#endif + unsigned int mode:12; + unsigned int valid:1; +} ; + +/* + * Entry in the permissions cache for directories with no security_id + */ + +struct CACHED_PERMISSIONS_LEGACY { + struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 mft_no; + struct CACHED_PERMISSIONS perm; +} ; + +/* + * Entry in the securid cache + */ + +struct CACHED_SECURID { + struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + uid_t uid; + gid_t gid; + unsigned int dmode; + le32 securid; +} ; + +/* + * Header of the security cache + * (has no cache structure by itself) + */ + +struct CACHED_PERMISSIONS_HEADER { + unsigned int last; + /* statistics for permissions */ + unsigned long p_writes; + unsigned long p_reads; + unsigned long p_hits; +} ; + +/* + * The whole permissions cache + */ + +struct PERMISSIONS_CACHE { + struct CACHED_PERMISSIONS_HEADER head; + struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */ +} ; + +/* + * Security flags values + */ + +enum { + SECURITY_DEFAULT, /* rely on fuse for permissions checking */ + SECURITY_RAW, /* force same ownership/permissions on files */ + SECURITY_ACL, /* enable Posix ACLs (when compiled in) */ + SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ + SECURITY_STATICGRPS, /* use static groups for access control */ + SECURITY_WANTED /* a security related option was present */ +} ; + +/* + * Security context, needed by most security functions + */ + +enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; + +struct SECURITY_CONTEXT { + ntfs_volume *vol; + struct MAPPING *mapping[MAPCOUNT]; + struct PERMISSIONS_CACHE **pseccache; + uid_t uid; /* uid of user requesting (not the mounter) */ + gid_t gid; /* gid of user requesting (not the mounter) */ + pid_t tid; /* thread id of thread requesting */ + mode_t umask; /* umask of requesting thread */ + } ; + +#if POSIXACLS + +/* + * Posix ACL structures + */ + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} __attribute__((__packed__)); + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + s32 alignment[0]; + struct POSIX_ACL acl; +} ; + +/* + * Posix tags, cpu-endian 16 bits + */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ +} ; + +#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK) + +/* + * Posix permissions, cpu-endian 16 bits + */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ +} ; + +#define POSIX_VERSION 2 + +#endif + +extern BOOL ntfs_guid_is_zero(const GUID *guid); +extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); + +/** + * ntfs_sid_is_valid - determine if a SID is valid + * @sid: SID for which to determine if it is valid + * + * Determine if the SID pointed to by @sid is valid. + * + * Return TRUE if it is valid and FALSE otherwise. + */ +static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) +{ + if (!sid || sid->revision != SID_REVISION || + sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) + return FALSE; + return TRUE; +} + +extern int ntfs_sid_to_mbs_size(const SID *sid); +extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, + size_t sid_str_size); +extern void ntfs_generate_guid(GUID *guid); +extern int ntfs_sd_add_everyone(ntfs_inode *ni); + +extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, + const u32 len); + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, int accesstype); +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype); + +#if POSIXACLS +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); +#else +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir); +#endif +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + mode_t mode, struct POSIX_SECURITY *pxdesc); +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#endif +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir); +int ntfs_open_secure(ntfs_volume *vol); +void ntfs_close_secure(struct SECURITY_CONTEXT *scx); + +#if POSIXACLS + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); +#endif + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + + +/* + * Security API for direct access to security descriptors + * based on Win32 API + */ + +#define MAGIC_API 0x09042009 + +struct SECURITY_API { + u32 magic; + struct SECURITY_CONTEXT security; + struct PERMISSIONS_CACHE *seccache; +} ; + +/* + * The following constants are used in interfacing external programs. + * They are not to be stored on disk and must be defined in their + * native cpu representation. + * When disk representation (le) is needed, use SE_DACL_PRESENT, etc. + */ +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize); +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr); +int ntfs_get_file_attributes(struct SECURITY_API *scapi, + const char *path); +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib); +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context); +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset); +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags); +BOOL ntfs_leave_file_security(struct SECURITY_API *scx); + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf); +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid); +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid); + +#endif /* defined _NTFS_SECURITY_H */ diff --git a/lib/libntfs/src/source/support.h b/lib/libntfs/src/source/support.h new file mode 100644 index 0000000..6af4761 --- /dev/null +++ b/lib/libntfs/src/source/support.h @@ -0,0 +1,85 @@ +/* + * support.h - Useful definitions and macros. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SUPPORT_H +#define _NTFS_SUPPORT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDDEF_H +#include +#endif + +/* + * Our mailing list. Use this define to prevent typos in email address. + */ +#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net" + +/* + * Generic macro to convert pointers to values for comparison purposes. + */ +#ifndef p2n +#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) +#endif + +/* + * The classic min and max macros. + */ +#ifndef min +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif + +/* + * Useful macro for determining the offset of a struct member. + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Simple bit operation macros. NOTE: These are NOT atomic. + */ +#define test_bit(bit, var) ((var) & (1 << (bit))) +#define set_bit(bit, var) (var) |= 1 << (bit) +#define clear_bit(bit, var) (var) &= ~(1 << (bit)) + +#define test_and_set_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + set_bit(bit, var); \ + old_state; \ +}) + +#define test_and_clear_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + clear_bit(bit, var); \ + old_state; \ +}) + +#endif /* defined _NTFS_SUPPORT_H */ + diff --git a/lib/libntfs/src/source/types.h b/lib/libntfs/src/source/types.h new file mode 100644 index 0000000..fcd1360 --- /dev/null +++ b/lib/libntfs/src/source/types.h @@ -0,0 +1,141 @@ +/* + * types.h - Misc type definitions not related to on-disk structure. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_TYPES_H +#define _NTFS_TYPES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STDINT_H || !HAVE_CONFIG_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef GEKKO +#include +#include "compat.h" +#else /* GEKKO */ + +typedef uint8_t u8; /* Unsigned types of an exact size */ +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; /* Signed types of an exact size */ +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#endif /* GEKKO */ + +typedef u16 le16; +typedef u32 le32; +typedef u64 le64; + +/* + * Declare sle{16,32,64} to be unsigned because we do not want sign extension + * on BE architectures. + */ +typedef u16 sle16; +typedef u32 sle32; +typedef u64 sle64; + +typedef u16 ntfschar; /* 2-byte Unicode character type. */ +#define UCHAR_T_SIZE_BITS 1 + +/* + * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN + * and VCN, to allow for type checking and better code readability. + */ +typedef s64 VCN; +typedef sle64 leVCN; +typedef s64 LCN; +typedef sle64 leLCN; + +/* + * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit + * values. We define our own type LSN, to allow for type checking and better + * code readability. + */ +typedef s64 LSN; +typedef sle64 leLSN; + +/* + * Cygwin has a collision between our BOOL and 's + * As long as this file will be included after were fine. + */ +#ifndef GEKKO +#ifndef _WINDEF_H +/** + * enum BOOL - These are just to make the code more readable... + */ +typedef enum { +#ifndef FALSE + FALSE = 0, +#endif +#ifndef NO + NO = 0, +#endif +#ifndef ZERO + ZERO = 0, +#endif +#ifndef TRUE + TRUE = 1, +#endif +#ifndef YES + YES = 1, +#endif +#ifndef ONE + ONE = 1, +#endif +} BOOL; +#endif /* defined _WINDEF_H */ +#endif /* defined GECKO */ + +/** + * enum IGNORE_CASE_BOOL - + */ +typedef enum { + CASE_SENSITIVE = 0, + IGNORE_CASE = 1, +} IGNORE_CASE_BOOL; + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) +#define STATUS_KEEP_SEARCHING (-3) +#define STATUS_NOT_FOUND (-4) + +/* + * Force alignment in a struct if required by processor + */ +union ALIGNMENT { + u64 u64align; + void *ptralign; +} ; + +#endif /* defined _NTFS_TYPES_H */ + diff --git a/lib/libntfs/src/source/unistr.c b/lib/libntfs/src/source/unistr.c new file mode 100644 index 0000000..c0ac342 --- /dev/null +++ b/lib/libntfs/src/source/unistr.c @@ -0,0 +1,1523 @@ +/** + * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2011 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_WCHAR_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +#include +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#include "compat.h" +#include "attrib.h" +#include "types.h" +#include "unistr.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ + +/* + * IMPORTANT + * ========= + * + * All these routines assume that the Unicode characters are in little endian + * encoding inside the strings!!! + */ + +static int use_utf8 = 1; /* use UTF-8 encoding for file names */ + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +/** + * This variable controls whether or not automatic normalization form conversion + * should be performed when translating NTFS unicode file names to UTF-8. + * Defaults to on, but can be controlled from the outside using the function + * int ntfs_macosx_normalize_filenames(int normalize); + */ +static int nfconvert_utf8 = 1; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +/* + * This is used by the name collation functions to quickly determine what + * characters are (in)valid. + */ +#if 0 +static const u8 legal_ansi_char_array[0x40] = { + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, + + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, +}; +#endif + +/** + * ntfs_names_are_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) + * + * Compare the names @s1 and @s2 and return TRUE (1) if the names are + * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, + * the @upcase table is used to perform a case insensitive comparison. + */ +BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size) +{ + if (s1_len != s2_len) + return FALSE; + if (!s1_len) + return TRUE; + if (ic == CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: + TRUE; +} + +/* + * ntfs_names_full_collate() fully collate two Unicode names + * + * @name1: first Unicode name to compare + * @name1_len: length of first Unicode name to compare + * @name2: second Unicode name to compare + * @name2_len: length of second Unicode name to compare + * @ic: either CASE_SENSITIVE or IGNORE_CASE + * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) + * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) + * + * -1 if the first name collates before the second one, + * 0 if the names match, + * 1 if the second name collates before the first one, or + * + */ +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 cnt; + u16 c1, c2; + u16 u1, u2; + +#ifdef DEBUG + if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); + exit(1); + } +#endif + cnt = min(name1_len, name2_len); + if (cnt > 0) { + if (ic == CASE_SENSITIVE) { + while (--cnt && (*name1 == *name2)) { + name1++; + name2++; + } + u1 = c1 = le16_to_cpu(*name1); + u2 = c2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + name1++; + u1 = le16_to_cpu(*name1); + name2++; + u2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { + do { + u1 = c1 = le16_to_cpu(*name1); + name1++; + u2 = c2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + return 0; +} + +/** + * ntfs_ucsncmp - compare two little endian Unicode strings + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * The strings in little endian format and appropriate le16_to_cpu() + * conversion is performed on non-little endian machines. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) +{ + ntfschar c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2) { + ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + c2 = le16_to_cpu(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * @upcase: upcase table + * @upcase_size: upcase table size in Unicode characters + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * ignoring case. The strings in little endian format and appropriate + * le16_to_cpu() conversion is performed on non-little endian machines. + * + * Each character is uppercased using the @upcase table before the comparison. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size) +{ + u16 c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2 || !upcase) { + ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + if ((c1 = le16_to_cpu(s1[i])) < upcase_size) + c1 = le16_to_cpu(upcase[c1]); + if ((c2 = le16_to_cpu(s2[i])) < upcase_size) + c2 = le16_to_cpu(upcase[c2]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsnlen - determine the length of a little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return the number of Unicode characters in the little endian Unicode + * string @s up to a maximum of maxlen Unicode characters, not including + * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s + * and @s + @maxlen, @maxlen is returned. + * + * This function never looks beyond @s + @maxlen. + */ +u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) +{ + u32 i; + + for (i = 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; +} + +/** + * ntfs_ucsndup - duplicate little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return a pointer to a new little endian Unicode string which is a duplicate + * of the string s. Memory for the new string is obtained with ntfs_malloc(3), + * and can be freed with free(3). + * + * A maximum of @maxlen Unicode characters are copied and a terminating + * (ntfschar)'\0' little endian Unicode character is added. + * + * This function never looks beyond @s + @maxlen. + * + * Return a pointer to the new little endian Unicode string on success and NULL + * on failure with errno set to the error code. + */ +ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) +{ + ntfschar *dst; + u32 len; + + len = ntfs_ucsnlen(s, maxlen); + dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); + if (dst) { + memcpy(dst, s, len * sizeof(ntfschar)); + dst[len] = cpu_to_le16(L'\0'); + } + return dst; +} + +/** + * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent + * @name: + * @name_len: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 i; + u16 u; + + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < upcase_len) + name[i] = upcase[u]; +} + +/** + * ntfs_name_locase - Map a Unicode name to its lowercase equivalent + */ +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) +{ + u32 i; + u16 u; + + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; +} + +/** + * ntfs_file_value_upcase - Convert a filename to upper case + * @file_name_attr: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len) +{ + ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, + file_name_attr->file_name_length, upcase, upcase_len); +} + +/* + NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough + for now]) for path names, but the Unicode code points need to be + converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI, + glibc does this even without a locale in a hard-coded fashion as that + appears to be is easy because the low 7-bit ASCII range appears to be + available in all charsets but it does not convert anything if + there was some error with the locale setup or none set up like + when mount is called during early boot where he (by policy) do + not use locales (and may be not available if /usr is not yet mounted), + so this patch fixes the resulting issues for systems which use + UTF-8 and for others, specifying the locale in fstab brings them + the encoding which they want. + + If no locale is defined or there was a problem with setting one + up and whenever nl_langinfo(CODESET) returns a sting starting with + "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix + the bug where NTFS-3G does not show any path names which include + international characters!!! (and also fails on creating them) as result. + + Author: Bernhard Kaindl + Jean-Pierre Andre made it compliant with RFC3629/RFC2781. +*/ + +/* + * Return the amount of 8-bit elements in UTF-8 needed (without the terminating + * null) to store a given UTF-16LE string. + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) +{ + int i, ret = -1; + int count = 0; + BOOL surrog; + + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; +#if NOREVBOM + else if ((c >= 0xe000) && (c < 0xfffe)) +#else + else if (c >= 0xe000) +#endif + count += 3; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } + } + if (surrog) + goto fail; + + ret = count; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *original_outs_value = *outs; + int original_outs_len = outs_len; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + char *t; + int i, size, ret = -1; + int halfpair; + + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; + + size = utf16_to_utf8_size(ins, ins_len, outs_len); + + if (size < 0) + goto out; + + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } + + t = *outs; + + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else + goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else + goto fail; + } + } + *t = '\0'; + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(nfconvert_utf8 && (t - *outs) > 0) { + char *new_outs = NULL; + int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form + if(new_outs_len >= 0 && new_outs != NULL) { + if(original_outs_value != *outs) { + // We have allocated outs ourselves. + free(*outs); + *outs = new_outs; + t = *outs + new_outs_len; + } + else { + // We need to copy new_outs into the fixed outs buffer. + memset(*outs, 0, original_outs_len); + strncpy(*outs, new_outs, original_outs_len-1); + t = *outs + original_outs_len; + free(new_outs); + } + } + else { + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs); + ntfs_log_error(" new_outs=0x%p\n", new_outs); + ntfs_log_error(" new_outs_len=%d\n", new_outs_len); + } + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + ret = t - *outs; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * Return the amount of 16-bit elements in UTF-16LE needed + * (without the terminating null) to store given UTF-8 string. + * + * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. + * + * Note: This does not check whether the input sequence is a valid utf8 string, + * and should be used only in context where such check is made! + */ +static int utf8_to_utf16_size(const char *s) +{ + int ret = -1; + unsigned int byte; + size_t count = 0; + + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX) + goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + } + ret = count; +out: + return ret; +fail: + errno = ENAMETOOLONG; + goto out; +} +/* + * This converts one UTF-8 sequence to cpu-endian Unicode value + * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF + * + * Return the number of used utf8 bytes or -1 with errno set + * if sequence is invalid. + */ +static int utf8_to_unicode(u32 *wc, const char *s) +{ + unsigned int byte = *((const unsigned char *)s); + + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ +#if NOREVBOM + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; +#else + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; +#endif + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } +fail: + errno = EILSEQ; + return -1; +} + +/** + * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + * + * Return -1 with errno set. + */ +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *new_ins = NULL; + if(nfconvert_utf8) { + int new_ins_len; + new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form + if(new_ins_len >= 0) + ins = new_ins; + else + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins); + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + const char *t = ins; + u32 wc; + BOOL allocated; + ntfschar *outpos; + int shorts, ret = -1; + + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) + goto fail; + + allocated = FALSE; + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + allocated = TRUE; + } + + outpos = *outs; + + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + t += m; + } + + ret = --outpos - *outs; +fail: +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(new_ins != NULL) + free(new_ins); +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + return ret; +} + +/** + * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string + * @ins: input Unicode string buffer + * @ins_len: length of input string in Unicode characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Convert the input little endian, 2-byte Unicode string @ins, of length + * @ins_len into the multibyte string format dictated by the current locale. + * + * If *@outs is NULL, the function allocates the string and the caller is + * responsible for calling free(*@outs); when finished with it. + * + * On success the function returns the number of bytes written to the output + * string *@outs (>= 0), not counting the terminating NULL byte. If the output + * string buffer was allocated, *@outs is set to it. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a multibyte + * sequence according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len) +{ + char *mbs; + int mbs_len; +#ifdef MB_CUR_MAX + wchar_t wc; + int i, o; + int cnt = 0; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + mbs = *outs; + mbs_len = outs_len; + if (mbs && !mbs_len) { + errno = ENAMETOOLONG; + return -1; + } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); +#ifdef MB_CUR_MAX + if (!mbs) { + mbs_len = (ins_len + 1) * MB_CUR_MAX; + mbs = ntfs_malloc(mbs_len); + if (!mbs) + return -1; + } +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + wctomb(NULL, 0); +#endif + for (i = o = 0; i < ins_len; i++) { + /* Reallocate memory if necessary or abort. */ + if ((int)(o + MB_CUR_MAX) > mbs_len) { + char *tc; + if (mbs == *outs) { + errno = ENAMETOOLONG; + return -1; + } + tc = ntfs_malloc((mbs_len + 64) & ~63); + if (!tc) + goto err_out; + memcpy(tc, mbs, mbs_len); + mbs_len = (mbs_len + 64) & ~63; + free(mbs); + mbs = tc; + } + /* Convert the LE Unicode character to a CPU wide character. */ + wc = (wchar_t)le16_to_cpu(ins[i]); + if (!wc) + break; + /* Convert the CPU endian wide character to multibyte. */ +#ifdef HAVE_MBSINIT + cnt = wcrtomb(mbs + o, wc, &mbstate); +#else + cnt = wctomb(mbs + o, wc); +#endif + if (cnt == -1) + goto err_out; + if (cnt <= 0) { + ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + o += cnt; + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_debug("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + mbs[o] = '\0'; + if (*outs != mbs) + *outs = mbs; + return o; +err_out: + if (mbs != *outs) { + int eo = errno; + free(mbs); + errno = eo; + } +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/** + * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output Unicode string + * + * Convert the input multibyte string @ins, from the current locale into the + * corresponding little endian, 2-byte Unicode string. + * + * The function allocates the string and the caller is responsible for calling + * free(*@outs); when finished with it. + * + * On success the function returns the number of Unicode characters written to + * the output string *@outs (>= 0), not counting the terminating Unicode NULL + * character. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a Unicode + * string according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_mbstoucs(const char *ins, ntfschar **outs) +{ +#ifdef MB_CUR_MAX + ntfschar *ucs; + const char *s; + wchar_t wc; + int i, o, cnt, ins_len, ucs_len, ins_size; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); + +#ifdef MB_CUR_MAX + /* Determine the size of the multi-byte string in bytes. */ + ins_size = strlen(ins); + /* Determine the length of the multi-byte string. */ + s = ins; +#if defined(HAVE_MBSINIT) + memset(&mbstate, 0, sizeof(mbstate)); + ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); +#ifdef __CYGWIN32__ + if (!ins_len && *ins) { + /* Older Cygwin had broken mbsrtowcs() implementation. */ + ins_len = strlen(ins); + } +#endif +#elif !defined(DJGPP) + ins_len = mbstowcs(NULL, s, 0); +#else + /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ + ins_len = strlen(ins); +#endif + if (ins_len == -1) + return ins_len; +#ifdef HAVE_MBSINIT + if ((s != ins) || !mbsinit(&mbstate)) { +#else + if (s != ins) { +#endif + errno = EILSEQ; + return -1; + } + /* Add the NULL terminator. */ + ins_len++; + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + mbtowc(NULL, NULL, 0); +#endif + for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { + /* Reallocate memory if necessary. */ + if (o >= ucs_len) { + ntfschar *tc; + ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; + tc = realloc(ucs, ucs_len); + if (!tc) + goto err_out; + ucs = tc; + ucs_len /= sizeof(ntfschar); + } + /* Convert the multibyte character to a wide character. */ +#ifdef HAVE_MBSINIT + cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); +#else + cnt = mbtowc(&wc, ins + i, ins_size - i); +#endif + if (!cnt) + break; + if (cnt == -1) + goto err_out; + if (cnt < -1) { + ntfs_log_trace("Eeek. cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + /* Make sure we are not overflowing the NTFS Unicode set. */ + if ((unsigned long)wc >= (unsigned long)(1 << + (8 * sizeof(ntfschar)))) { + errno = EILSEQ; + goto err_out; + } + /* Convert the CPU wide character to a LE Unicode character. */ + ucs[o] = cpu_to_le16(wc); + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_trace("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + ucs[o] = cpu_to_le16(L'\0'); + *outs = ucs; + return o; +err_out: + free(ucs); +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/* + * Turn a UTF8 name uppercase + * + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) + */ + +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) +{ + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; + + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); +} + +/** + * ntfs_upcase_table_build - build the default upcase table for NTFS + * @uc: destination buffer where to store the built table + * @uc_len: size of destination buffer in bytes + * + * ntfs_upcase_table_build() builds the default upcase table for NTFS and + * stores it in the caller supplied buffer @uc of size @uc_len. + * + * Note, @uc_len must be at least 128kiB in size or bad things will happen! + */ +void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) +{ +#if 1 /* Vista */ + /* + * This is the table as defined by Vista + */ + /* + * "Start" is inclusive and "End" is exclusive, every value has the + * value of "Add" added to it. + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, + {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, + {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, + {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, + {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, + {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, + {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, + {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, + {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, + {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, + {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, + {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, + {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, + {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, + {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, + {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} + }; + /* + * "Start" is exclusive and "End" is inclusive, every second value is + * decremented by one. + */ + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, + {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, + {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, + {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, + {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, + {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, + {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, + {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, + {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} + }; + /* + * Set the Unicode character at offset "Offset" to "Value". Note, + * "Value" is host endian. + */ + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, + {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, + {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, + {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, + {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, + {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, + {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, + {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, + {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, + {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, + {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, + {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} + }; +#else /* Vista */ + /* + * This is the table as defined by Windows XP + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; +#endif /* Vista */ + int i, r; + int k, off; + + memset((char*)uc, 0, uc_len); + uc_len >>= 1; + if (uc_len > 65536) + uc_len = 65536; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) { + off = uc_run_table[r][2]; + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(i + off); + } + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(i); + for (r = 0; uc_byte_table[r][0]; r++) { + k = uc_byte_table[r][1]; + uc[uc_byte_table[r][0]] = cpu_to_le16(k); + } +} + +/* + * Allocate and build the default upcase table + * + * Returns the number of entries + * 0 if failed + */ + +#define UPCASE_LEN 65536 /* default number of entries in upcase */ + +u32 ntfs_upcase_build_default(ntfschar **upcase) +{ + u32 upcase_len = 0; + + *upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2); + if (*upcase) { + ntfs_upcase_table_build(*upcase, UPCASE_LEN*2); + upcase_len = UPCASE_LEN; + } + return (upcase_len); +} + +/* + * Build a table for converting to lower case + * + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. + */ + +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) +{ + ntfschar *lc; + u32 upp; + u32 i; + + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i NTFS_MAX_NAME_LEN) { + free(ucs); + errno = ENAMETOOLONG; + return NULL; + } + if (!ucs || !*len) { + ucs = AT_UNNAMED; + *len = 0; + } + return ucs; +} + +/** + * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() + * @ucs input string to be freed + * + * Free memory at @ucs and which was allocated by ntfs_str2ucs. + * + * Return value: none. + */ +void ntfs_ucsfree(ntfschar *ucs) +{ + if (ucs && (ucs != AT_UNNAMED)) + free(ucs); +} + +/* + * Check whether a name contains no chars forbidden + * for DOS or Win32 use + * + * If there is a bad char, errno is set to EINVAL + */ + +BOOL ntfs_forbidden_chars(const ntfschar *name, int len) +{ + BOOL forbidden; + int ch; + int i; + u32 mainset = (1L << ('\"' - 0x20)) + | (1L << ('*' - 0x20)) + | (1L << ('/' - 0x20)) + | (1L << (':' - 0x20)) + | (1L << ('<' - 0x20)) + | (1L << ('>' - 0x20)) + | (1L << ('?' - 0x20)); + + forbidden = (len == 0) + || (le16_to_cpu(name[len-1]) == ' ') + || (le16_to_cpu(name[len-1]) == '.'); + for (i=0; i= vol->upcase_len) + || (cs >= vol->upcase_len) + || (vol->upcase[cs] != vol->upcase[ch]))) + collapsible = FALSE; + } + return (collapsible); +} + +/* + * Define the character encoding to be used. + * Use UTF-8 unless specified otherwise. + */ + +int ntfs_set_char_encoding(const char *locale) +{ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ +} + +#if defined(__APPLE__) || defined(__DARWIN__) + +int ntfs_macosx_normalize_filenames(int normalize) { +#ifdef ENABLE_NFCONV + if(normalize == 0 || normalize == 1) { + nfconvert_utf8 = normalize; + return 0; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} + +int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, + int composed) { +#ifdef ENABLE_NFCONV + /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + CFStringRef cfSourceString; + CFMutableStringRef cfMutableString; + CFRange rangeToProcess; + CFIndex requiredBufferLength; + char *result = NULL; + int resultLength = -1; + + /* Convert the UTF-8 string to a CFString. */ + cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8); + if(cfSourceString == NULL) { + ntfs_log_error("CFStringCreateWithCString failed!\n"); + return -2; + } + + /* Create a mutable string from cfSourceString that we are free to modify. */ + cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString); + CFRelease(cfSourceString); /* End-of-life. */ + if(cfMutableString == NULL) { + ntfs_log_error("CFStringCreateMutableCopy failed!\n"); + return -3; + } + + /* Normalize the mutable string to the desired normalization form. */ + CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD)); + + /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ + rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString)); + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) { + resultLength = sizeof(char)*(requiredBufferLength + 1); + result = ntfs_calloc(resultLength); + + if(result != NULL) { + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, + 0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) { + ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n"); + free(result); + result = NULL; + } + } + else + ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength); + } + else + ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); + + + CFRelease(cfMutableString); + + if(result != NULL) { + *target = result; + return resultLength - 1; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ diff --git a/lib/libntfs/src/source/unistr.h b/lib/libntfs/src/source/unistr.h new file mode 100644 index 0000000..8cadc3c --- /dev/null +++ b/lib/libntfs/src/source/unistr.h @@ -0,0 +1,119 @@ +/* + * unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_UNISTR_H +#define _NTFS_UNISTR_H + +#include "types.h" +#include "layout.h" + +extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size); + +extern int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); + +extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size); + +extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); + +extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); + +extern void ntfs_name_upcase(ntfschar *name, u32 name_len, + const ntfschar *upcase, const u32 upcase_len); + +extern void ntfs_name_locase(ntfschar *name, u32 name_len, + const ntfschar *locase, const u32 locase_len); + +extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); + +extern char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_len); + +extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); +extern u32 ntfs_upcase_build_default(ntfschar **upcase); +extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt); + +extern ntfschar *ntfs_str2ucs(const char *s, int *len); + +extern void ntfs_ucsfree(ntfschar *ucs); + +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen); + +extern int ntfs_set_char_encoding(const char *locale); + +#if defined(__APPLE__) || defined(__DARWIN__) +/** + * Mac OS X only. + * + * Sets file name Unicode normalization form conversion on or off. + * normalize=0 : Off + * normalize=1 : On + * If set to on, all filenames returned by ntfs-3g will be converted to the NFD + * normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC + * normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X + * mostly uses NFD, this conversion increases compatibility between Mac applications and + * NTFS-3G. + * + * @param normalize decides whether or not the string functions will do automatic filename + * normalization when converting to and from UTF-8. 0 means normalization is disabled, + * 1 means it is enabled. + * @return -1 if the argument was invalid or an error occurred, 0 if all went well. + */ +extern int ntfs_macosx_normalize_filenames(int normalize); + +/** + * Mac OS X only. + * + * Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC. + * The parameter "composed" decides whether output should be in composed, NFC, form + * (composed == 1) or decomposed, NFD, form (composed == 0). + * Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly + * ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the + * allocated string when it's no longer needed. + * + * @param utf8_string the input string, which may be in any normalization form. + * @param target a pointer where the resulting string will be stored. + * @param composed decides which composition form to normalize the input string to. 0 means + * composed form (NFC), 1 means decomposed form (NFD). + * @return -1 if the normalization failed for some reason, otherwise the length of the + * normalized string stored in target. + */ +extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed); +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#endif /* defined _NTFS_UNISTR_H */ + diff --git a/lib/libntfs/src/source/volume.c b/lib/libntfs/src/source/volume.c new file mode 100644 index 0000000..a18109f --- /dev/null +++ b/lib/libntfs/src/source/volume.c @@ -0,0 +1,1844 @@ +/** + * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "param.h" +#include "compat.h" +#include "volume.h" +#include "attrib.h" +#include "mft.h" +#include "bootsect.h" +#include "device.h" +#include "debug.h" +#include "inode.h" +#include "runlist.h" +#include "logfile.h" +#include "dir.h" +#include "logging.h" +#include "cache.h" +#include "realpath.h" +#include "misc.h" + +const char *ntfs_home = +"News, support and information: http://tuxera.com\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume and shutdown Windows\n" +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g -o remove_hiberfile %s %s\n" +"\n"; + +static const char *unclean_journal_msg = +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; + +static const char *opened_volume_msg = +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *fakeraid_msg = +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; + +static const char *access_denied_msg = +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; + +/** + * ntfs_volume_alloc - Create an NTFS volume object and initialise it + * + * Description... + * + * Returns: + */ +ntfs_volume *ntfs_volume_alloc(void) +{ + return ntfs_calloc(sizeof(ntfs_volume)); +} + +static void ntfs_attr_free(ntfs_attr **na) +{ + if (na && *na) { + ntfs_attr_close(*na); + *na = NULL; + } +} + +static int ntfs_inode_free(ntfs_inode **ni) +{ + int ret = -1; + + if (ni && *ni) { + ret = ntfs_inode_close(*ni); + *ni = NULL; + } + + return ret; +} + +static void ntfs_error_set(int *err) +{ + if (!*err) + *err = errno; +} + +/** + * __ntfs_volume_release - Destroy an NTFS volume object + * @v: + * + * Description... + * + * Returns: + */ +static int __ntfs_volume_release(ntfs_volume *v) +{ + int err = 0; + + if (ntfs_inode_free(&v->vol_ni)) + ntfs_error_set(&err); + /* + * FIXME: Inodes must be synced before closing + * attributes, otherwise unmount could fail. + */ + if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) + ntfs_inode_sync(v->lcnbmp_ni); + ntfs_attr_free(&v->lcnbmp_na); + if (ntfs_inode_free(&v->lcnbmp_ni)) + ntfs_error_set(&err); + + if (v->mft_ni && NInoDirty(v->mft_ni)) + ntfs_inode_sync(v->mft_ni); + ntfs_attr_free(&v->mftbmp_na); + ntfs_attr_free(&v->mft_na); + if (ntfs_inode_free(&v->mft_ni)) + ntfs_error_set(&err); + + if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) + ntfs_inode_sync(v->mftmirr_ni); + ntfs_attr_free(&v->mftmirr_na); + if (ntfs_inode_free(&v->mftmirr_ni)) + ntfs_error_set(&err); + + if (v->dev) { + struct ntfs_device *dev = v->dev; + + if (dev->d_ops->sync(dev)) + ntfs_error_set(&err); + if (dev->d_ops->close(dev)) + ntfs_error_set(&err); + } + + ntfs_free_lru_caches(v); + free(v->vol_name); + free(v->upcase); + if (v->locase) free(v->locase); + free(v->attrdef); + free(v); + + errno = err; + return errno ? -1 : 0; +} + +static void ntfs_attr_setup_flag(ntfs_inode *ni) +{ + STANDARD_INFORMATION *si; + + si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL); + if (si) { + ni->flags = si->file_attributes; + free(si); + } +} + +/** + * ntfs_mft_load - load the $MFT and setup the ntfs volume with it + * @vol: ntfs volume whose $MFT to load + * + * Load $MFT from @vol and setup @vol with it. After calling this function the + * volume @vol is ready for use by all read access functions provided by the + * ntfs library. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_load(ntfs_volume *vol) +{ + VCN next_vcn, last_vcn, highest_vcn; + s64 l; + MFT_RECORD *mb = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ATTR_RECORD *a; + int eo; + + /* Manually setup an ntfs_inode. */ + vol->mft_ni = ntfs_inode_allocate(vol); + mb = ntfs_malloc(vol->mft_record_size); + if (!vol->mft_ni || !mb) { + ntfs_log_perror("Error allocating memory for $MFT"); + goto error_exit; + } + vol->mft_ni->mft_no = 0; + vol->mft_ni->mrec = mb; + /* Can't use any of the higher level functions yet! */ + l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, + vol->mft_record_size, mb); + if (l != 1) { + if (l != -1) + errno = EIO; + ntfs_log_perror("Error reading $MFT"); + goto error_exit; + } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + goto mft_has_no_attr_list; + } + NInoSetAttrList(vol->mft_ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (l <= 0 || l > 0x40000) { + ntfs_log_error("$MFT/$ATTR_LIST invalid length (%lld).\n", + (long long)l); + goto io_error_exit; + } + vol->mft_ni->attr_list_size = l; + vol->mft_ni->attr_list = ntfs_malloc(l); + if (!vol->mft_ni->attr_list) + goto error_exit; + + l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); + if (!l) { + ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n"); + goto io_error_exit; + } + if (l != vol->mft_ni->attr_list_size) { + ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != " + "%u).\n", (long long)l, + vol->mft_ni->attr_list_size); + goto io_error_exit; + } + +mft_has_no_attr_list: + + ntfs_attr_setup_flag(vol->mft_ni); + + /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + + /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ + vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mft_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Read all extents from the $DATA attribute in $MFT. */ + ntfs_attr_reinit_search_ctx(ctx); + last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; + highest_vcn = next_vcn = 0; + a = NULL; + while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, + ctx)) { + runlist_element *nrl; + + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + ntfs_log_error("$MFT must be non-resident.\n"); + goto io_error_exit; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED) { + ntfs_log_error("$MFT must be uncompressed and " + "unencrypted.\n"); + goto io_error_exit; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing runlist. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); + if (!nrl) { + ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); + goto error_exit; + } + vol->mft_na->rl = nrl; + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + } + if (!a) { + ntfs_log_error("$MFT/$DATA attribute not found.\n"); + goto io_error_exit; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + ntfs_log_error("Failed to load runlist for $MFT/$DATA.\n"); + ntfs_log_error("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", + (long long)highest_vcn, (long long)last_vcn - 1); + goto io_error_exit; + } + /* Done with the $Mft mft record. */ + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Update the size fields in the inode. */ + vol->mft_ni->data_size = vol->mft_na->data_size; + vol->mft_ni->allocated_size = vol->mft_na->allocated_size; + set_nino_flag(vol->mft_ni, KnownSize); + + /* + * The volume is now setup so we can use all read access functions. + */ + vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!vol->mftbmp_na) { + ntfs_log_perror("Failed to open $MFT/$BITMAP"); + goto error_exit; + } + return 0; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (vol->mft_na) { + ntfs_attr_close(vol->mft_na); + vol->mft_na = NULL; + } + if (vol->mft_ni) { + ntfs_inode_close(vol->mft_ni); + vol->mft_ni = NULL; + } + errno = eo; + return -1; +} + +/** + * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it + * @vol: ntfs volume whose $MFTMirr to load + * + * Load $MFTMirr from @vol and setup @vol with it. After calling this function + * the volume @vol is ready for use by all write access functions provided by + * the ntfs library (assuming ntfs_mft_load() has been called successfully + * beforehand). + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mftmirr_load(ntfs_volume *vol) +{ + int err; + + vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); + if (!vol->mftmirr_ni) { + ntfs_log_perror("Failed to open inode $MFTMirr"); + return -1; + } + + vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mftmirr_na) { + ntfs_log_perror("Failed to open $MFTMirr/$DATA"); + goto error_exit; + } + + if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { + ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); + goto error_exit; + } + + return 0; + +error_exit: + err = errno; + if (vol->mftmirr_na) { + ntfs_attr_close(vol->mftmirr_na); + vol->mftmirr_na = NULL; + } + ntfs_inode_close(vol->mftmirr_ni); + vol->mftmirr_ni = NULL; + errno = err; + return -1; +} + +/** + * ntfs_volume_startup - allocate and setup an ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After + * calling this function, the volume is setup sufficiently to call all read + * and write access functions provided by the library. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) +{ + LCN mft_zone_size, mft_lcn; + s64 br; + ntfs_volume *vol; + NTFS_BOOT_SECTOR *bs; + int eo; + + if (!dev || !dev->d_ops || !dev->d_name) { + errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); + return NULL; + } + + bs = ntfs_malloc(sizeof(NTFS_BOOT_SECTOR)); + if (!bs) + return NULL; + + /* Allocate the volume structure. */ + vol = ntfs_volume_alloc(); + if (!vol) + goto error_exit; + + /* Create the default upcase table. */ + vol->upcase_len = ntfs_upcase_build_default(&vol->upcase); + if (!vol->upcase_len || !vol->upcase) + goto error_exit; + + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); + /* set default compression */ +#if DEFAULT_COMPRESSION + NVolSetCompression(vol); +#else + NVolClearCompression(vol); +#endif + if (flags & MS_RDONLY) + NVolSetReadOnly(vol); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } + /* Attach the device to the volume. */ + vol->dev = dev; + + /* Now read the bootsector. */ + br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); + if (br != sizeof(NTFS_BOOT_SECTOR)) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading bootsector"); + goto error_exit; + } + if (!ntfs_boot_sector_is_ntfs(bs)) { + errno = EINVAL; + goto error_exit; + } + if (ntfs_boot_sector_parse(vol, bs) < 0) + goto error_exit; + + free(bs); + bs = NULL; + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, vol->sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may affect performance " + "but should be harmless otherwise. Error: " + "%s\n", strerror(errno)); + + /* We now initialize the cluster allocator. */ + vol->full_zones = 0; + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ + + /* Setup the mft zone. */ + vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; + ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos); + + /* + * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs + * source) and if the actual mft_lcn is in the expected place or even + * further to the front of the volume, extend the mft_zone to cover the + * beginning of the volume as well. This is in order to protect the + * area reserved for the mft bitmap as well within the mft_zone itself. + * On non-standard volumes we don't protect it as the overhead would be + * higher than the speed increase we would get by doing it. + */ + mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; + if (mft_lcn * vol->cluster_size < 16 * 1024) + mft_lcn = (16 * 1024 + vol->cluster_size - 1) / + vol->cluster_size; + if (vol->mft_zone_start <= mft_lcn) + vol->mft_zone_start = 0; + ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)vol->mft_zone_start); + + /* + * Need to cap the mft zone on non-standard volumes so that it does + * not point outside the boundaries of the volume. We do this by + * halving the zone size until we are inside the volume. + */ + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + while (vol->mft_zone_end >= vol->nr_clusters) { + mft_zone_size >>= 1; + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + } + ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); + + /* + * Set the current position within each data zone to the start of the + * respective zone. + */ + vol->data1_zone_pos = vol->mft_zone_end; + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); + vol->data2_zone_pos = 0; + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); + + /* Set the mft data allocation position to mft record 24. */ + vol->mft_data_pos = 24; + + /* + * The cluster allocator is now fully operational. + */ + + /* Need to setup $MFT so we can use the library read functions. */ + if (ntfs_mft_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFT"); + goto error_exit; + } + + /* Need to setup $MFTMirr so we can use the write functions, too. */ + if (ntfs_mftmirr_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFTMirr"); + goto error_exit; + } + return vol; +error_exit: + eo = errno; + free(bs); + if (vol) + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/** + * ntfs_volume_check_logfile - check logfile on target volume + * @vol: volume on which to check logfile + * + * Return 0 on success and -1 on error with errno set error code. + */ +static int ntfs_volume_check_logfile(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + RESTART_PAGE_HEADER *rp = NULL; + int err = 0; + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + errno = EIO; + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + err = EIO; + goto out; + } + + if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) + err = EOPNOTSUPP; + free(rp); + ntfs_attr_close(na); +out: + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + if (err) { + errno = err; + return -1; + } + return 0; +} + +/** + * ntfs_hiberfile_open - Find and open '/hiberfil.sys' + * @vol: An ntfs volume obtained from ntfs_mount + * + * Return: inode Success, hiberfil.sys is valid + * NULL hiberfil.sys doesn't exist or some other error occurred + */ +static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) +{ + u64 inode; + ntfs_inode *ni_root; + ntfs_inode *ni_hibr = NULL; + ntfschar *unicode = NULL; + int unicode_len; + const char *hiberfile = "hiberfil.sys"; + + if (!vol) { + errno = EINVAL; + return NULL; + } + + ni_root = ntfs_inode_open(vol, FILE_root); + if (!ni_root) { + ntfs_log_debug("Couldn't open the root directory.\n"); + return NULL; + } + + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); + if (unicode_len < 0) { + ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); + goto out; + } + + inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); + if (inode == (u64)-1) { + ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); + goto out; + } + + inode = MREF(inode); + ni_hibr = ntfs_inode_open(vol, inode); + if (!ni_hibr) { + ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); + goto out; + } +out: + if (ntfs_inode_close(ni_root)) { + ntfs_inode_close(ni_hibr); + ni_hibr = NULL; + } + free(unicode); + return ni_hibr; +} + + +#define NTFS_HIBERFILE_HEADER_SIZE 4096 + +/** + * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is + * hibernated on the target volume + * @vol: volume on which to check hiberfil.sys + * + * Return: 0 if Windows isn't hibernated for sure + * -1 otherwise and errno is set to the appropriate value + */ +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + int bytes_read, err; + char *buf = NULL; + + ni = ntfs_hiberfile_open(vol); + if (!ni) { + if (errno == ENOENT) + return 0; + return -1; + } + + buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); + if (!buf) + goto out; + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open hiberfil.sys data attribute"); + goto out; + } + + bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); + if (bytes_read == -1) { + ntfs_log_perror("Failed to read hiberfil.sys"); + goto out; + } + if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); + errno = EPERM; + goto out; + } + if (memcmp(buf, "hibr", 4) == 0) { + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); + errno = EPERM; + goto out; + } + /* All right, all header bytes are zero */ + errno = 0; +out: + if (na) + ntfs_attr_close(na); + free(buf); + err = errno; + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + errno = err; + return errno ? -1 : 0; +} + +/* + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) + * + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. + * + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno + */ + +static int fix_txf_data(ntfs_volume *vol) +{ + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; + + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); +} + +/** + * ntfs_device_mount - open ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @dev should describe the device which + * to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: + * MS_RDONLY - mount volume read-only + * + * The function opens the device @dev and verifies that it contains a valid + * bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) +{ + s64 l; + ntfs_volume *vol; + u8 *m = NULL, *m2 = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_inode *ni; + ntfs_attr *na; + ATTR_RECORD *a; + VOLUME_INFORMATION *vinf; + ntfschar *vname; + int i, j, eo; + unsigned int k; + u32 u; + + vol = ntfs_volume_startup(dev, flags); + if (!vol) + return NULL; + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) + goto error_exit; + + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + if (l == -1) + ntfs_log_perror("Failed to read $MFT"); + else { + ntfs_log_error("Failed to read $MFT, unexpected length " + "(%lld != %d).\n", (long long)l, + vol->mftmirr_size); + errno = EIO; + } + goto error_exit; + } + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + if (l == -1) { + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + vol->mftmirr_size = l; + } + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec)) { + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "'%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec)) { + ntfs_log_error("$MFT error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in '%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + ntfs_log_error("$MFTMirr does not match $MFT (record " + "%d).\n", i); + goto io_error_exit; + } + } + + free(m2); + free(m); + m = m2 = NULL; + + /* Now load the bitmap from $Bitmap. */ + ntfs_log_debug("Loading $Bitmap...\n"); + vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + ntfs_log_perror("Failed to open inode FILE_Bitmap"); + goto error_exit; + } + + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } + + /* Now load the upcase table from $UpCase. */ + ntfs_log_debug("Loading $UpCase...\n"); + ni = ntfs_inode_open(vol, FILE_UpCase); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_UpCase"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + ntfs_log_error("Error: Upcase table is too big (max 32-bit " + "allowed).\n"); + errno = EINVAL; + goto error_exit; + } + if (vol->upcase_len != na->data_size >> 1) { + vol->upcase_len = na->data_size >> 1; + /* Throw away default table. */ + free(vol->upcase); + vol->upcase = ntfs_malloc(na->data_size); + if (!vol->upcase) + goto error_exit; + } + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); + if (l != na->data_size) { + ntfs_log_error("Failed to read $UpCase, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $UpCase mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + goto error_exit; + } + /* Consistency check of $UpCase, restricted to plain ASCII chars */ + k = 0x20; + while ((k < vol->upcase_len) + && (k < 0x7f) + && (le16_to_cpu(vol->upcase[k]) + == ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a'))) + k++; + if (k < 0x7f) { + ntfs_log_error("Corrupted file $UpCase\n"); + goto io_error_exit; + } + + /* + * Now load $Volume and set the version information and flags in the + * vol structure accordingly. + */ + ntfs_log_debug("Loading $Volume...\n"); + vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); + if (!vol->vol_ni) { + ntfs_log_perror("Failed to open inode FILE_Volume"); + goto error_exit; + } + /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $VOLUME_INFORMATION attribute. */ + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " + "$Volume"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be " + "resident but it isn't.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + le32_to_cpu( + a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); + errno = EIO; + goto error_exit; + } + /* Setup vol from the volume information attribute value. */ + vol->major_ver = vinf->major_ver; + vol->minor_ver = vinf->minor_ver; + /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are + defined using cpu_to_le16() macro and hence are consistent. */ + vol->flags = vinf->flags; + /* + * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " + "$Volume failed"); + goto error_exit; + } + /* + * Attribute not present. This has been seen in the field. + * Treat this the same way as if the attribute was present but + * had zero length. + */ + vol->vol_name = ntfs_malloc(1); + if (!vol->vol_name) + goto error_exit; + vol->vol_name[0] = '\0'; + } else { + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("$VOLUME_NAME must be resident.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* + * Convert Unicode volume name to current locale multibyte + * format. + */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + ntfs_log_perror("Volume name could not be converted " + "to current locale"); + ntfs_log_debug("Forcing name into ASCII by replacing " + "non-ASCII characters with underscores.\n"); + vol->vol_name = ntfs_malloc(u + 1); + if (!vol->vol_name) + goto error_exit; + + for (j = 0; j < (s32)u; j++) { + u16 uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (u16)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* Now load the attribute definitions from $AttrDef. */ + ntfs_log_debug("Loading $AttrDef...\n"); + ni = ntfs_inode_open(vol, FILE_AttrDef); + if (!ni) { + ntfs_log_perror("Failed to open $AttrDef"); + goto error_exit; + } + /* Get an ntfs attribute for $AttrDef/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Check we don't overflow 32-bits. */ + if (na->data_size > 0xffffffffLL) { + ntfs_log_error("Attribute definition table is too big (max " + "32-bit allowed).\n"); + errno = EINVAL; + goto error_exit; + } + vol->attrdef_len = na->data_size; + vol->attrdef = ntfs_malloc(na->data_size); + if (!vol->attrdef) + goto error_exit; + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); + if (l != na->data_size) { + ntfs_log_error("Failed to read $AttrDef, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $AttrDef mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $AttrDef"); + goto error_exit; + } + /* + * Check for dirty logfile and hibernated Windows. + * We care only about read-write mounts. + */ + if (!(flags & (MS_RDONLY | MS_FORENSIC))) { + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) + goto error_exit; + if (ntfs_volume_check_logfile(vol) < 0) { + if (!(flags & MS_RECOVER)) + goto error_exit; + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } + /* make $TXF_DATA resident if present on the root directory */ + if (fix_txf_data(vol)) + goto error_exit; + } + + return vol; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(m); + free(m2); + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/* + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. + */ + +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) +{ + int res; + + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); +} + +/* + * Set ignore case mode + */ + +int ntfs_set_ignore_case(ntfs_volume *vol) +{ + int res; + + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); +} + +/** + * ntfs_mount - open ntfs volume + * @name: name of device/file to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @name should contain the name of the + * device/file to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flags + * is implemented: + * MS_RDONLY - mount volume read-only + * + * The function opens the device or file @name and verifies that it contains a + * valid bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + * + * Note, that a copy is made of @name, and hence it can be discarded as + * soon as the function returns. + */ +ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), + unsigned long flags __attribute__((unused))) +{ +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + struct ntfs_device *dev; + ntfs_volume *vol; + + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return NULL; + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, flags); + if (!vol) { + int eo = errno; + ntfs_device_free(dev); + errno = eo; + } else + ntfs_create_lru_caches(vol); + return vol; +#else + /* + * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is + * defined as there are no device operations available in libntfs in + * this case. + */ + errno = EOPNOTSUPP; + return NULL; +#endif +} + +/** + * ntfs_umount - close ntfs volume + * @vol: address of ntfs_volume structure of volume to close + * @force: if true force close the volume even if it is busy + * + * Deallocate all structures (including @vol itself) associated with the ntfs + * volume @vol. + * + * Return 0 on success. On error return -1 with errno set appropriately + * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that + * an operation is in progress and if you try the close later the operation + * might be completed and the close succeed. + * + * If @force is true (i.e. not zero) this function will close the volume even + * if this means that data might be lost. + * + * @vol must have previously been returned by a call to ntfs_mount(). + * + * @vol itself is deallocated and should no longer be dereferenced after this + * function returns success. If it returns an error then nothing has been done + * so it is safe to continue using @vol. + */ +int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) +{ + struct ntfs_device *dev; + int ret; + + if (!vol) { + errno = EINVAL; + return -1; + } + dev = vol->dev; + ret = __ntfs_volume_release(vol); + ntfs_device_free(dev); + return ret; +} + +#ifdef HAVE_MNTENT_H + +/** + * ntfs_mntent_check - desc + * + * If you are wanting to use this, you actually wanted to use + * ntfs_check_if_mounted(), you just didn't realize. (-: + * + * See description of ntfs_check_if_mounted(), below. + */ +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) +{ + struct mntent *mnt; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; + + real_file = ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = ntfs_malloc(PATH_MAX + 1); + if (!real_fsname) { + err = errno; + goto exit; + } + if (!ntfs_realpath_canonicalize(file, real_file)) { + err = errno; + goto exit; + } + if (!(f = setmntent(MOUNTED, "r"))) { + err = errno; + goto exit; + } + while ((mnt = getmntent(f))) { + if (!ntfs_realpath_canonicalize(mnt->mnt_fsname, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) + break; + } + endmntent(f); + if (!mnt) + goto exit; + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_dir, "/")) + *mnt_flags |= NTFS_MF_ISROOT; +#ifdef HAVE_HASMNTOPT + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; +#endif +exit: + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; +} +#endif /* HAVE_MNTENT_H */ + +/** + * ntfs_check_if_mounted - check if an ntfs volume is currently mounted + * @file: device file to check + * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) + * + * If the running system does not support the {set,get,end}mntent() calls, + * just return 0 and set *@mnt_flags to zero. + * + * When the system does support the calls, ntfs_check_if_mounted() first tries + * to find the device @file in /etc/mtab (or wherever this is kept on the + * running system). If it is not found, assume the device is not mounted and + * return 0 and set *@mnt_flags to zero. + * + * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. + * + * Further if @file is mounted as the file system root ("/"), set the flag + * NTFS_MF_ISROOT in *@mnt_flags. + * + * Finally, check if the file system is mounted read-only, and if so set the + * NTFS_MF_READONLY flag in *@mnt_flags. + * + * On success return 0 with *@mnt_flags set to the ntfs mount flags. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_check_if_mounted(const char *file __attribute__((unused)), + unsigned long *mnt_flags) +{ + *mnt_flags = 0; +#ifdef HAVE_MNTENT_H + return ntfs_mntent_check(file, mnt_flags); +#else + return 0; +#endif +} + +/** + * ntfs_version_is_supported - check if NTFS version is supported. + * @vol: ntfs volume whose version we're interested in. + * + * The function checks if the NTFS volume version is known or not. + * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. + * Version 2.x is used by Windows 2000 Betas. + * Version 3.0 is used by Windows 2000. + * Version 3.1 is used by Windows XP, Windows Server 2003 and Longhorn. + * + * Return 0 if NTFS version is supported otherwise -1 with errno set. + * + * The following error codes are defined: + * EOPNOTSUPP - Unknown NTFS version + * EINVAL - Invalid argument + */ +int ntfs_version_is_supported(ntfs_volume *vol) +{ + u8 major, minor; + + if (!vol) { + errno = EINVAL; + return -1; + } + + major = vol->major_ver; + minor = vol->minor_ver; + + if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) + return 0; + + if (NTFS_V2_X(major, minor)) + return 0; + + if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) + return 0; + + errno = EOPNOTSUPP; + return -1; +} + +/** + * ntfs_logfile_reset - "empty" $LogFile data attribute value + * @vol: ntfs volume whose $LogFile we intend to reset. + * + * Fill the value of the $LogFile data attribute, i.e. the contents of + * the file, with 0xff's, thus marking the journal as empty. + * + * FIXME(?): We might need to zero the LSN field of every single mft + * record as well. (But, first try without doing that and see what + * happens, since chkdsk might pickup the pieces and do it for us...) + * + * On success return 0. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_logfile_reset(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na; + int eo; + + if (!vol) { + errno = EINVAL; + return -1; + } + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + eo = errno; + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + goto error_exit; + } + + if (ntfs_empty_logfile(na)) { + eo = errno; + ntfs_attr_close(na); + goto error_exit; + } + + ntfs_attr_close(na); + return ntfs_inode_close(ni); + +error_exit: + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * ntfs_volume_write_flags - set the flags of an ntfs volume + * @vol: ntfs volume where we set the volume flags + * @flags: new flags + * + * Set the on-disk volume flags in the mft record of $Volume and + * on volume @vol to @flags. + * + * Return 0 if successful and -1 if not with errno set to the error code. + */ +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) +{ + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol || !vol->vol_ni) { + errno = EINVAL; + return -1; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " + "in $Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "but it isn't.\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; + /* Write them to disk. */ + ntfs_inode_mark_dirty(vol->vol_ni); + if (ntfs_inode_sync(vol->vol_ni)) + goto err_out; + + ret = 0; /* success */ +err_out: + ntfs_attr_put_search_ctx(ctx); + return ret; +} + +int ntfs_volume_error(int err) +{ + int ret; + + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; +} + + +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) +{ + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } +} + +int ntfs_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } + return 0; +} + +/* + * Feed the counts of free clusters and free mft records + */ + +int ntfs_volume_get_free_space(ntfs_volume *vol) +{ + ntfs_attr *na; + int ret; + + ret = -1; /* default return */ + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + } else { + na = vol->mftbmp_na; + vol->free_mft_records = ntfs_attr_get_free_bits(na); + + if (vol->free_mft_records >= 0) + vol->free_mft_records += (na->allocated_size - na->data_size) << 3; + + if (vol->free_mft_records < 0) + ntfs_log_perror("Failed to calculate free MFT records"); + else + ret = 0; + } + return (ret); +} + +/** + * ntfs_volume_rename - change the current label on a volume + * @vol: volume to change the label on + * @label: the new label + * @label_len: the length of @label in ntfschars including the terminating NULL + * character, which is mandatory (the value can not exceed 128) + * + * Change the label on the volume @vol to @label. + */ +int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len) +{ + ntfs_attr *na; + char *old_vol_name; + char *new_vol_name = NULL; + int new_vol_name_len; + int err; + + if (NVolReadOnly(vol)) { + ntfs_log_error("Refusing to change label on read-only mounted " + "volume.\n"); + errno = EROFS; + return -1; + } + + label_len *= sizeof(ntfschar); + if (label_len > 0x100) { + ntfs_log_error("New label is too long. Maximum %u characters " + "allowed.\n", + (unsigned)(0x100 / sizeof(ntfschar))); + errno = ERANGE; + return -1; + } + + na = ntfs_attr_open(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0); + if (!na) { + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Lookup of $VOLUME_NAME attribute " + "failed"); + goto err_out; + } + + /* The volume name attribute does not exist. Need to add it. */ + if (ntfs_attr_add(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, + (u8*) label, label_len)) + { + err = errno; + ntfs_log_perror("Encountered error while adding " + "$VOLUME_NAME attribute"); + goto err_out; + } + } + else { + s64 written; + + if (NAttrNonResident(na)) { + err = errno; + ntfs_log_error("Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } + + if (na->data_size != label_len) { + if (ntfs_attr_truncate(na, label_len)) { + err = errno; + ntfs_log_perror("Error resizing resident " + "attribute"); + goto err_out; + } + } + + if (label_len) { + written = ntfs_attr_pwrite(na, 0, label_len, label); + if (written == -1) { + err = errno; + ntfs_log_perror("Error when writing " + "$VOLUME_NAME data"); + goto err_out; + } + else if (written != label_len) { + err = EIO; + ntfs_log_error("Partial write when writing " + "$VOLUME_NAME data."); + goto err_out; + + } + } + } + + new_vol_name_len = + ntfs_ucstombs(label, label_len, &new_vol_name, 0); + if (new_vol_name_len == -1) { + err = errno; + ntfs_log_perror("Error while decoding new volume name"); + goto err_out; + } + + old_vol_name = vol->vol_name; + vol->vol_name = new_vol_name; + free(old_vol_name); + + err = 0; +err_out: + if (na) + ntfs_attr_close(na); + if (err) + errno = err; + return err ? -1 : 0; +} diff --git a/lib/libntfs/src/source/volume.h b/lib/libntfs/src/source/volume.h new file mode 100644 index 0000000..9d62d66 --- /dev/null +++ b/lib/libntfs/src/source/volume.h @@ -0,0 +1,313 @@ +/* + * volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_VOLUME_H +#define _NTFS_VOLUME_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_MNTENT_H +#include +#endif + +/* + * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, + * so we define them ourselves. + */ +#ifndef MS_RDONLY +#define MS_RDONLY 1 +#endif + +#define MS_EXCLUSIVE 0x08000000 + +#ifndef MS_RECOVER +#define MS_RECOVER 0x10000000 +#endif + +#define MS_IGNORE_HIBERFILE 0x20000000 +#define MS_FORENSIC 0x04000000 /* No modification during mount */ + +/* Forward declaration */ +typedef struct _ntfs_volume ntfs_volume; + +#include "param.h" +#include "types.h" +#include "support.h" +#include "device.h" +#include "inode.h" +#include "attrib.h" +#include "index.h" + +/** + * enum ntfs_mount_flags - + * + * Flags returned by the ntfs_check_if_mounted() function. + */ +typedef enum { + NTFS_MF_MOUNTED = 1, /* Device is mounted. */ + NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ + NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ +} ntfs_mount_flags; + +extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); + +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 +} ntfs_volume_status; + +/** + * enum ntfs_volume_state_bits - + * + * Defined bits for the state field in the ntfs_volume structure. + */ +typedef enum { + NV_ReadOnly, /* 1: Volume is read-only. */ + NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ + NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ + NV_NoFixupWarn, /* 1: Do not log fixup errors */ +} ntfs_volume_state_bits; + +#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) +#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) +#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) + +#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) +#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) +#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) + +#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) +#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) +#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) + +#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) +#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) +#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) + +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) + +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) + +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) + +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) + +#define NVolNoFixupWarn(nv) test_nvol_flag(nv, NoFixupWarn) +#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn) +#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn) + +/* + * NTFS version 1.1 and 1.2 are used by Windows NT4. + * NTFS version 2.x is used by Windows 2000 Beta + * NTFS version 3.0 is used by Windows 2000. + * NTFS version 3.1 is used by Windows XP, 2003 and Vista. + */ + +#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1) +#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2) +#define NTFS_V2_X(major, minor) ((major) == 2) +#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0) +#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1) + +#define NTFS_BUF_SIZE 8192 + +/** + * struct _ntfs_volume - structure describing an open volume in memory. + */ +struct _ntfs_volume { + union { + struct ntfs_device *dev; /* NTFS device associated with + the volume. */ + void *sb; /* For kernel porting compatibility. */ + }; + char *vol_name; /* Name of the volume. */ + unsigned long state; /* NTFS specific flags describing this volume. + See ntfs_volume_state_bits above. */ + + ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ + u8 major_ver; /* Ntfs major version of volume. */ + u8 minor_ver; /* Ntfs minor version of volume. */ + le16 flags; /* Bit array of VOLUME_* flags. */ + + u16 sector_size; /* Byte size of a sector. */ + u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ + u32 cluster_size; /* Byte size of a cluster. */ + u32 mft_record_size; /* Byte size of a mft record. */ + u32 indx_record_size; /* Byte size of a INDX record. */ + u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ + u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ + u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ + + /* Variables used by the cluster and mft allocators. */ + u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ + u8 full_zones; /* cluster zones which are full */ + s64 mft_data_pos; /* Mft record number at which to allocate the + next mft record. */ + LCN mft_zone_start; /* First cluster of the mft zone. */ + LCN mft_zone_end; /* First cluster beyond the mft zone. */ + LCN mft_zone_pos; /* Current position in the mft zone. */ + LCN data1_zone_pos; /* Current position in the first data zone. */ + LCN data2_zone_pos; /* Current position in the second data zone. */ + + s64 nr_clusters; /* Volume size in clusters, hence also the + number of bits in lcn_bitmap. */ + ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ + ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute + of FILE_Bitmap. Each bit represents a + cluster on the volume, bit 0 representing + lcn 0 and so on. A set bit means that the + cluster and vice versa. */ + + LCN mft_lcn; /* Logical cluster number of the data attribute + for FILE_MFT. */ + ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ + ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute + of FILE_MFT. */ + ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute + of FILE_MFT. Each bit represents an mft + record in the $DATA attribute, bit 0 + representing mft record 0 and so on. A set + bit means that the mft record is in use and + vice versa. */ + + ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */ + ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */ + ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */ + int secure_reentry; /* check for non-rentries */ + unsigned int secure_flags; /* flags, see security.h for values */ + + int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ + LCN mftmirr_lcn; /* Logical cluster number of the data attribute + for FILE_MFTMirr. */ + ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ + ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute + of FILE_MFTMirr. */ + + ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte + Unicode characters. Obtained from + FILE_UpCase. */ + u32 upcase_len; /* Length in Unicode characters of the upcase + table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ + + ATTR_DEF *attrdef; /* Attribute definitions. Obtained from + FILE_AttrDef. */ + s32 attrdef_len; /* Size of the attribute definition table in + bytes. */ + + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ + BOOL efs_raw; /* volume is mounted for raw access to + efs-encrypted files */ +#ifdef XATTR_MAPPINGS + struct XATTRMAPPING *xattr_mapping; +#endif /* XATTR_MAPPINGS */ +#if CACHE_INODE_SIZE + struct CACHE_HEADER *xinode_cache; +#endif +#if CACHE_NIDATA_SIZE + struct CACHE_HEADER *nidata_cache; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHE_HEADER *lookup_cache; +#endif +#if CACHE_SECURID_SIZE + struct CACHE_HEADER *securid_cache; +#endif +#if CACHE_LEGACY_SIZE + struct CACHE_HEADER *legacy_cache; +#endif + +}; + +extern const char *ntfs_home; + +extern ntfs_volume *ntfs_volume_alloc(void); + +extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + unsigned long flags); + +extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, + unsigned long flags); + +extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); +extern int ntfs_umount(ntfs_volume *vol, const BOOL force); + +extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); +extern int ntfs_logfile_reset(ntfs_volume *vol); + +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); + +extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); + +extern int ntfs_volume_get_free_space(ntfs_volume *vol); +extern int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len); + +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); +extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); + +#endif /* defined _NTFS_VOLUME_H */ + diff --git a/lib/libntfs/src/source/wii_release/acls.d b/lib/libntfs/src/source/wii_release/acls.d new file mode 100644 index 0000000..eba3994 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/acls.d @@ -0,0 +1,76 @@ +acls.o: c:/progging/cfgMod/lib/libntfs/src/source/acls.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/acls.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/acls.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/attrib.d b/lib/libntfs/src/source/wii_release/attrib.d new file mode 100644 index 0000000..6cc7e8b --- /dev/null +++ b/lib/libntfs/src/source/wii_release/attrib.d @@ -0,0 +1,88 @@ +attrib.o: c:/progging/cfgMod/lib/libntfs/src/source/attrib.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compress.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compress.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: diff --git a/lib/libntfs/src/source/wii_release/attrib_frag.d b/lib/libntfs/src/source/wii_release/attrib_frag.d new file mode 100644 index 0000000..c5c037f --- /dev/null +++ b/lib/libntfs/src/source/wii_release/attrib_frag.d @@ -0,0 +1,244 @@ +attrib_frag.o: c:/progging/cfgMod/lib/libntfs/src/source/attrib_frag.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compress.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs_frag.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compress.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs_frag.h: diff --git a/lib/libntfs/src/source/wii_release/attrlist.d b/lib/libntfs/src/source/wii_release/attrlist.d new file mode 100644 index 0000000..57c411d --- /dev/null +++ b/lib/libntfs/src/source/wii_release/attrlist.d @@ -0,0 +1,70 @@ +attrlist.o: c:/progging/cfgMod/lib/libntfs/src/source/attrlist.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/bitmap.d b/lib/libntfs/src/source/wii_release/bitmap.d new file mode 100644 index 0000000..b4677e2 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/bitmap.d @@ -0,0 +1,70 @@ +bitmap.o: c:/progging/cfgMod/lib/libntfs/src/source/bitmap.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/bootsect.d b/lib/libntfs/src/source/wii_release/bootsect.d new file mode 100644 index 0000000..e77d661 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/bootsect.d @@ -0,0 +1,67 @@ +bootsect.o: c:/progging/cfgMod/lib/libntfs/src/source/bootsect.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: diff --git a/lib/libntfs/src/source/wii_release/cache.d b/lib/libntfs/src/source/wii_release/cache.d new file mode 100644 index 0000000..2003ab9 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/cache.d @@ -0,0 +1,76 @@ +cache.o: c:/progging/cfgMod/lib/libntfs/src/source/cache.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/cache2.d b/lib/libntfs/src/source/wii_release/cache2.d new file mode 100644 index 0000000..88aaae1 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/cache2.d @@ -0,0 +1,169 @@ +cache2.o: c:/progging/cfgMod/lib/libntfs/src/source/cache2.c \ + c:/devkitPro/libogc/include/ogc/lwp_watchdog.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache2.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bit_ops.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h + +c:/devkitPro/libogc/include/ogc/lwp_watchdog.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache2.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bit_ops.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: diff --git a/lib/libntfs/src/source/wii_release/collate.d b/lib/libntfs/src/source/wii_release/collate.d new file mode 100644 index 0000000..d672edb --- /dev/null +++ b/lib/libntfs/src/source/wii_release/collate.d @@ -0,0 +1,67 @@ +collate.o: c:/progging/cfgMod/lib/libntfs/src/source/collate.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/collate.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/collate.h: diff --git a/lib/libntfs/src/source/wii_release/compat.d b/lib/libntfs/src/source/wii_release/compat.d new file mode 100644 index 0000000..6d824a1 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/compat.d @@ -0,0 +1,10 @@ +compat.o: c:/progging/cfgMod/lib/libntfs/src/source/compat.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: diff --git a/lib/libntfs/src/source/wii_release/compress.d b/lib/libntfs/src/source/wii_release/compress.d new file mode 100644 index 0000000..5185afb --- /dev/null +++ b/lib/libntfs/src/source/wii_release/compress.d @@ -0,0 +1,73 @@ +compress.o: c:/progging/cfgMod/lib/libntfs/src/source/compress.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compress.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compress.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/debug.d b/lib/libntfs/src/source/wii_release/debug.d new file mode 100644 index 0000000..2a61633 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/debug.d @@ -0,0 +1,64 @@ +debug.o: c:/progging/cfgMod/lib/libntfs/src/source/debug.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: diff --git a/lib/libntfs/src/source/wii_release/device.d b/lib/libntfs/src/source/wii_release/device.d new file mode 100644 index 0000000..13bfeab --- /dev/null +++ b/lib/libntfs/src/source/wii_release/device.d @@ -0,0 +1,70 @@ +device.o: c:/progging/cfgMod/lib/libntfs/src/source/device.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/device_io.d b/lib/libntfs/src/source/wii_release/device_io.d new file mode 100644 index 0000000..e8141a2 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/device_io.d @@ -0,0 +1,4 @@ +device_io.o: c:/progging/cfgMod/lib/libntfs/src/source/device_io.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: diff --git a/lib/libntfs/src/source/wii_release/dir.d b/lib/libntfs/src/source/wii_release/dir.d new file mode 100644 index 0000000..a9fac8c --- /dev/null +++ b/lib/libntfs/src/source/wii_release/dir.d @@ -0,0 +1,85 @@ +dir.o: c:/progging/cfgMod/lib/libntfs/src/source/dir.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/object_id.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/object_id.h: diff --git a/lib/libntfs/src/source/wii_release/efs.d b/lib/libntfs/src/source/wii_release/efs.d new file mode 100644 index 0000000..d60a567 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/efs.d @@ -0,0 +1,73 @@ +efs.o: c:/progging/cfgMod/lib/libntfs/src/source/efs.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/gekko_io.d b/lib/libntfs/src/source/wii_release/gekko_io.d new file mode 100644 index 0000000..05d4580 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/gekko_io.d @@ -0,0 +1,229 @@ +gekko_io.o: c:/progging/cfgMod/lib/libntfs/src/source/gekko_io.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/gekko_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache2.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/gekko_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache2.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h: diff --git a/lib/libntfs/src/source/wii_release/index.d b/lib/libntfs/src/source/wii_release/index.d new file mode 100644 index 0000000..d2a9e72 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/index.d @@ -0,0 +1,82 @@ +index.o: c:/progging/cfgMod/lib/libntfs/src/source/index.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/collate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/collate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/inode.d b/lib/libntfs/src/source/wii_release/inode.d new file mode 100644 index 0000000..35162d1 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/inode.d @@ -0,0 +1,79 @@ +inode.o: c:/progging/cfgMod/lib/libntfs/src/source/inode.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/lcnalloc.d b/lib/libntfs/src/source/wii_release/lcnalloc.d new file mode 100644 index 0000000..406e75b --- /dev/null +++ b/lib/libntfs/src/source/wii_release/lcnalloc.d @@ -0,0 +1,73 @@ +lcnalloc.o: c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/logfile.d b/lib/libntfs/src/source/wii_release/logfile.d new file mode 100644 index 0000000..bfa7463 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/logfile.d @@ -0,0 +1,73 @@ +logfile.o: c:/progging/cfgMod/lib/libntfs/src/source/logfile.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logfile.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logfile.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/logging.d b/lib/libntfs/src/source/wii_release/logging.d new file mode 100644 index 0000000..f73470b --- /dev/null +++ b/lib/libntfs/src/source/wii_release/logging.d @@ -0,0 +1,22 @@ +logging.o: c:/progging/cfgMod/lib/libntfs/src/source/logging.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/mft.d b/lib/libntfs/src/source/wii_release/mft.d new file mode 100644 index 0000000..a82dbc0 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/mft.d @@ -0,0 +1,73 @@ +mft.o: c:/progging/cfgMod/lib/libntfs/src/source/mft.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/misc.d b/lib/libntfs/src/source/wii_release/misc.d new file mode 100644 index 0000000..b531389 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/misc.d @@ -0,0 +1,22 @@ +misc.o: c:/progging/cfgMod/lib/libntfs/src/source/misc.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: diff --git a/lib/libntfs/src/source/wii_release/mst.d b/lib/libntfs/src/source/wii_release/mst.d new file mode 100644 index 0000000..bb04f03 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/mst.d @@ -0,0 +1,67 @@ +mst.o: c:/progging/cfgMod/lib/libntfs/src/source/mst.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mst.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mst.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: diff --git a/lib/libntfs/src/source/wii_release/ntfs.d b/lib/libntfs/src/source/wii_release/ntfs.d new file mode 100644 index 0000000..805d60f --- /dev/null +++ b/lib/libntfs/src/source/wii_release/ntfs.d @@ -0,0 +1,247 @@ +ntfs.o: c:/progging/cfgMod/lib/libntfs/src/source/ntfs.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/gekko_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache2.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/gekko_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache2.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: diff --git a/lib/libntfs/src/source/wii_release/ntfsdir.d b/lib/libntfs/src/source/wii_release/ntfsdir.d new file mode 100644 index 0000000..e0a3e72 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/ntfsdir.d @@ -0,0 +1,232 @@ +ntfsdir.o: c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h: diff --git a/lib/libntfs/src/source/wii_release/ntfsfile.d b/lib/libntfs/src/source/wii_release/ntfsfile.d new file mode 100644 index 0000000..7b69fe2 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/ntfsfile.d @@ -0,0 +1,232 @@ +ntfsfile.o: c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h: diff --git a/lib/libntfs/src/source/wii_release/ntfsfile_frag.d b/lib/libntfs/src/source/wii_release/ntfsfile_frag.d new file mode 100644 index 0000000..2cba67a --- /dev/null +++ b/lib/libntfs/src/source/wii_release/ntfsfile_frag.d @@ -0,0 +1,239 @@ +ntfsfile_frag.o: \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile_frag.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h \ + c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs_frag.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs.h: + +c:/progging/cfgMod/lib/libntfs/src/source/../include/ntfs_frag.h: diff --git a/lib/libntfs/src/source/wii_release/ntfsinternal.d b/lib/libntfs/src/source/wii_release/ntfsinternal.d new file mode 100644 index 0000000..63acba0 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/ntfsinternal.d @@ -0,0 +1,244 @@ +ntfsinternal.o: c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/efs.h \ + c:/devkitPro/libogc/include/gccore.h \ + c:/devkitPro/libogc/include/ogc/dsp.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqueue.h \ + c:/devkitPro/libogc/include/ogc/lwp_queue.h \ + c:/devkitPro/libogc/include/ogc/aram.h \ + c:/devkitPro/libogc/include/ogc/arqmgr.h \ + c:/devkitPro/libogc/include/ogc/audio.h \ + c:/devkitPro/libogc/include/ogc/cache.h \ + c:/devkitPro/libogc/include/ogc/card.h \ + c:/devkitPro/libogc/include/ogc/cast.h \ + c:/devkitPro/libogc/include/ogc/color.h \ + c:/devkitPro/libogc/include/ogc/consol.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/dvd.h \ + c:/devkitPro/libogc/include/ogc/disc_io.h \ + c:/devkitPro/libogc/include/ogc/exi.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/gu.h \ + c:/devkitPro/libogc/include/ogc/si.h \ + c:/devkitPro/libogc/include/ogc/gx_struct.h \ + c:/devkitPro/libogc/include/ogc/irq.h \ + c:/devkitPro/libogc/include/ogc/context.h \ + c:/devkitPro/libogc/include/ogc/lwp.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/message.h \ + c:/devkitPro/libogc/include/ogc/semaphore.h \ + c:/devkitPro/libogc/include/ogc/pad.h \ + c:/devkitPro/libogc/include/ogc/tpl.h \ + c:/devkitPro/libogc/include/ogc/gx.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/gcutil.h \ + c:/devkitPro/libogc/include/ogc/video.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/usbgecko.h \ + c:/devkitPro/libogc/include/ogc/video_types.h \ + c:/devkitPro/libogc/include/ogc/texconv.h \ + c:/devkitPro/libogc/include/ogc/ipc.h \ + c:/devkitPro/libogc/include/ogc/es.h \ + c:/devkitPro/libogc/include/ogc/stm.h \ + c:/devkitPro/libogc/include/ogc/ios.h \ + c:/devkitPro/libogc/include/ogc/usb.h \ + c:/devkitPro/libogc/include/ogc/isfs.h \ + c:/devkitPro/libogc/include/ogc/conf.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h \ + c:/devkitPro/libogc/include/ogc/mutex.h \ + c:/devkitPro/libogc/include/ogc/system.h \ + c:/devkitPro/libogc/include/ogc/wiilaunch.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h \ + c:/devkitPro/libogc/include/sdcard/wiisd_io.h \ + c:/devkitPro/libogc/include/sdcard/gcsd.h \ + c:/devkitPro/libogc/include/ogc/usbstorage.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsinternal.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/efs.h: + +c:/devkitPro/libogc/include/gccore.h: + +c:/devkitPro/libogc/include/ogc/dsp.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqueue.h: + +c:/devkitPro/libogc/include/ogc/lwp_queue.h: + +c:/devkitPro/libogc/include/ogc/aram.h: + +c:/devkitPro/libogc/include/ogc/arqmgr.h: + +c:/devkitPro/libogc/include/ogc/audio.h: + +c:/devkitPro/libogc/include/ogc/cache.h: + +c:/devkitPro/libogc/include/ogc/card.h: + +c:/devkitPro/libogc/include/ogc/cast.h: + +c:/devkitPro/libogc/include/ogc/color.h: + +c:/devkitPro/libogc/include/ogc/consol.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/dvd.h: + +c:/devkitPro/libogc/include/ogc/disc_io.h: + +c:/devkitPro/libogc/include/ogc/exi.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/gu.h: + +c:/devkitPro/libogc/include/ogc/si.h: + +c:/devkitPro/libogc/include/ogc/gx_struct.h: + +c:/devkitPro/libogc/include/ogc/irq.h: + +c:/devkitPro/libogc/include/ogc/context.h: + +c:/devkitPro/libogc/include/ogc/lwp.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/message.h: + +c:/devkitPro/libogc/include/ogc/semaphore.h: + +c:/devkitPro/libogc/include/ogc/pad.h: + +c:/devkitPro/libogc/include/ogc/tpl.h: + +c:/devkitPro/libogc/include/ogc/gx.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/gcutil.h: + +c:/devkitPro/libogc/include/ogc/video.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/usbgecko.h: + +c:/devkitPro/libogc/include/ogc/video_types.h: + +c:/devkitPro/libogc/include/ogc/texconv.h: + +c:/devkitPro/libogc/include/ogc/ipc.h: + +c:/devkitPro/libogc/include/ogc/es.h: + +c:/devkitPro/libogc/include/ogc/stm.h: + +c:/devkitPro/libogc/include/ogc/ios.h: + +c:/devkitPro/libogc/include/ogc/usb.h: + +c:/devkitPro/libogc/include/ogc/isfs.h: + +c:/devkitPro/libogc/include/ogc/conf.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: + +c:/devkitPro/libogc/include/ogc/mutex.h: + +c:/devkitPro/libogc/include/ogc/system.h: + +c:/devkitPro/libogc/include/ogc/wiilaunch.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsdir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfsfile.h: + +c:/devkitPro/libogc/include/sdcard/wiisd_io.h: + +c:/devkitPro/libogc/include/sdcard/gcsd.h: + +c:/devkitPro/libogc/include/ogc/usbstorage.h: diff --git a/lib/libntfs/src/source/wii_release/object_id.d b/lib/libntfs/src/source/wii_release/object_id.d new file mode 100644 index 0000000..27f4bf6 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/object_id.d @@ -0,0 +1,76 @@ +object_id.o: c:/progging/cfgMod/lib/libntfs/src/source/object_id.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/object_id.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/object_id.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/realpath.d b/lib/libntfs/src/source/wii_release/realpath.d new file mode 100644 index 0000000..19f988f --- /dev/null +++ b/lib/libntfs/src/source/wii_release/realpath.d @@ -0,0 +1,10 @@ +realpath.o: c:/progging/cfgMod/lib/libntfs/src/source/realpath.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/realpath.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/realpath.h: diff --git a/lib/libntfs/src/source/wii_release/reparse.d b/lib/libntfs/src/source/wii_release/reparse.d new file mode 100644 index 0000000..4127c98 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/reparse.d @@ -0,0 +1,76 @@ +reparse.o: c:/progging/cfgMod/lib/libntfs/src/source/reparse.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h \ + c:/progging/cfgMod/lib/libntfs/src/source/reparse.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/lcnalloc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: + +c:/progging/cfgMod/lib/libntfs/src/source/reparse.h: diff --git a/lib/libntfs/src/source/wii_release/runlist.d b/lib/libntfs/src/source/wii_release/runlist.d new file mode 100644 index 0000000..18a7fca --- /dev/null +++ b/lib/libntfs/src/source/wii_release/runlist.d @@ -0,0 +1,67 @@ +runlist.o: c:/progging/cfgMod/lib/libntfs/src/source/runlist.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/security.d b/lib/libntfs/src/source/wii_release/security.d new file mode 100644 index 0000000..089a502 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/security.d @@ -0,0 +1,82 @@ +security.o: c:/progging/cfgMod/lib/libntfs/src/source/security.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h \ + c:/progging/cfgMod/lib/libntfs/src/source/security.h \ + c:/progging/cfgMod/lib/libntfs/src/source/acls.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bitmap.h: + +c:/progging/cfgMod/lib/libntfs/src/source/security.h: + +c:/progging/cfgMod/lib/libntfs/src/source/acls.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/unistr.d b/lib/libntfs/src/source/wii_release/unistr.d new file mode 100644 index 0000000..0cf7f6e --- /dev/null +++ b/lib/libntfs/src/source/wii_release/unistr.d @@ -0,0 +1,67 @@ +unistr.o: c:/progging/cfgMod/lib/libntfs/src/source/unistr.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/volume.d b/lib/libntfs/src/source/wii_release/volume.d new file mode 100644 index 0000000..e0701ef --- /dev/null +++ b/lib/libntfs/src/source/wii_release/volume.d @@ -0,0 +1,82 @@ +volume.o: c:/progging/cfgMod/lib/libntfs/src/source/volume.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h \ + c:/progging/cfgMod/lib/libntfs/src/source/param.h \ + c:/progging/cfgMod/lib/libntfs/src/source/compat.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h \ + c:/progging/cfgMod/lib/libntfs/src/source/volume.h \ + c:/progging/cfgMod/lib/libntfs/src/source/types.h \ + c:/devkitPro/libogc/include/gctypes.h \ + c:/progging/cfgMod/lib/libntfs/src/source/support.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device.h \ + c:/progging/cfgMod/lib/libntfs/src/source/device_io.h \ + c:/progging/cfgMod/lib/libntfs/src/source/inode.h \ + c:/progging/cfgMod/lib/libntfs/src/source/layout.h \ + c:/progging/cfgMod/lib/libntfs/src/source/endians.h \ + c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h \ + c:/progging/cfgMod/lib/libntfs/src/source/attrib.h \ + c:/progging/cfgMod/lib/libntfs/src/source/unistr.h \ + c:/progging/cfgMod/lib/libntfs/src/source/runlist.h \ + c:/progging/cfgMod/lib/libntfs/src/source/debug.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logging.h \ + c:/progging/cfgMod/lib/libntfs/src/source/index.h \ + c:/progging/cfgMod/lib/libntfs/src/source/mft.h \ + c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h \ + c:/progging/cfgMod/lib/libntfs/src/source/logfile.h \ + c:/progging/cfgMod/lib/libntfs/src/source/dir.h \ + c:/progging/cfgMod/lib/libntfs/src/source/cache.h \ + c:/progging/cfgMod/lib/libntfs/src/source/realpath.h \ + c:/progging/cfgMod/lib/libntfs/src/source/misc.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: + +c:/progging/cfgMod/lib/libntfs/src/source/param.h: + +c:/progging/cfgMod/lib/libntfs/src/source/compat.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mem_allocate.h: + +c:/progging/cfgMod/lib/libntfs/src/source/volume.h: + +c:/progging/cfgMod/lib/libntfs/src/source/types.h: + +c:/devkitPro/libogc/include/gctypes.h: + +c:/progging/cfgMod/lib/libntfs/src/source/support.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device.h: + +c:/progging/cfgMod/lib/libntfs/src/source/device_io.h: + +c:/progging/cfgMod/lib/libntfs/src/source/inode.h: + +c:/progging/cfgMod/lib/libntfs/src/source/layout.h: + +c:/progging/cfgMod/lib/libntfs/src/source/endians.h: + +c:/progging/cfgMod/lib/libntfs/src/source/ntfstime.h: + +c:/progging/cfgMod/lib/libntfs/src/source/attrib.h: + +c:/progging/cfgMod/lib/libntfs/src/source/unistr.h: + +c:/progging/cfgMod/lib/libntfs/src/source/runlist.h: + +c:/progging/cfgMod/lib/libntfs/src/source/debug.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logging.h: + +c:/progging/cfgMod/lib/libntfs/src/source/index.h: + +c:/progging/cfgMod/lib/libntfs/src/source/mft.h: + +c:/progging/cfgMod/lib/libntfs/src/source/bootsect.h: + +c:/progging/cfgMod/lib/libntfs/src/source/logfile.h: + +c:/progging/cfgMod/lib/libntfs/src/source/dir.h: + +c:/progging/cfgMod/lib/libntfs/src/source/cache.h: + +c:/progging/cfgMod/lib/libntfs/src/source/realpath.h: + +c:/progging/cfgMod/lib/libntfs/src/source/misc.h: diff --git a/lib/libntfs/src/source/wii_release/xattrs.d b/lib/libntfs/src/source/wii_release/xattrs.d new file mode 100644 index 0000000..5bd2617 --- /dev/null +++ b/lib/libntfs/src/source/wii_release/xattrs.d @@ -0,0 +1,4 @@ +xattrs.o: c:/progging/cfgMod/lib/libntfs/src/source/xattrs.c \ + c:/progging/cfgMod/lib/libntfs/src/source/config.h + +c:/progging/cfgMod/lib/libntfs/src/source/config.h: diff --git a/lib/libntfs/src/source/xattrs.c b/lib/libntfs/src/source/xattrs.c new file mode 100644 index 0000000..5be2c06 --- /dev/null +++ b/lib/libntfs/src/source/xattrs.c @@ -0,0 +1,791 @@ +/** + * xattrs.c : common functions to deal with system extended attributes + * + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SETXATTR /* extended attributes support required */ + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "security.h" +#include "acls.h" +#include "efs.h" +#include "reparse.h" +#include "object_id.h" +#include "misc.h" +#include "logging.h" +#include "xattrs.h" + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Posix ACL structures + */ + +struct LE_POSIX_ACE { + le16 tag; + le16 perms; + le32 id; +} __attribute__((__packed__)); + +struct LE_POSIX_ACL { + u8 version; + u8 flags; + le16 filler; + struct LE_POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +#endif +#endif + +static const char xattr_ntfs_3g[] = "ntfs-3g."; +static const char nf_ns_user_prefix[] = "user."; +static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; + +static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; +static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; +static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; +static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo"; +static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; +static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id"; +static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; +static const char nf_ns_xattr_times[] = "system.ntfs_times"; +static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be"; +static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime"; +static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be"; +static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; +static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; + +static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; + +struct XATTRNAME { + enum SYSTEMXATTRS xattr; + const char *name; +} ; + +static struct XATTRNAME nf_ns_xattr_names[] = { + { XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl }, + { XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib }, + { XATTR_NTFS_ATTRIB_BE, nf_ns_xattr_attrib_be }, + { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, + { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, + { XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id }, + { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, + { XATTR_NTFS_TIMES, nf_ns_xattr_times }, + { XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be }, + { XATTR_NTFS_CRTIME, nf_ns_xattr_crtime }, + { XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be }, + { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, + { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, + { XATTR_UNMAPPED, (char*)NULL } /* terminator */ +}; + +/* + * Make an integer big-endian + * + * Swap bytes on a small-endian computer and does nothing on a + * big-endian computer. + */ + +static void fix_big_endian(char *p, int size) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + int i,j; + int c; + + i = 0; + j = size - 1; + while (i < j) { + c = p[i]; + p[i++] = p[j]; + p[j--] = c; + } +#endif +} + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Make a Posix ACL CPU endian + */ + +static int le_acl_to_cpu(const struct LE_POSIX_ACL *le_acl, size_t size, + struct POSIX_ACL *acl) +{ + int i; + int cnt; + + acl->version = le_acl->version; + acl->flags = le_acl->flags; + acl->filler = 0; + cnt = (size - sizeof(struct LE_POSIX_ACL)) / sizeof(struct LE_POSIX_ACE); + for (i=0; iace[i].tag = le16_to_cpu(le_acl->ace[i].tag); + acl->ace[i].perms = le16_to_cpu(le_acl->ace[i].perms); + acl->ace[i].id = le32_to_cpu(le_acl->ace[i].id); + } + return (0); +} + +/* + * Make a Posix ACL little endian + */ + +int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size, + struct LE_POSIX_ACL *le_acl) +{ + int i; + int cnt; + + le_acl->version = acl->version; + le_acl->flags = acl->flags; + le_acl->filler = const_cpu_to_le16(0); + cnt = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + for (i=0; iace[i].tag = cpu_to_le16(acl->ace[i].tag); + le_acl->ace[i].perms = cpu_to_le16(acl->ace[i].perms); + le_acl->ace[i].id = cpu_to_le32(acl->ace[i].id); + } + return (0); +} + +#endif +#endif + +/* + * Determine whether an extended attribute is mapped to + * internal data (original name in system namespace, or renamed) + */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol) +{ + struct XATTRNAME *p; + enum SYSTEMXATTRS ret; +#ifdef XATTR_MAPPINGS + const struct XATTRMAPPING *q; +#endif /* XATTR_MAPPINGS */ + + p = nf_ns_xattr_names; + while (p->name && strcmp(p->name,name)) + p++; + ret = p->xattr; +#ifdef XATTR_MAPPINGS + if (!p->name && vol && vol->xattr_mapping) { + q = vol->xattr_mapping; + while (q && strcmp(q->name,name)) + q = q->next; + if (q) + ret = q->xattr; + } +#else /* XATTR_MAPPINGS */ + if (!p->name + && vol + && vol->efs_raw + && !strcmp(nf_ns_alt_xattr_efsinfo,name)) + ret = XATTR_NTFS_EFSINFO; +#endif /* XATTR_MAPPINGS */ + return (ret); +} + +#ifdef XATTR_MAPPINGS + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + * Note : errors are logged, but not returned +// TODO partially share with acls.c + */ + +static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *pe; + char *ps; + char *pu; + enum SYSTEMXATTRS xattr; + int gotend; + char maptext[LINESZ]; + struct XATTRMAPPING *item; + + src = *psrc; + dst = 0; + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + /* ignore spaces */ + if ((dst < LINESZ) + && (buf[src] != '\r') + && (buf[src] != '\t') + && (buf[src] != ' ')) + maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((maptext[0] == '#') || !gotend)); + item = (struct XATTRMAPPING*)NULL; + if (gotend) { + /* decompose into system name and user name */ + ps = maptext; + pu = strchr(maptext,':'); + if (pu) { + *pu++ = 0; + pe = strchr(pu,':'); + if (pe) + *pe = 0; + /* check name validity */ + if ((strlen(pu) < 6) || strncmp(pu,"user.",5)) + pu = (char*)NULL; + xattr = ntfs_xattr_system_type(ps, + (ntfs_volume*)NULL); + if (xattr == XATTR_UNMAPPED) + pu = (char*)NULL; + } + if (pu) { + item = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(pu)); + if (item) { + item->xattr = xattr; + strcpy(item->name,pu); + item->next = (struct XATTRMAPPING*)NULL; + } + } else { + ntfs_log_early_error("Bad xattr mapping item, aborting\n"); + } + } + *psrc = src; + return (item); +} + +/* + * Read xattr mapping file and split into their attribute. + * Parameters are kept in a chained list. + * Returns the head of list, if any + * Errors are logged, but not returned + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader, + void *fileid) +{ + char buf[BUFSZ]; + struct XATTRMAPPING *item; + struct XATTRMAPPING *current; + struct XATTRMAPPING *firstitem; + struct XATTRMAPPING *lastitem; + BOOL duplicated; + int src; + off_t offs; + s64 size; + + firstitem = (struct XATTRMAPPING*)NULL; + lastitem = (struct XATTRMAPPING*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + /* check no double mapping */ + duplicated = FALSE; + for (current=firstitem; current; current=current->next) + if ((current->xattr == item->xattr) + || !strcmp(current->name,item->name)) + duplicated = TRUE; + if (duplicated) { + free(item); + ntfs_log_early_error("Conflicting xattr mapping ignored\n"); + } else { + item->next = (struct XATTRMAPPING*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } + } while (item); + } + return (firstitem); +} + +/* + * Build the extended attribute mappings to user namespace + * + * Note : no error is returned. If we refused mounting when there + * is an error it would be too difficult to fix the offending file + */ + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *xattrmap_path) +{ + struct XATTRMAPPING *firstmapping; + struct XATTRMAPPING *mapping; + BOOL user_efs; + BOOL notfound; + ntfs_inode *ni; + int fd; + + firstmapping = (struct XATTRMAPPING*)NULL; + notfound = FALSE; + if (!xattrmap_path) + xattrmap_path = XATTRMAPPINGFILE; + if (xattrmap_path[0] == '/') { + fd = open(xattrmap_path,O_RDONLY); + if (fd > 0) { + firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd); + close(fd); + } else + notfound = TRUE; + } else { + ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path); + if (ni) { + firstmapping = ntfs_read_xattr_mapping(localread, ni); + ntfs_inode_close(ni); + } else + notfound = TRUE; + } + if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) { + ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path); + } + if (vol->efs_raw) { + user_efs = TRUE; + for (mapping=firstmapping; mapping; mapping=mapping->next) + if (mapping->xattr == XATTR_NTFS_EFSINFO) + user_efs = FALSE; + } else + user_efs = FALSE; + if (user_efs) { + mapping = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(nf_ns_alt_xattr_efsinfo)); + if (mapping) { + mapping->next = firstmapping; + mapping->xattr = XATTR_NTFS_EFSINFO; + strcpy(mapping->name,nf_ns_alt_xattr_efsinfo); + firstmapping = mapping; + } + } + return (firstmapping); +} + +void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) +{ + struct XATTRMAPPING *p, *q; + + p = mapping; + while (p) { + q = p->next; + free(p); + p = q; + } +} + +#endif /* XATTR_MAPPINGS */ + + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int res; + int i; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + /* + * the returned value is the needed + * size. If it is too small, no copy + * is done, and the caller has to + * issue a new call with correct size. + */ + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_get_ntfs_acl(scx, ni, value, size); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_access, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_default, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access, + value, size); + break; + case XATTR_POSIX_DEF : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(ni, value, size); + break; + case XATTR_NTFS_ATTRIB_BE : + res = ntfs_get_ntfs_attrib(ni, value, size); + if ((res == 4) && value) { + if (size >= 4) + fix_big_endian(value,4); + else + res = -EINVAL; + } + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_get_efs_info(ni, value, size); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(ni, value, size); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_get_ntfs_object_id(ni, value, size); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(ni, value, size); + break; + case XATTR_NTFS_TIMES_BE: + res = ntfs_inode_get_times(ni, value, size); + if ((res > 0) && value) { + for (i=0; (i+1)*sizeof(u64)<=(unsigned int)res; i++) + fix_big_endian(&value[i*sizeof(u64)], + sizeof(u64)); + } + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + break; + case XATTR_NTFS_CRTIME_BE: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + if ((res >= (int)sizeof(u64)) && value) + fix_big_endian(value,sizeof(u64)); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res; + int i; + char buf[4*sizeof(u64)]; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(scx, ni, value, size, flags); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_access, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_default, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access, + value, size, flags); + break; + case XATTR_POSIX_DEF : + res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size, flags); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_ATTRIB_BE : + if (value && (size >= 4)) { + memcpy(buf,value,4); + fix_big_endian(buf,4); + res = ntfs_set_ntfs_attrib(ni, buf, 4, flags); + } else + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_set_efs_info(ni, value, size, flags); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_set_ntfs_object_id(ni, value, size, flags); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + /* warning : this closes both inodes */ + res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, + size, flags); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_TIMES_BE: + if (value && (size > 0) && (size <= 4*sizeof(u64))) { + memcpy(buf,value,size); + for (i=0; (i+1)*sizeof(u64)<=size; i++) + fix_big_endian(&buf[i*sizeof(u64)], + sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, size, flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_set_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size), flags); + break; + case XATTR_NTFS_CRTIME_BE: + if (value && (size >= sizeof(u64))) { + memcpy(buf,value,sizeof(u64)); + fix_big_endian(buf,sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, sizeof(u64), flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = 0; + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; +#if POSIXACLS + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_posix_acl(scx, ni, + (attr == XATTR_POSIX_ACC ? + nf_ns_xattr_posix_access : + nf_ns_xattr_posix_default))) + res = -errno; + } else + res = -errno; + break; +#endif + case XATTR_NTFS_REPARSE_DATA : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_reparse_data(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_OBJECT_ID : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_object_id(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_DOS_NAME: + if (ni && dir_ni) { + if (ntfs_remove_ntfs_dos_name(ni,dir_ni)) + res = -errno; + /* ni and dir_ni have been closed */ + } else + res = -errno; + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ diff --git a/lib/libntfs/src/source/xattrs.h b/lib/libntfs/src/source/xattrs.h new file mode 100644 index 0000000..5158311 --- /dev/null +++ b/lib/libntfs/src/source/xattrs.h @@ -0,0 +1,75 @@ +/* + * xattrs.h : definitions related to system extended attributes + * + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_XATTR_H_ +#define _NTFS_XATTR_H_ + +/* + * Identification of data mapped to the system name space + */ + +enum SYSTEMXATTRS { + XATTR_UNMAPPED, + XATTR_NTFS_ACL, + XATTR_NTFS_ATTRIB, + XATTR_NTFS_ATTRIB_BE, + XATTR_NTFS_EFSINFO, + XATTR_NTFS_REPARSE_DATA, + XATTR_NTFS_OBJECT_ID, + XATTR_NTFS_DOS_NAME, + XATTR_NTFS_TIMES, + XATTR_NTFS_TIMES_BE, + XATTR_NTFS_CRTIME, + XATTR_NTFS_CRTIME_BE, + XATTR_POSIX_ACC, + XATTR_POSIX_DEF +} ; + +struct XATTRMAPPING { + struct XATTRMAPPING *next; + enum SYSTEMXATTRS xattr; + char name[1]; /* variable length */ +} ; + +#ifdef XATTR_MAPPINGS + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *path); +void ntfs_xattr_free_mapping(struct XATTRMAPPING*); + +#endif /* XATTR_MAPPINGS */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol); + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni); + +#endif /* _NTFS_XATTR_H_ */ diff --git a/lib/png-1.2.34/include/png.h b/lib/png-1.2.34/include/png.h new file mode 100644 index 0000000..3873728 --- /dev/null +++ b/lib/png-1.2.34/include/png.h @@ -0,0 +1,3661 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.34 - December 18, 2008 + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.34 - December 18, 2008: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * 1.2.22beta1-4 13 10222 12.so.0.22[.0] + * 1.0.30rc1 10 10030 10.so.0.30[.0] + * 1.2.22rc1 13 10222 12.so.0.22[.0] + * 1.0.30 10 10030 10.so.0.30[.0] + * 1.2.22 13 10222 12.so.0.22[.0] + * 1.2.23beta01-05 13 10223 12.so.0.23[.0] + * 1.2.23rc01 13 10223 12.so.0.23[.0] + * 1.2.23 13 10223 12.so.0.23[.0] + * 1.2.24beta01-02 13 10224 12.so.0.24[.0] + * 1.2.24rc01 13 10224 12.so.0.24[.0] + * 1.2.24 13 10224 12.so.0.24[.0] + * 1.2.25beta01-06 13 10225 12.so.0.25[.0] + * 1.2.25rc01-02 13 10225 12.so.0.25[.0] + * 1.0.31 10 10031 10.so.0.31[.0] + * 1.2.25 13 10225 12.so.0.25[.0] + * 1.2.26beta01-06 13 10226 12.so.0.26[.0] + * 1.2.26rc01 13 10226 12.so.0.26[.0] + * 1.2.26 13 10226 12.so.0.26[.0] + * 1.0.32 10 10032 10.so.0.32[.0] + * 1.2.27beta01-06 13 10227 12.so.0.27[.0] + * 1.2.27rc01 13 10227 12.so.0.27[.0] + * 1.0.33 10 10033 10.so.0.33[.0] + * 1.2.27 13 10227 12.so.0.27[.0] + * 1.0.34 10 10034 10.so.0.34[.0] + * 1.2.28 13 10228 12.so.0.28[.0] + * 1.2.29beta01-03 13 10229 12.so.0.29[.0] + * 1.2.29rc01 13 10229 12.so.0.29[.0] + * 1.0.35 10 10035 10.so.0.35[.0] + * 1.2.29 13 10229 12.so.0.29[.0] + * 1.0.37 10 10037 10.so.0.37[.0] + * 1.2.30beta01-04 13 10230 12.so.0.30[.0] + * 1.0.38rc01-08 10 10038 10.so.0.38[.0] + * 1.2.30rc01-08 13 10230 12.so.0.30[.0] + * 1.0.38 10 10038 10.so.0.38[.0] + * 1.2.30 13 10230 12.so.0.30[.0] + * 1.0.39rc01-03 10 10039 10.so.0.39[.0] + * 1.2.31rc01-03 13 10231 12.so.0.31[.0] + * 1.0.39 10 10039 10.so.0.39[.0] + * 1.2.31 13 10231 12.so.0.31[.0] + * 1.2.32beta01-02 13 10232 12.so.0.32[.0] + * 1.0.40rc01 10 10040 10.so.0.40[.0] + * 1.2.32rc01 13 10232 12.so.0.32[.0] + * 1.0.40 10 10040 10.so.0.40[.0] + * 1.2.32 13 10232 12.so.0.32[.0] + * 1.2.33beta01-02 13 10233 12.so.0.33[.0] + * 1.2.33rc01-02 13 10233 12.so.0.33[.0] + * 1.0.41rc01 10 10041 10.so.0.41[.0] + * 1.2.33 13 10233 12.so.0.33[.0] + * 1.0.41 10 10041 10.so.0.41[.0] + * 1.2.34beta01-07 13 10234 12.so.0.34[.0] + * 1.0.42rc01 10 10042 10.so.0.42[.0] + * 1.2.34rc01 13 10234 12.so.0.34[.0] + * 1.0.42 10 10042 10.so.0.42[.0] + * 1.2.34 13 10234 12.so.0.34[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only, deprecated */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE 0x0800 /* WRITE only */ +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ +#ifndef PNG_NO_WRITE_FILTER + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +# if !defined(PNG_1_0_X) +# if defined(PNG_MMX_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; +# endif + png_uint_32 asm_flags; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size, old_prev_row_size; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata; /* buffer for reading chunk data */ + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_34; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#ifndef _DEBUG +# define _DEBUG +#endif +#ifndef png_debug +#define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +#endif +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#ifndef png_debug +/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on + * non-ISO compilers */ +#ifdef __STDC__ +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif +#else /* __STDC __ */ +#ifndef png_debug +#define png_debug(l,m) \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); +#endif +#endif /* __STDC __ */ +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#if defined(PNG_INTERNAL) + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_pHYs_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Read the chunk header (length + type name) */ +PNG_EXTERN png_uint_32 png_read_chunk_header PNGARG((png_structp png_ptr)); + +/* Added at libpng version 1.2.34 */ +#if defined(PNG_cHRM_SUPPORTED) +PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/lib/png-1.2.34/include/pngconf.h b/lib/png-1.2.34/include/pngconf.h new file mode 100644 index 0000000..464458d --- /dev/null +++ b/lib/png-1.2.34/include/pngconf.h @@ -0,0 +1,1487 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.34 - December 18, 2008 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ + !defined(PNG_WARN_UNINITIALIZED_ROW) +# define PNG_WARN_UNINITIALIZED_ROW 1 +#endif +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifndef _BSD_SOURCE +# define _BSD_SOURCE +# endif +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.3.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if defined(__APPLE__) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + + +/* Added at libpng-1.2.34 and 1.4.0 */ +#ifndef PNG_STRING_NEWLINE +#define PNG_STRING_NEWLINE "\n" +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) + /* "time.h" functions are not supported on WindowsCE */ +# include +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof(x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof(x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/lib/png/include/png.h b/lib/png/include/png.h new file mode 100644 index 0000000..f46e97c --- /dev/null +++ b/lib/png/include/png.h @@ -0,0 +1,2699 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.4.4 - September 23, 2010 + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.4.4 - September 23, 2010: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 12.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 12.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-7 13 10210 12.so.0.10[.0] + * 1.2.10rc1-2 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.4.0beta1-5 14 10400 14.so.0.0[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.4.0beta7-8 14 10400 14.so.0.0[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.4.0beta9-14 14 10400 14.so.0.0[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.4.0beta15-36 14 10400 14.so.0.0[.0] + * 1.4.0beta37-87 14 10400 14.so.14.0[.0] + * 1.4.0rc01 14 10400 14.so.14.0[.0] + * 1.4.0beta88-109 14 10400 14.so.14.0[.0] + * 1.4.0rc02-08 14 10400 14.so.14.0[.0] + * 1.4.0 14 10400 14.so.14.0[.0] + * 1.4.1beta01-03 14 10401 14.so.14.1[.0] + * 1.4.1rc01 14 10401 14.so.14.1[.0] + * 1.4.1beta04-12 14 10401 14.so.14.1[.0] + * 1.4.1rc02-04 14 10401 14.so.14.1[.0] + * 1.4.1 14 10401 14.so.14.1[.0] + * 1.4.2beta01 14 10402 14.so.14.2[.0] + * 1.4.2rc02-06 14 10402 14.so.14.2[.0] + * 1.4.2 14 10402 14.so.14.2[.0] + * 1.4.3beta01-05 14 10403 14.so.14.3[.0] + * 1.4.3rc01-03 14 10403 14.so.14.3[.0] + * 1.4.3 14 10403 14.so.14.3[.0] + * 1.4.4beta01-08 14 10404 14.so.14.4[.0] + * 1.4.4rc01-06 14 10404 14.so.14.4[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, + png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the + * application must include this before png.h to obtain the definition + * of jmp_buf. + */ +typedef void (PNGAPI *png_longjmp_ptr) PNGARG((jmp_buf, int)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_alloc_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf PNG_DEPSTRUCT; /* used in png_error */ + png_longjmp_ptr longjmp_fn PNG_DEPSTRUCT;/* setjmp non-local goto + function. */ +#endif + png_error_ptr error_fn PNG_DEPSTRUCT; /* function for printing + errors and aborting */ + png_error_ptr warning_fn PNG_DEPSTRUCT; /* function for printing + warnings */ + png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for + error functions */ + png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing + output data */ + png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading + input data */ + png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct + for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn PNG_DEPSTRUCT; /* user read + transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn PNG_DEPSTRUCT; /* user write + transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr PNG_DEPSTRUCT; /* user supplied struct + for user transform */ + png_byte user_transform_depth PNG_DEPSTRUCT; /* bit depth of user + transformed pixels */ + png_byte user_transform_channels PNG_DEPSTRUCT; /* channels in user + transformed pixels */ +#endif +#endif + + png_uint_32 mode PNG_DEPSTRUCT; /* tells us where we are in + the PNG file */ + png_uint_32 flags PNG_DEPSTRUCT; /* flags indicating various + things to libpng */ + png_uint_32 transformations PNG_DEPSTRUCT; /* which transformations + to perform */ + + z_stream zstream PNG_DEPSTRUCT; /* pointer to decompression + structure (below) */ + png_bytep zbuf PNG_DEPSTRUCT; /* buffer for zlib */ + png_size_t zbuf_size PNG_DEPSTRUCT; /* size of zbuf */ + int zlib_level PNG_DEPSTRUCT; /* holds zlib compression level */ + int zlib_method PNG_DEPSTRUCT; /* holds zlib compression method */ + int zlib_window_bits PNG_DEPSTRUCT; /* holds zlib compression window + bits */ + int zlib_mem_level PNG_DEPSTRUCT; /* holds zlib compression memory + level */ + int zlib_strategy PNG_DEPSTRUCT; /* holds zlib compression + strategy */ + + png_uint_32 width PNG_DEPSTRUCT; /* width of image in pixels */ + png_uint_32 height PNG_DEPSTRUCT; /* height of image in pixels */ + png_uint_32 num_rows PNG_DEPSTRUCT; /* number of rows in current pass */ + png_uint_32 usr_width PNG_DEPSTRUCT; /* width of row at start of write */ + png_size_t rowbytes PNG_DEPSTRUCT; /* size of row in bytes */ +#if 0 /* Replaced with the following in libpng-1.4.1 */ + png_size_t irowbytes PNG_DEPSTRUCT; +#endif +/* Added in libpng-1.4.1 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk + * can occupy when decompressed. 0 means unlimited. + * We will change the typedef from png_size_t to png_alloc_size_t + * in libpng-1.6.0 + */ + png_alloc_size_t user_chunk_malloc_max PNG_DEPSTRUCT; +#endif + png_uint_32 iwidth PNG_DEPSTRUCT; /* width of current interlaced + row in pixels */ + png_uint_32 row_number PNG_DEPSTRUCT; /* current row in interlace pass */ + png_bytep prev_row PNG_DEPSTRUCT; /* buffer to save previous + (unfiltered) row */ + png_bytep row_buf PNG_DEPSTRUCT; /* buffer to save current + (unfiltered) row */ + png_bytep sub_row PNG_DEPSTRUCT; /* buffer to save "sub" row + when filtering */ + png_bytep up_row PNG_DEPSTRUCT; /* buffer to save "up" row + when filtering */ + png_bytep avg_row PNG_DEPSTRUCT; /* buffer to save "avg" row + when filtering */ + png_bytep paeth_row PNG_DEPSTRUCT; /* buffer to save "Paeth" row + when filtering */ + png_row_info row_info PNG_DEPSTRUCT; /* used for transformation + routines */ + + png_uint_32 idat_size PNG_DEPSTRUCT; /* current IDAT size for read */ + png_uint_32 crc PNG_DEPSTRUCT; /* current chunk CRC value */ + png_colorp palette PNG_DEPSTRUCT; /* palette from the input file */ + png_uint_16 num_palette PNG_DEPSTRUCT; /* number of color entries in + palette */ + png_uint_16 num_trans PNG_DEPSTRUCT; /* number of transparency values */ + png_byte chunk_name[5] PNG_DEPSTRUCT; /* null-terminated name of current + chunk */ + png_byte compression PNG_DEPSTRUCT; /* file compression type + (always 0) */ + png_byte filter PNG_DEPSTRUCT; /* file filter type (always 0) */ + png_byte interlaced PNG_DEPSTRUCT; /* PNG_INTERLACE_NONE, + PNG_INTERLACE_ADAM7 */ + png_byte pass PNG_DEPSTRUCT; /* current interlace pass (0 - 6) */ + png_byte do_filter PNG_DEPSTRUCT; /* row filter flags (see + PNG_FILTER_ below ) */ + png_byte color_type PNG_DEPSTRUCT; /* color type of file */ + png_byte bit_depth PNG_DEPSTRUCT; /* bit depth of file */ + png_byte usr_bit_depth PNG_DEPSTRUCT; /* bit depth of users row */ + png_byte pixel_depth PNG_DEPSTRUCT; /* number of bits per pixel */ + png_byte channels PNG_DEPSTRUCT; /* number of channels in file */ + png_byte usr_channels PNG_DEPSTRUCT; /* channels at start of write */ + png_byte sig_bytes PNG_DEPSTRUCT; /* magic bytes read/written from + start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + png_uint_16 filler PNG_DEPSTRUCT; /* filler bytes for pixel + expansion */ +#endif + +#ifdef PNG_bKGD_SUPPORTED + png_byte background_gamma_type PNG_DEPSTRUCT; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma PNG_DEPSTRUCT; +# endif + png_color_16 background PNG_DEPSTRUCT; /* background color in + screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1 PNG_DEPSTRUCT; /* background normalized + to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn PNG_DEPSTRUCT; /* Function for flushing + output */ + png_uint_32 flush_dist PNG_DEPSTRUCT; /* how many rows apart to flush, + 0 - no flush */ + png_uint_32 flush_rows PNG_DEPSTRUCT; /* number of rows written since + last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift PNG_DEPSTRUCT; /* number of "insignificant" bits + 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma PNG_DEPSTRUCT; /* file gamma value */ + float screen_gamma PNG_DEPSTRUCT; /* screen gamma value + (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table PNG_DEPSTRUCT; /* gamma table for 8-bit + depth files */ + png_bytep gamma_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_bytep gamma_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table PNG_DEPSTRUCT; /* gamma table for 16-bit + depth files */ + png_uint_16pp gamma_16_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to + screen */ + png_uint_16pp gamma_16_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit PNG_DEPSTRUCT; /* significant bits in each + available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift PNG_DEPSTRUCT; /* shift for significant bit + tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans_alpha PNG_DEPSTRUCT; /* alpha values for + paletted files */ + png_color_16 trans_color PNG_DEPSTRUCT; /* transparent color for + non-paletted files */ +#endif + + png_read_status_ptr read_row_fn PNG_DEPSTRUCT; /* called after each + row is decoded */ + png_write_status_ptr write_row_fn PNG_DEPSTRUCT; /* called after each + row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn PNG_DEPSTRUCT; /* called after header + data fully read */ + png_progressive_row_ptr row_fn PNG_DEPSTRUCT; /* called after each + prog. row is decoded */ + png_progressive_end_ptr end_fn PNG_DEPSTRUCT; /* called after image + is complete */ + png_bytep save_buffer_ptr PNG_DEPSTRUCT; /* current location in + save_buffer */ + png_bytep save_buffer PNG_DEPSTRUCT; /* buffer for previously + read data */ + png_bytep current_buffer_ptr PNG_DEPSTRUCT; /* current location in + current_buffer */ + png_bytep current_buffer PNG_DEPSTRUCT; /* buffer for recently + used data */ + png_uint_32 push_length PNG_DEPSTRUCT; /* size of current input + chunk */ + png_uint_32 skip_length PNG_DEPSTRUCT; /* bytes to skip in + input data */ + png_size_t save_buffer_size PNG_DEPSTRUCT; /* amount of data now + in save_buffer */ + png_size_t save_buffer_max PNG_DEPSTRUCT; /* total size of + save_buffer */ + png_size_t buffer_size PNG_DEPSTRUCT; /* total amount of + available input data */ + png_size_t current_buffer_size PNG_DEPSTRUCT; /* amount of data now + in current_buffer */ + int process_mode PNG_DEPSTRUCT; /* what push library + is currently doing */ + int cur_palette PNG_DEPSTRUCT; /* current push library + palette index */ + +# ifdef PNG_TEXT_SUPPORTED + png_size_t current_text_size PNG_DEPSTRUCT; /* current size of + text input data */ + png_size_t current_text_left PNG_DEPSTRUCT; /* how much text left + to read in input */ + png_charp current_text PNG_DEPSTRUCT; /* current text chunk + buffer */ + png_charp current_text_ptr PNG_DEPSTRUCT; /* current location + in current_text */ +# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* For the Borland special 64K segment handler */ + png_bytepp offset_table_ptr PNG_DEPSTRUCT; + png_bytep offset_table PNG_DEPSTRUCT; + png_uint_16 offset_table_number PNG_DEPSTRUCT; + png_uint_16 offset_table_count PNG_DEPSTRUCT; + png_uint_16 offset_table_count_free PNG_DEPSTRUCT; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup PNG_DEPSTRUCT; /* lookup table for quantizing */ + png_bytep quantize_index PNG_DEPSTRUCT; /* index translation for palette + files */ +#endif + +#if defined(PNG_READ_QUANTIZE_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist PNG_DEPSTRUCT; /* histogram */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method PNG_DEPSTRUCT; /* heuristic for row + filter selection */ + png_byte num_prev_filters PNG_DEPSTRUCT; /* number of weights + for previous rows */ + png_bytep prev_filters PNG_DEPSTRUCT; /* filter type(s) of + previous row(s) */ + png_uint_16p filter_weights PNG_DEPSTRUCT; /* weight(s) for previous + line(s) */ + png_uint_16p inv_filter_weights PNG_DEPSTRUCT; /* 1/weight(s) for + previous line(s) */ + png_uint_16p filter_costs PNG_DEPSTRUCT; /* relative filter + calculation cost */ + png_uint_16p inv_filter_costs PNG_DEPSTRUCT; /* 1/relative filter + calculation cost */ +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_charp time_buffer PNG_DEPSTRUCT; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + + png_uint_32 free_me PNG_DEPSTRUCT; /* flags items libpng is + responsible for freeing */ + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr PNG_DEPSTRUCT; + png_user_chunk_ptr read_user_chunk_fn PNG_DEPSTRUCT; /* user read + chunk handler */ +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int num_chunk_list PNG_DEPSTRUCT; + png_bytep chunk_list PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status PNG_DEPSTRUCT; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_green_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_blue_coeff PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ + png_uint_32 mng_features_permitted PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.0 */ + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr PNG_DEPSTRUCT; /* user supplied struct for + mem functions */ + png_malloc_ptr malloc_fn PNG_DEPSTRUCT; /* function for + allocating memory */ + png_free_ptr free_fn PNG_DEPSTRUCT; /* function for + freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf PNG_DEPSTRUCT; /* buffer to save current + (unfiltered) row */ + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep quantize_sort PNG_DEPSTRUCT; /* working sort array */ + png_bytep index_to_palette PNG_DEPSTRUCT; /* where the original + index currently is + in the palette */ + png_bytep palette_to_index PNG_DEPSTRUCT; /* which original index + points to this + palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type PNG_DEPSTRUCT; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max PNG_DEPSTRUCT; + png_uint_32 user_height_max PNG_DEPSTRUCT; + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; + png_uint_32 old_prev_row_size PNG_DEPSTRUCT; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ + +#ifdef PNG_IO_STATE_SUPPORTED +/* New member added in libpng-1.4.0 */ + png_uint_32 io_state PNG_DEPSTRUCT; +#endif +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_4_4; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig,n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +PNG_EXPORT(png_size_t,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); + +PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(jmp_buf*, png_set_longjmp_fn) + PNGARG((png_structp png_ptr, png_longjmp_ptr longjmp_fn, size_t + jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, sizeof (jmp_buf))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(void,png_write_sig) PNGARG((png_structp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)) PNG_ALLOCATED; + +PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. Prior to libpng-1.4.2, this was png_set_dither(). + */ +PNG_EXPORT(void,png_set_quantize) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_quantize)); +#endif +/* This migration aid will be removed from libpng-1.5.0 */ +#define png_set_dither png_set_quantize + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* Write a few rows of image data */ +PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, + png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* Function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; +/* Added at libpng version 1.4.0 */ +PNG_EXPORT(png_voidp,png_calloc) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; + +/* Added at libpng version 1.2.4 */ +PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; +PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)) PNG_NORETURN; +#endif + +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(void,png_benign_error) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Same, chunk name is prepended to message. */ +PNG_EXPORT(void,png_chunk_benign_error) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +PNG_EXPORT(void,png_set_benign_errors) PNGARG((png_structp + png_ptr, int allowed)); +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(png_size_t,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans_alpha, int num_trans, + png_color_16p trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(void,png_set_chunk_cache_max) PNGARG((png_structp + png_ptr, png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(png_uint_32,png_get_chunk_cache_max) + PNGARG((png_structp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(void,png_set_chunk_malloc_max) PNGARG((png_structp + png_ptr, png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(png_alloc_size_t,png_get_chunk_malloc_max) + PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXPORT(png_uint_32,png_get_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(png_uint_32,png_get_x_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(png_uint_32,png_get_y_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(float,png_get_x_offset_inches) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(float,png_get_y_offset_inches) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pHYs_dpi) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_io_state) PNGARG((png_structp png_ptr)); + +PNG_EXPORT(png_bytep,png_get_io_chunk_name) + PNGARG((png_structp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +#define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +#define PNG_IO_READING 0x0001 /* currently reading */ +#define PNG_IO_WRITING 0x0002 /* currently writing */ +#define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +#define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +#define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +#define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +#define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +#define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project + * defs + */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L \ + - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define png_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) +# define png_get_uint_16(buf) \ + (((png_uint_32)(*(buf)) << 8) + \ + ((png_uint_32)(*((buf) + 1)))) +# define png_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffff)+1)) \ + : (png_int_32)png_get_uint_32(buf))) +#else +PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +#ifdef PNG_GET_INT_32_SUPPORTED +PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif +#endif +PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/lib/png/include/pngconf.h b/lib/png/include/pngconf.h new file mode 100644 index 0000000..7d17802 --- /dev/null +++ b/lib/png/include/pngconf.h @@ -0,0 +1,1540 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.4.4 - September 23, 2010 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_NO_LIMITS_H +# include +#endif + +/* Added at libpng-1.2.9 */ + +/* config.h is created by and PNG_CONFIGURE_LIBPNG is set by the "configure" + * script. + */ +#ifdef PNG_CONFIGURE_LIBPNG +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +#endif + +/* + * Added at libpng-1.2.8 + * + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# include "pngusr.h" +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#endif + +/* + * If you create a private DLL you should define in "pngusr.h" the following: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng14gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +# ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +# endif + +# ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +# endif +#endif /* __STDC__ */ + +/* End of material added to libpng-1.2.8 */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled in 1.4.0. */ +#ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +#else +# ifndef PNG_BENIGN_ERRORS_SUPPORTED +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_WARNINGS) && !defined(PNG_WARNINGS_SUPPORTED) +# define PNG_WARNINGS_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_ERROR_TEXT) && !defined(PNG_ERROR_TEXT_SUPPORTED) +# define PNG_ERROR_TEXT_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_CHECK_cHRM) && !defined(PNG_CHECK_cHRM_SUPPORTED) +# define PNG_CHECK_cHRM_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_ALIGNED_MEMORY) && !defined(PNG_ALIGNED_MEMORY_SUPPORTED) +# define PNG_ALIGNED_MEMORY_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#ifndef PNG_NO_MNG_FEATURES +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +/* Added at libpng version 1.4.0 */ +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* Added at libpng-1.4.0beta49 for testing (this test is no longer used + in libpng and png_calloc() is always present) + */ +#define PNG_CALLOC_SUPPORTED + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO. + */ +#ifdef __CYGWIN__ +# ifdef ALL_STATIC +# ifdef PNG_BUILD_DLL +# undef PNG_BUILD_DLL +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# ifndef PNG_STATIC +# define PNG_STATIC +# endif +# else +# ifdef PNG_BUILD_DLL +# ifdef PNG_STATIC +# undef PNG_STATIC +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# else +# ifdef PNG_STATIC +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# else +# ifndef PNG_USE_DLL +# define PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#ifdef _WIN32_WCE +# define PNG_NO_CONSOLE_IO +# define PNG_NO_STDIO +# define PNG_NO_TIME_RFC1123 +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#if !defined(PNG_NO_STDIO) && !defined(PNG_STDIO_SUPPORTED) +# define PNG_STDIO_SUPPORTED +#endif + +#ifdef PNG_BUILD_DLL +# if !defined(PNG_CONSOLE_IO_SUPPORTED) && !defined(PNG_NO_CONSOLE_IO) +# define PNG_NO_CONSOLE_IO +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# include +# endif + +#if !(defined PNG_NO_CONSOLE_IO) && !defined(PNG_CONSOLE_IO_SUPPORTED) +# define PNG_CONSOLE_IO_SUPPORTED +#endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* Enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) +# include +#endif + +/* PNG_SETJMP_NOT_SUPPORTED and PNG_NO_SETJMP_SUPPORTED are deprecated. */ +#if !defined(PNG_NO_SETJMP) && \ + !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + * + * You can bypass this test if you know that your application uses exactly + * the same setjmp.h that was included when libpng was built. Only define + * PNG_SKIP_SETJMP_CHECK while building your application, prior to the + * application's '#include "png.h"'. Don't define PNG_SKIP_SETJMP_CHECK + * while building a separate libpng library for general use. + */ + +# ifndef PNG_SKIP_SETJMP_CHECK +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ in libpng already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ +# endif /* PNG_SKIP_SETJMP_CHECK */ + + /* Include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifdef _BSD_SOURCE +# undef _BSD_SOURCE +# endif +# define _BSD_SOURCE +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ + +/* This controls how fine the quantizing gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with quantizing quality can decrease some or all of these. + */ + +/* Prior to libpng-1.4.2, these were PNG_DITHER_*_BITS + * These migration aids will be removed from libpng-1.5.0. + */ +#ifdef PNG_DITHER_RED_BITS +# define PNG_QUANTIZE_RED_BITS PNG_DITHER_RED_BITS +#endif +#ifdef PNG_DITHER_GREEN_BITS +# define PNG_QUANTIZE_GREEN_BITS PNG_DITHER_GREEN_BITS +#endif +#ifdef PNG_DITHER_BLUE_BITS +# define PNG_QUANTIZE_BLUE_BITS PNG_DITHER_BLUE_BITS +#endif + +#ifndef PNG_QUANTIZE_RED_BITS +# define PNG_QUANTIZE_RED_BITS 5 +#endif +#ifndef PNG_QUANTIZE_GREEN_BITS +# define PNG_QUANTIZE_GREEN_BITS 5 +#endif +#ifndef PNG_QUANTIZE_BLUE_BITS +# define PNG_QUANTIZE_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST. + */ + +#ifndef PNG_CONST +# ifndef PNG_NO_CONST +# define PNG_CONST const +# else +# define PNG_CONST +# endif +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to an #undef, or pass in PNG_NO_feature and that part of the library + * will not be compiled. + + * If your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with PNG_NO_READ|WRITE_TRANSFORMS on the compile line, + * then pick and choose which ones to define without having to edit this + * file. It is safe to use the PNG_NO_READ|WRITE_TRANSFORMS + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency; PNG_*_TRANSFORMS_NOT_SUPPORTED deprecated in + * 1.4.0) + */ + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifdef PNG_READ_SUPPORTED + +/* PNG_READ_TRANSFORMS_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_QUANTIZE + /* Prior to libpng-1.4.0 this was PNG_READ_DITHER_SUPPORTED */ +# ifndef PNG_NO_READ_DITHER /* This migration aid will be removed */ +# define PNG_READ_QUANTIZE_SUPPORTED +# endif +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +/* PNG_PROGRESSIVE_READ_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following define: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +/* PNG_NO_SEQUENTIAL_READ_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_SEQUENTIAL_READ) && \ + !defined(PNG_SEQUENTIAL_READ_SUPPORTED) && \ + !defined(PNG_NO_SEQUENTIAL_READ_SUPPORTED) +# define PNG_SEQUENTIAL_READ_SUPPORTED +#endif + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if !defined(PNG_NO_GET_INT_32) || defined(PNG_READ_oFFS_SUPPORTED) || \ + defined(PNG_READ_pCAL_SUPPORTED) +# ifndef PNG_GET_INT_32_SUPPORTED +# define PNG_GET_INT_32_SUPPORTED +# endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#ifdef PNG_WRITE_SUPPORTED + +/* PNG_WRITE_TRANSFORMS_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* This is not required for PNG-compliant encoders, but can cause + * trouble if left undefined + */ +# define PNG_WRITE_INTERLACING_SUPPORTED +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if !defined(PNG_NO_SAVE_INT_32) || defined(PNG_WRITE_oFFS_SUPPORTED) || \ + defined(PNG_WRITE_pCAL_SUPPORTED) +# ifndef PNG_SAVE_INT_32_SUPPORTED +# define PNG_SAVE_INT_32_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#define PNG_NO_ERROR_NUMBERS + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#if defined(PNG_STDIO_SUPPORTED) && !defined(PNG_TIME_RFC1123_SUPPORTED) +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* Added at libpng-1.2.0 */ +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif + +/* Added at libpng-1.2.6 */ +#ifndef PNG_NO_SET_USER_LIMITS +# ifndef PNG_SET_USER_LIMITS_SUPPORTED +# define PNG_SET_USER_LIMITS_SUPPORTED +# endif + /* Feature added at libpng-1.4.0, this flag added at 1.4.1 */ +# ifndef PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED +# define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED +# endif + /* Feature added at libpng-1.4.1, this flag added at 1.4.1 */ +# ifndef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED +# define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED +# endif +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_USER_LIMITS +# define PNG_USER_LIMITS_SUPPORTED +# endif +#endif + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGs no matter + * how large, set these two limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* Added at libpng-1.2.43. To accept all valid PNGs no matter + * how large, set these two limits to 0. + */ +#ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 0 +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +#endif + +/* Added at libpng-1.4.0 */ +#if !defined(PNG_NO_IO_STATE) && !defined(PNG_IO_STATE_SUPPORTED) +# define PNG_IO_STATE_SUPPORTED +#endif + +#ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +#endif +#ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +#endif +#ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +#endif +#ifndef PNG_STRING_NEWLINE +#define PNG_STRING_NEWLINE "\n" +#endif + +/* These are currently experimental features, define them if you want */ + +/* Very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# define PNG_USE_READ_MACROS +#endif + +/* Buggy compilers (e.g., gcc 2.7.2.2) need PNG_NO_POINTER_INDEXING */ + +#if !defined(PNG_NO_POINTER_INDEXING) && \ + !defined(PNG_POINTER_INDEXING_SUPPORTED) +# define PNG_POINTER_INDEXING_SUPPORTED +#endif + + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.4.0. + */ + +/* PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +/* PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif + +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_READ_USER_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_USER_CHUNKS +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_READ_USER_CHUNKS_SUPPORTED +# endif +# ifndef PNG_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_NO_CONVERT_tIME +# ifndef _WIN32_WCE +/* The "tm" structure is not supported on WindowsCE */ +# ifndef PNG_CONVERT_tIME_SUPPORTED +# define PNG_CONVERT_tIME_SUPPORTED +# endif +# endif +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_WRITE_FILTER +# ifndef PNG_WRITE_FILTER_SUPPORTED +# define PNG_WRITE_FILTER_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* Need the time information for converting tIME chunks */ +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* "time.h" functions are not supported on WindowsCE */ +# include +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. + */ + +#if defined(INT_MAX) && (INT_MAX > 0x7ffffffeL) +typedef unsigned int png_uint_32; +typedef int png_int_32; +#else +typedef unsigned long png_uint_32; +typedef long png_int_32; +#endif +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +#ifdef PNG_NO_SIZE_T + typedef unsigned int png_size_t; +#else + typedef size_t png_size_t; +#endif +#define png_sizeof(x) (sizeof (x)) + +/* The following is needed for medium model support. It cannot be in the + * pngpriv.h header. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + * defines FAR. (SJT) + */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#ifdef FAR +# ifdef M_I86MM +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +/* Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#ifdef __CYGWIN__ +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef __WATCOMC__ +# ifndef PNGAPI +# define PNGAPI +# endif +#endif + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# ifndef PNG_IMPEXP + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#define PNG_USE_LOCAL_ARRAYS /* Not used in libpng, defined for legacy apps */ + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. + */ +# ifdef __GNUC__ +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif + + /* This specifically protects structure members that should only be + * accessed from within the library, therefore should be empty during + * a library build. + */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif /* PNG_PRIVATE */ +# endif /* __GNUC__ */ +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif + +/* Users may want to use these so they are not private. Any library + * functions that are passed far data must be model-independent. + */ + +/* memory model/platform independent fns */ +#ifndef PNG_ABORT +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) +# define PNG_ABORT() ExitProcess(0) +# else +# define PNG_ABORT() abort() +# endif +#endif + +#ifdef USE_FAR_KEYWORD +/* Use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_strcpy _fstrcpy +# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +# define png_sprintf sprintf +#else +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) +# /* Favor Windows over C runtime fns */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy lstrcpyA +# define png_strncpy lstrcpynA +# define png_strlen lstrlenA +# define png_memcmp memcmp +# define png_memcpy CopyMemory +# define png_memset memset +# define png_sprintf wsprintfA +# else +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy strcpy +# define png_strncpy strncpy /* Added to v 1.2.6 */ +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +# define png_sprintf sprintf +# endif +#endif + +#ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +#else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). + */ +# define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, + * and no smaller than png_uint_32. Casts from png_size_t or png_uint_32 + * to png_alloc_size_t are not necessary; in fact, it is recommended + * not to use them at all so that the compiler can complain when something + * turns out to be problematic. + * Casts in the other direction (from png_alloc_size_t to png_size_t or + * png_uint_32) should be explicitly applied; however, we do not expect + * to encounter practical situations that require such conversions. + */ +#if defined(__TURBOC__) && !defined(__FLAT__) + typedef unsigned long png_alloc_size_t; +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + typedef unsigned long png_alloc_size_t; +# else + /* This is an attempt to detect an old Windows system where (int) is + * actually 16 bits, in that case png_malloc must have an argument with a + * bigger size to accomodate the requirements of the library. + */ +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) && \ + (!defined(INT_MAX) || INT_MAX <= 0x7ffffffeL) + typedef DWORD png_alloc_size_t; +# else + typedef png_size_t png_alloc_size_t; +# endif +# endif +#endif +/* End of memory model/platform independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/lib/png/src/Makefile b/lib/png/src/Makefile new file mode 100644 index 0000000..1801c92 --- /dev/null +++ b/lib/png/src/Makefile @@ -0,0 +1,52 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +include $(DEVKITPPC)/wii_rules + +#PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +#CC := $(PREFIX)gcc +#AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii +DEPSDIR := . + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../include +DEST_LIB := ../lib$(DKV) + +INCLUDE := -I../../zlib/include -I$(LIBOGC_INC) +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -Os -Wall $(MACHDEP) $(INCLUDE) + +LIB := png +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +DEPENDS := $(OFILES:.o=.d) +ARC := lib$(LIB).a +HDR := $(LIB).h $(LIB)conf.h + +#all : $(OFILES) $(ARC) +# $(AR) -r $(ARC) $(OFILES) + +all : $(ARC) + +$(ARC) : $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) $(DEPENDS) + +install : + mkdir -p $(DEST_LIB) $(DEST_INC) + cp -f $(ARC) $(DEST_LIB)/ + cp -f $(HDR) $(DEST_INC)/ + +#%.o : %.c +# $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/png/src/Makefile-orig b/lib/png/src/Makefile-orig new file mode 100644 index 0000000..1d240ae --- /dev/null +++ b/lib/png/src/Makefile-orig @@ -0,0 +1,40 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +CC := $(PREFIX)gcc +AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii + +INCLUDE := -I../zlib -I$(LIBOGC_INC) +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -O2 -Wall $(MACHDEP) $(INCLUDE) + +LIB := png +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +ARC := lib$(LIB).a +HDR := $(LIB).h $(LIB)conf.h + +all : $(OFILES) + $(AR) -r $(ARC) $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) + +install : + mkdir -p $(LIBOGC_LIB) $(LIBOGC_INC) + cp -f $(ARC) $(LIBOGC_LIB)/ + cp -f $(HDR) $(LIBOGC_INC)/ + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/png/src/png.c b/lib/png/src/png.c new file mode 100644 index 0000000..4b02764 --- /dev/null +++ b/lib/png/src/png.c @@ -0,0 +1,918 @@ + +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.4.2 [May 6, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_NO_EXTERN +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#include "pngpriv.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_4_4 Your_png_h_is_not_version_1_4_4; + +/* Version information for C files. This had better match the version + * string defined in png.h. + */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature"); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +voidpf /* PRIVATE */ +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=(png_structp)png_ptr; + png_uint_32 save_flags=p->flags; + png_alloc_size_t num_bytes; + + if (png_ptr == NULL) + return (NULL); + if (items > PNG_UINT_32_MAX/size) + { + png_warning (p, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_alloc_size_t)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + + return ((voidpf)ptr); +} + +/* Function to free memory for zlib */ +void /* PRIVATE */ +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3"); + + if (info_ptr == NULL) + return; + + if (png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* Set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof(png_info)); +} + +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if (freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer"); +} + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ + if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } + } +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ + if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->trans_alpha); + info_ptr->trans_alpha = NULL; + info_ptr->valid &= ~PNG_INFO_tRNS; + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ + if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) + { +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ + if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i] = NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any iCCP entry */ + if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ + if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if (info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_chunk.data) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + + if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if (info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } + } +#endif + +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ + if ((mask & PNG_FREE_HIST) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; + } +#endif + + /* Free any PLTE entry that was internally allocated */ + if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) + { + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ + if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) + { + if (info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row] = NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + + if (num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; + png_ptr->num_chunk_list = 0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr == NULL) + return (NULL); + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +#ifdef __STDC__ + return ((png_charp) PNG_STRING_NEWLINE \ + "libpng version 1.4.4 - September 23, 2010" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2010 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE); +#else + return ((png_charp) "libpng version 1.4.4 - September 23, 2010\ + Copyright (c) 1998-2010 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."); +#endif +#endif +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return ((png_charp) PNG_HEADER_VERSION_STRING +#ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +#endif + PNG_STRING_NEWLINE); +#else + return ((png_charp) PNG_HEADER_VERSION_STRING); +#endif +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) + return 0; + p = png_ptr->chunk_list + png_ptr->num_chunk_list*5 - 5; + for (i = png_ptr->num_chunk_list; i; i--, p -= 5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p + 4)); + return 0; +} +#endif +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +#ifdef PNG_READ_SUPPORTED +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + if (png_ptr == NULL) + return Z_STREAM_ERROR; + return (inflateReset(&png_ptr->zstream)); +} +#endif /* PNG_READ_SUPPORTED */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED + +/* + * Multiply two 32-bit numbers, V1 and V2, using 32-bit + * arithmetic, to produce a 64 bit result in the HI/LO words. + * + * A B + * x C D + * ------ + * AD || BD + * AC || CB || 0 + * + * where A and B are the high and low 16-bit words of V1, + * C and D are the 16-bit words of V2, AD is the product of + * A and D, and X || Y is (X << 16) + Y. +*/ + +void /* PRIVATE */ +png_64bit_product (long v1, long v2, unsigned long *hi_product, + unsigned long *lo_product) +{ + int a, b, c, d; + long lo, hi, x, y; + + a = (v1 >> 16) & 0xffff; + b = v1 & 0xffff; + c = (v2 >> 16) & 0xffff; + d = v2 & 0xffff; + + lo = b * d; /* BD */ + x = a * d + c * b; /* AD + CB */ + y = ((lo >> 16) & 0xffff) + x; + + lo = (lo & 0xffff) | ((y & 0xffff) << 16); + hi = (y >> 16) & 0xffff; + + hi += a * c; /* AC */ + + *hi_product = (unsigned long)hi; + *lo_product = (unsigned long)lo; +} + +int /* PRIVATE */ +png_check_cHRM_fixed(png_structp png_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + int ret = 1; + unsigned long xy_hi,xy_lo,yx_hi,yx_lo; + + png_debug(1, "in function png_check_cHRM_fixed"); + + if (png_ptr == NULL) + return 0; + + if (white_x < 0 || white_y <= 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + ret = 0; + } + if (white_x > (png_fixed_point) PNG_UINT_31_MAX || + white_y > (png_fixed_point) PNG_UINT_31_MAX || + red_x > (png_fixed_point) PNG_UINT_31_MAX || + red_y > (png_fixed_point) PNG_UINT_31_MAX || + green_x > (png_fixed_point) PNG_UINT_31_MAX || + green_y > (png_fixed_point) PNG_UINT_31_MAX || + blue_x > (png_fixed_point) PNG_UINT_31_MAX || + blue_y > (png_fixed_point) PNG_UINT_31_MAX ) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + ret = 0; + } + if (white_x > 100000L - white_y) + { + png_warning(png_ptr, "Invalid cHRM white point"); + ret = 0; + } + if (red_x > 100000L - red_y) + { + png_warning(png_ptr, "Invalid cHRM red point"); + ret = 0; + } + if (green_x > 100000L - green_y) + { + png_warning(png_ptr, "Invalid cHRM green point"); + ret = 0; + } + if (blue_x > 100000L - blue_y) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + ret = 0; + } + + png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo); + png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo); + + if (xy_hi == yx_hi && xy_lo == yx_lo) + { + png_warning(png_ptr, + "Ignoring attempt to set cHRM RGB triangle with zero area"); + ret = 0; + } + + return ret; +} +#endif /* PNG_CHECK_cHRM_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +void /* PRIVATE */ +png_check_IHDR(png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX) +#else + if (width > PNG_USER_WIDTH_MAX) +#endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX) +#else + if (height > PNG_USER_HEIGHT_MAX) +#endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if ( height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && + png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +#else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +#endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/lib/png/src/png.d b/lib/png/src/png.d new file mode 100644 index 0000000..843abe4 --- /dev/null +++ b/lib/png/src/png.d @@ -0,0 +1,12 @@ +png.o: png.c png.h ../../zlib/include/zlib.h ../../zlib/include/zconf.h \ + pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/png.h b/lib/png/src/png.h new file mode 100644 index 0000000..f46e97c --- /dev/null +++ b/lib/png/src/png.h @@ -0,0 +1,2699 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.4.4 - September 23, 2010 + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.4.4 - September 23, 2010: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 12.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 12.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-7 13 10210 12.so.0.10[.0] + * 1.2.10rc1-2 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.4.0beta1-5 14 10400 14.so.0.0[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.4.0beta7-8 14 10400 14.so.0.0[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.4.0beta9-14 14 10400 14.so.0.0[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.4.0beta15-36 14 10400 14.so.0.0[.0] + * 1.4.0beta37-87 14 10400 14.so.14.0[.0] + * 1.4.0rc01 14 10400 14.so.14.0[.0] + * 1.4.0beta88-109 14 10400 14.so.14.0[.0] + * 1.4.0rc02-08 14 10400 14.so.14.0[.0] + * 1.4.0 14 10400 14.so.14.0[.0] + * 1.4.1beta01-03 14 10401 14.so.14.1[.0] + * 1.4.1rc01 14 10401 14.so.14.1[.0] + * 1.4.1beta04-12 14 10401 14.so.14.1[.0] + * 1.4.1rc02-04 14 10401 14.so.14.1[.0] + * 1.4.1 14 10401 14.so.14.1[.0] + * 1.4.2beta01 14 10402 14.so.14.2[.0] + * 1.4.2rc02-06 14 10402 14.so.14.2[.0] + * 1.4.2 14 10402 14.so.14.2[.0] + * 1.4.3beta01-05 14 10403 14.so.14.3[.0] + * 1.4.3rc01-03 14 10403 14.so.14.3[.0] + * 1.4.3 14 10403 14.so.14.3[.0] + * 1.4.4beta01-08 14 10404 14.so.14.4[.0] + * 1.4.4rc01-06 14 10404 14.so.14.4[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, + png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the + * application must include this before png.h to obtain the definition + * of jmp_buf. + */ +typedef void (PNGAPI *png_longjmp_ptr) PNGARG((jmp_buf, int)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_alloc_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf PNG_DEPSTRUCT; /* used in png_error */ + png_longjmp_ptr longjmp_fn PNG_DEPSTRUCT;/* setjmp non-local goto + function. */ +#endif + png_error_ptr error_fn PNG_DEPSTRUCT; /* function for printing + errors and aborting */ + png_error_ptr warning_fn PNG_DEPSTRUCT; /* function for printing + warnings */ + png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for + error functions */ + png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing + output data */ + png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading + input data */ + png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct + for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn PNG_DEPSTRUCT; /* user read + transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn PNG_DEPSTRUCT; /* user write + transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr PNG_DEPSTRUCT; /* user supplied struct + for user transform */ + png_byte user_transform_depth PNG_DEPSTRUCT; /* bit depth of user + transformed pixels */ + png_byte user_transform_channels PNG_DEPSTRUCT; /* channels in user + transformed pixels */ +#endif +#endif + + png_uint_32 mode PNG_DEPSTRUCT; /* tells us where we are in + the PNG file */ + png_uint_32 flags PNG_DEPSTRUCT; /* flags indicating various + things to libpng */ + png_uint_32 transformations PNG_DEPSTRUCT; /* which transformations + to perform */ + + z_stream zstream PNG_DEPSTRUCT; /* pointer to decompression + structure (below) */ + png_bytep zbuf PNG_DEPSTRUCT; /* buffer for zlib */ + png_size_t zbuf_size PNG_DEPSTRUCT; /* size of zbuf */ + int zlib_level PNG_DEPSTRUCT; /* holds zlib compression level */ + int zlib_method PNG_DEPSTRUCT; /* holds zlib compression method */ + int zlib_window_bits PNG_DEPSTRUCT; /* holds zlib compression window + bits */ + int zlib_mem_level PNG_DEPSTRUCT; /* holds zlib compression memory + level */ + int zlib_strategy PNG_DEPSTRUCT; /* holds zlib compression + strategy */ + + png_uint_32 width PNG_DEPSTRUCT; /* width of image in pixels */ + png_uint_32 height PNG_DEPSTRUCT; /* height of image in pixels */ + png_uint_32 num_rows PNG_DEPSTRUCT; /* number of rows in current pass */ + png_uint_32 usr_width PNG_DEPSTRUCT; /* width of row at start of write */ + png_size_t rowbytes PNG_DEPSTRUCT; /* size of row in bytes */ +#if 0 /* Replaced with the following in libpng-1.4.1 */ + png_size_t irowbytes PNG_DEPSTRUCT; +#endif +/* Added in libpng-1.4.1 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk + * can occupy when decompressed. 0 means unlimited. + * We will change the typedef from png_size_t to png_alloc_size_t + * in libpng-1.6.0 + */ + png_alloc_size_t user_chunk_malloc_max PNG_DEPSTRUCT; +#endif + png_uint_32 iwidth PNG_DEPSTRUCT; /* width of current interlaced + row in pixels */ + png_uint_32 row_number PNG_DEPSTRUCT; /* current row in interlace pass */ + png_bytep prev_row PNG_DEPSTRUCT; /* buffer to save previous + (unfiltered) row */ + png_bytep row_buf PNG_DEPSTRUCT; /* buffer to save current + (unfiltered) row */ + png_bytep sub_row PNG_DEPSTRUCT; /* buffer to save "sub" row + when filtering */ + png_bytep up_row PNG_DEPSTRUCT; /* buffer to save "up" row + when filtering */ + png_bytep avg_row PNG_DEPSTRUCT; /* buffer to save "avg" row + when filtering */ + png_bytep paeth_row PNG_DEPSTRUCT; /* buffer to save "Paeth" row + when filtering */ + png_row_info row_info PNG_DEPSTRUCT; /* used for transformation + routines */ + + png_uint_32 idat_size PNG_DEPSTRUCT; /* current IDAT size for read */ + png_uint_32 crc PNG_DEPSTRUCT; /* current chunk CRC value */ + png_colorp palette PNG_DEPSTRUCT; /* palette from the input file */ + png_uint_16 num_palette PNG_DEPSTRUCT; /* number of color entries in + palette */ + png_uint_16 num_trans PNG_DEPSTRUCT; /* number of transparency values */ + png_byte chunk_name[5] PNG_DEPSTRUCT; /* null-terminated name of current + chunk */ + png_byte compression PNG_DEPSTRUCT; /* file compression type + (always 0) */ + png_byte filter PNG_DEPSTRUCT; /* file filter type (always 0) */ + png_byte interlaced PNG_DEPSTRUCT; /* PNG_INTERLACE_NONE, + PNG_INTERLACE_ADAM7 */ + png_byte pass PNG_DEPSTRUCT; /* current interlace pass (0 - 6) */ + png_byte do_filter PNG_DEPSTRUCT; /* row filter flags (see + PNG_FILTER_ below ) */ + png_byte color_type PNG_DEPSTRUCT; /* color type of file */ + png_byte bit_depth PNG_DEPSTRUCT; /* bit depth of file */ + png_byte usr_bit_depth PNG_DEPSTRUCT; /* bit depth of users row */ + png_byte pixel_depth PNG_DEPSTRUCT; /* number of bits per pixel */ + png_byte channels PNG_DEPSTRUCT; /* number of channels in file */ + png_byte usr_channels PNG_DEPSTRUCT; /* channels at start of write */ + png_byte sig_bytes PNG_DEPSTRUCT; /* magic bytes read/written from + start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + png_uint_16 filler PNG_DEPSTRUCT; /* filler bytes for pixel + expansion */ +#endif + +#ifdef PNG_bKGD_SUPPORTED + png_byte background_gamma_type PNG_DEPSTRUCT; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma PNG_DEPSTRUCT; +# endif + png_color_16 background PNG_DEPSTRUCT; /* background color in + screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1 PNG_DEPSTRUCT; /* background normalized + to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn PNG_DEPSTRUCT; /* Function for flushing + output */ + png_uint_32 flush_dist PNG_DEPSTRUCT; /* how many rows apart to flush, + 0 - no flush */ + png_uint_32 flush_rows PNG_DEPSTRUCT; /* number of rows written since + last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift PNG_DEPSTRUCT; /* number of "insignificant" bits + 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma PNG_DEPSTRUCT; /* file gamma value */ + float screen_gamma PNG_DEPSTRUCT; /* screen gamma value + (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table PNG_DEPSTRUCT; /* gamma table for 8-bit + depth files */ + png_bytep gamma_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_bytep gamma_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table PNG_DEPSTRUCT; /* gamma table for 16-bit + depth files */ + png_uint_16pp gamma_16_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to + screen */ + png_uint_16pp gamma_16_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit PNG_DEPSTRUCT; /* significant bits in each + available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift PNG_DEPSTRUCT; /* shift for significant bit + tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans_alpha PNG_DEPSTRUCT; /* alpha values for + paletted files */ + png_color_16 trans_color PNG_DEPSTRUCT; /* transparent color for + non-paletted files */ +#endif + + png_read_status_ptr read_row_fn PNG_DEPSTRUCT; /* called after each + row is decoded */ + png_write_status_ptr write_row_fn PNG_DEPSTRUCT; /* called after each + row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn PNG_DEPSTRUCT; /* called after header + data fully read */ + png_progressive_row_ptr row_fn PNG_DEPSTRUCT; /* called after each + prog. row is decoded */ + png_progressive_end_ptr end_fn PNG_DEPSTRUCT; /* called after image + is complete */ + png_bytep save_buffer_ptr PNG_DEPSTRUCT; /* current location in + save_buffer */ + png_bytep save_buffer PNG_DEPSTRUCT; /* buffer for previously + read data */ + png_bytep current_buffer_ptr PNG_DEPSTRUCT; /* current location in + current_buffer */ + png_bytep current_buffer PNG_DEPSTRUCT; /* buffer for recently + used data */ + png_uint_32 push_length PNG_DEPSTRUCT; /* size of current input + chunk */ + png_uint_32 skip_length PNG_DEPSTRUCT; /* bytes to skip in + input data */ + png_size_t save_buffer_size PNG_DEPSTRUCT; /* amount of data now + in save_buffer */ + png_size_t save_buffer_max PNG_DEPSTRUCT; /* total size of + save_buffer */ + png_size_t buffer_size PNG_DEPSTRUCT; /* total amount of + available input data */ + png_size_t current_buffer_size PNG_DEPSTRUCT; /* amount of data now + in current_buffer */ + int process_mode PNG_DEPSTRUCT; /* what push library + is currently doing */ + int cur_palette PNG_DEPSTRUCT; /* current push library + palette index */ + +# ifdef PNG_TEXT_SUPPORTED + png_size_t current_text_size PNG_DEPSTRUCT; /* current size of + text input data */ + png_size_t current_text_left PNG_DEPSTRUCT; /* how much text left + to read in input */ + png_charp current_text PNG_DEPSTRUCT; /* current text chunk + buffer */ + png_charp current_text_ptr PNG_DEPSTRUCT; /* current location + in current_text */ +# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* For the Borland special 64K segment handler */ + png_bytepp offset_table_ptr PNG_DEPSTRUCT; + png_bytep offset_table PNG_DEPSTRUCT; + png_uint_16 offset_table_number PNG_DEPSTRUCT; + png_uint_16 offset_table_count PNG_DEPSTRUCT; + png_uint_16 offset_table_count_free PNG_DEPSTRUCT; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup PNG_DEPSTRUCT; /* lookup table for quantizing */ + png_bytep quantize_index PNG_DEPSTRUCT; /* index translation for palette + files */ +#endif + +#if defined(PNG_READ_QUANTIZE_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist PNG_DEPSTRUCT; /* histogram */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method PNG_DEPSTRUCT; /* heuristic for row + filter selection */ + png_byte num_prev_filters PNG_DEPSTRUCT; /* number of weights + for previous rows */ + png_bytep prev_filters PNG_DEPSTRUCT; /* filter type(s) of + previous row(s) */ + png_uint_16p filter_weights PNG_DEPSTRUCT; /* weight(s) for previous + line(s) */ + png_uint_16p inv_filter_weights PNG_DEPSTRUCT; /* 1/weight(s) for + previous line(s) */ + png_uint_16p filter_costs PNG_DEPSTRUCT; /* relative filter + calculation cost */ + png_uint_16p inv_filter_costs PNG_DEPSTRUCT; /* 1/relative filter + calculation cost */ +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_charp time_buffer PNG_DEPSTRUCT; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + + png_uint_32 free_me PNG_DEPSTRUCT; /* flags items libpng is + responsible for freeing */ + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr PNG_DEPSTRUCT; + png_user_chunk_ptr read_user_chunk_fn PNG_DEPSTRUCT; /* user read + chunk handler */ +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int num_chunk_list PNG_DEPSTRUCT; + png_bytep chunk_list PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status PNG_DEPSTRUCT; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_green_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_blue_coeff PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ + png_uint_32 mng_features_permitted PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.0 */ + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr PNG_DEPSTRUCT; /* user supplied struct for + mem functions */ + png_malloc_ptr malloc_fn PNG_DEPSTRUCT; /* function for + allocating memory */ + png_free_ptr free_fn PNG_DEPSTRUCT; /* function for + freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf PNG_DEPSTRUCT; /* buffer to save current + (unfiltered) row */ + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep quantize_sort PNG_DEPSTRUCT; /* working sort array */ + png_bytep index_to_palette PNG_DEPSTRUCT; /* where the original + index currently is + in the palette */ + png_bytep palette_to_index PNG_DEPSTRUCT; /* which original index + points to this + palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type PNG_DEPSTRUCT; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max PNG_DEPSTRUCT; + png_uint_32 user_height_max PNG_DEPSTRUCT; + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; + png_uint_32 old_prev_row_size PNG_DEPSTRUCT; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ + +#ifdef PNG_IO_STATE_SUPPORTED +/* New member added in libpng-1.4.0 */ + png_uint_32 io_state PNG_DEPSTRUCT; +#endif +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_4_4; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig,n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +PNG_EXPORT(png_size_t,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); + +PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(jmp_buf*, png_set_longjmp_fn) + PNGARG((png_structp png_ptr, png_longjmp_ptr longjmp_fn, size_t + jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, sizeof (jmp_buf))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(void,png_write_sig) PNGARG((png_structp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)) PNG_ALLOCATED; + +PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. Prior to libpng-1.4.2, this was png_set_dither(). + */ +PNG_EXPORT(void,png_set_quantize) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_quantize)); +#endif +/* This migration aid will be removed from libpng-1.5.0 */ +#define png_set_dither png_set_quantize + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* Write a few rows of image data */ +PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, + png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* Function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; +/* Added at libpng version 1.4.0 */ +PNG_EXPORT(png_voidp,png_calloc) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; + +/* Added at libpng version 1.2.4 */ +PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_alloc_size_t size)) PNG_ALLOCATED; +PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)) PNG_NORETURN; +#endif + +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(void,png_benign_error) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Same, chunk name is prepended to message. */ +PNG_EXPORT(void,png_chunk_benign_error) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +PNG_EXPORT(void,png_set_benign_errors) PNGARG((png_structp + png_ptr, int allowed)); +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(png_size_t,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans_alpha, int num_trans, + png_color_16p trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(void,png_set_chunk_cache_max) PNGARG((png_structp + png_ptr, png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(png_uint_32,png_get_chunk_cache_max) + PNGARG((png_structp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(void,png_set_chunk_malloc_max) PNGARG((png_structp + png_ptr, png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(png_alloc_size_t,png_get_chunk_malloc_max) + PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXPORT(png_uint_32,png_get_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(png_uint_32,png_get_x_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(png_uint_32,png_get_y_pixels_per_inch) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(float,png_get_x_offset_inches) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXPORT(float,png_get_y_offset_inches) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_pHYs_dpi) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(png_uint_32,png_get_io_state) PNGARG((png_structp png_ptr)); + +PNG_EXPORT(png_bytep,png_get_io_chunk_name) + PNGARG((png_structp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +#define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +#define PNG_IO_READING 0x0001 /* currently reading */ +#define PNG_IO_WRITING 0x0002 /* currently writing */ +#define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +#define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +#define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +#define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +#define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +#define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project + * defs + */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L \ + - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define png_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) +# define png_get_uint_16(buf) \ + (((png_uint_32)(*(buf)) << 8) + \ + ((png_uint_32)(*((buf) + 1)))) +# define png_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffff)+1)) \ + : (png_int_32)png_get_uint_32(buf))) +#else +PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +#ifdef PNG_GET_INT_32_SUPPORTED +PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif +#endif +PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/lib/png/src/pngconf.h b/lib/png/src/pngconf.h new file mode 100644 index 0000000..7d17802 --- /dev/null +++ b/lib/png/src/pngconf.h @@ -0,0 +1,1540 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.4.4 - September 23, 2010 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_NO_LIMITS_H +# include +#endif + +/* Added at libpng-1.2.9 */ + +/* config.h is created by and PNG_CONFIGURE_LIBPNG is set by the "configure" + * script. + */ +#ifdef PNG_CONFIGURE_LIBPNG +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +#endif + +/* + * Added at libpng-1.2.8 + * + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# include "pngusr.h" +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#endif + +/* + * If you create a private DLL you should define in "pngusr.h" the following: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng14gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +# ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +# endif + +# ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +# endif +#endif /* __STDC__ */ + +/* End of material added to libpng-1.2.8 */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled in 1.4.0. */ +#ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +#else +# ifndef PNG_BENIGN_ERRORS_SUPPORTED +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_WARNINGS) && !defined(PNG_WARNINGS_SUPPORTED) +# define PNG_WARNINGS_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_ERROR_TEXT) && !defined(PNG_ERROR_TEXT_SUPPORTED) +# define PNG_ERROR_TEXT_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_CHECK_cHRM) && !defined(PNG_CHECK_cHRM_SUPPORTED) +# define PNG_CHECK_cHRM_SUPPORTED +#endif + +/* Added at libpng version 1.4.0 */ +#if !defined(PNG_NO_ALIGNED_MEMORY) && !defined(PNG_ALIGNED_MEMORY_SUPPORTED) +# define PNG_ALIGNED_MEMORY_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#ifndef PNG_NO_MNG_FEATURES +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +/* Added at libpng version 1.4.0 */ +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* Added at libpng-1.4.0beta49 for testing (this test is no longer used + in libpng and png_calloc() is always present) + */ +#define PNG_CALLOC_SUPPORTED + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO. + */ +#ifdef __CYGWIN__ +# ifdef ALL_STATIC +# ifdef PNG_BUILD_DLL +# undef PNG_BUILD_DLL +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# ifndef PNG_STATIC +# define PNG_STATIC +# endif +# else +# ifdef PNG_BUILD_DLL +# ifdef PNG_STATIC +# undef PNG_STATIC +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# else +# ifdef PNG_STATIC +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# else +# ifndef PNG_USE_DLL +# define PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#ifdef _WIN32_WCE +# define PNG_NO_CONSOLE_IO +# define PNG_NO_STDIO +# define PNG_NO_TIME_RFC1123 +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#if !defined(PNG_NO_STDIO) && !defined(PNG_STDIO_SUPPORTED) +# define PNG_STDIO_SUPPORTED +#endif + +#ifdef PNG_BUILD_DLL +# if !defined(PNG_CONSOLE_IO_SUPPORTED) && !defined(PNG_NO_CONSOLE_IO) +# define PNG_NO_CONSOLE_IO +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# include +# endif + +#if !(defined PNG_NO_CONSOLE_IO) && !defined(PNG_CONSOLE_IO_SUPPORTED) +# define PNG_CONSOLE_IO_SUPPORTED +#endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* Enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) +# include +#endif + +/* PNG_SETJMP_NOT_SUPPORTED and PNG_NO_SETJMP_SUPPORTED are deprecated. */ +#if !defined(PNG_NO_SETJMP) && \ + !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + * + * You can bypass this test if you know that your application uses exactly + * the same setjmp.h that was included when libpng was built. Only define + * PNG_SKIP_SETJMP_CHECK while building your application, prior to the + * application's '#include "png.h"'. Don't define PNG_SKIP_SETJMP_CHECK + * while building a separate libpng library for general use. + */ + +# ifndef PNG_SKIP_SETJMP_CHECK +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ in libpng already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ +# endif /* PNG_SKIP_SETJMP_CHECK */ + + /* Include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifdef _BSD_SOURCE +# undef _BSD_SOURCE +# endif +# define _BSD_SOURCE +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ + +/* This controls how fine the quantizing gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with quantizing quality can decrease some or all of these. + */ + +/* Prior to libpng-1.4.2, these were PNG_DITHER_*_BITS + * These migration aids will be removed from libpng-1.5.0. + */ +#ifdef PNG_DITHER_RED_BITS +# define PNG_QUANTIZE_RED_BITS PNG_DITHER_RED_BITS +#endif +#ifdef PNG_DITHER_GREEN_BITS +# define PNG_QUANTIZE_GREEN_BITS PNG_DITHER_GREEN_BITS +#endif +#ifdef PNG_DITHER_BLUE_BITS +# define PNG_QUANTIZE_BLUE_BITS PNG_DITHER_BLUE_BITS +#endif + +#ifndef PNG_QUANTIZE_RED_BITS +# define PNG_QUANTIZE_RED_BITS 5 +#endif +#ifndef PNG_QUANTIZE_GREEN_BITS +# define PNG_QUANTIZE_GREEN_BITS 5 +#endif +#ifndef PNG_QUANTIZE_BLUE_BITS +# define PNG_QUANTIZE_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST. + */ + +#ifndef PNG_CONST +# ifndef PNG_NO_CONST +# define PNG_CONST const +# else +# define PNG_CONST +# endif +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to an #undef, or pass in PNG_NO_feature and that part of the library + * will not be compiled. + + * If your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with PNG_NO_READ|WRITE_TRANSFORMS on the compile line, + * then pick and choose which ones to define without having to edit this + * file. It is safe to use the PNG_NO_READ|WRITE_TRANSFORMS + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency; PNG_*_TRANSFORMS_NOT_SUPPORTED deprecated in + * 1.4.0) + */ + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifdef PNG_READ_SUPPORTED + +/* PNG_READ_TRANSFORMS_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_QUANTIZE + /* Prior to libpng-1.4.0 this was PNG_READ_DITHER_SUPPORTED */ +# ifndef PNG_NO_READ_DITHER /* This migration aid will be removed */ +# define PNG_READ_QUANTIZE_SUPPORTED +# endif +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +/* PNG_PROGRESSIVE_READ_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following define: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +/* PNG_NO_SEQUENTIAL_READ_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_SEQUENTIAL_READ) && \ + !defined(PNG_SEQUENTIAL_READ_SUPPORTED) && \ + !defined(PNG_NO_SEQUENTIAL_READ_SUPPORTED) +# define PNG_SEQUENTIAL_READ_SUPPORTED +#endif + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if !defined(PNG_NO_GET_INT_32) || defined(PNG_READ_oFFS_SUPPORTED) || \ + defined(PNG_READ_pCAL_SUPPORTED) +# ifndef PNG_GET_INT_32_SUPPORTED +# define PNG_GET_INT_32_SUPPORTED +# endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#ifdef PNG_WRITE_SUPPORTED + +/* PNG_WRITE_TRANSFORMS_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* This is not required for PNG-compliant encoders, but can cause + * trouble if left undefined + */ +# define PNG_WRITE_INTERLACING_SUPPORTED +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if !defined(PNG_NO_SAVE_INT_32) || defined(PNG_WRITE_oFFS_SUPPORTED) || \ + defined(PNG_WRITE_pCAL_SUPPORTED) +# ifndef PNG_SAVE_INT_32_SUPPORTED +# define PNG_SAVE_INT_32_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#define PNG_NO_ERROR_NUMBERS + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#if defined(PNG_STDIO_SUPPORTED) && !defined(PNG_TIME_RFC1123_SUPPORTED) +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* Added at libpng-1.2.0 */ +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif + +/* Added at libpng-1.2.6 */ +#ifndef PNG_NO_SET_USER_LIMITS +# ifndef PNG_SET_USER_LIMITS_SUPPORTED +# define PNG_SET_USER_LIMITS_SUPPORTED +# endif + /* Feature added at libpng-1.4.0, this flag added at 1.4.1 */ +# ifndef PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED +# define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED +# endif + /* Feature added at libpng-1.4.1, this flag added at 1.4.1 */ +# ifndef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED +# define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED +# endif +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_USER_LIMITS +# define PNG_USER_LIMITS_SUPPORTED +# endif +#endif + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGs no matter + * how large, set these two limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* Added at libpng-1.2.43. To accept all valid PNGs no matter + * how large, set these two limits to 0. + */ +#ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 0 +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +#endif + +/* Added at libpng-1.4.0 */ +#if !defined(PNG_NO_IO_STATE) && !defined(PNG_IO_STATE_SUPPORTED) +# define PNG_IO_STATE_SUPPORTED +#endif + +#ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +#endif +#ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +#endif +#ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +#endif +#ifndef PNG_STRING_NEWLINE +#define PNG_STRING_NEWLINE "\n" +#endif + +/* These are currently experimental features, define them if you want */ + +/* Very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# define PNG_USE_READ_MACROS +#endif + +/* Buggy compilers (e.g., gcc 2.7.2.2) need PNG_NO_POINTER_INDEXING */ + +#if !defined(PNG_NO_POINTER_INDEXING) && \ + !defined(PNG_POINTER_INDEXING_SUPPORTED) +# define PNG_POINTER_INDEXING_SUPPORTED +#endif + + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.4.0. + */ + +/* PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +/* PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif + +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_READ_USER_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_USER_CHUNKS +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_READ_USER_CHUNKS_SUPPORTED +# endif +# ifndef PNG_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_NO_CONVERT_tIME +# ifndef _WIN32_WCE +/* The "tm" structure is not supported on WindowsCE */ +# ifndef PNG_CONVERT_tIME_SUPPORTED +# define PNG_CONVERT_tIME_SUPPORTED +# endif +# endif +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_WRITE_FILTER +# ifndef PNG_WRITE_FILTER_SUPPORTED +# define PNG_WRITE_FILTER_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* Need the time information for converting tIME chunks */ +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* "time.h" functions are not supported on WindowsCE */ +# include +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. + */ + +#if defined(INT_MAX) && (INT_MAX > 0x7ffffffeL) +typedef unsigned int png_uint_32; +typedef int png_int_32; +#else +typedef unsigned long png_uint_32; +typedef long png_int_32; +#endif +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +#ifdef PNG_NO_SIZE_T + typedef unsigned int png_size_t; +#else + typedef size_t png_size_t; +#endif +#define png_sizeof(x) (sizeof (x)) + +/* The following is needed for medium model support. It cannot be in the + * pngpriv.h header. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + * defines FAR. (SJT) + */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#ifdef FAR +# ifdef M_I86MM +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +/* Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#ifdef __CYGWIN__ +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef __WATCOMC__ +# ifndef PNGAPI +# define PNGAPI +# endif +#endif + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# ifndef PNG_IMPEXP + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#define PNG_USE_LOCAL_ARRAYS /* Not used in libpng, defined for legacy apps */ + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. + */ +# ifdef __GNUC__ +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif + + /* This specifically protects structure members that should only be + * accessed from within the library, therefore should be empty during + * a library build. + */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif /* PNG_PRIVATE */ +# endif /* __GNUC__ */ +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif + +/* Users may want to use these so they are not private. Any library + * functions that are passed far data must be model-independent. + */ + +/* memory model/platform independent fns */ +#ifndef PNG_ABORT +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) +# define PNG_ABORT() ExitProcess(0) +# else +# define PNG_ABORT() abort() +# endif +#endif + +#ifdef USE_FAR_KEYWORD +/* Use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_strcpy _fstrcpy +# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +# define png_sprintf sprintf +#else +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) +# /* Favor Windows over C runtime fns */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy lstrcpyA +# define png_strncpy lstrcpynA +# define png_strlen lstrlenA +# define png_memcmp memcmp +# define png_memcpy CopyMemory +# define png_memset memset +# define png_sprintf wsprintfA +# else +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy strcpy +# define png_strncpy strncpy /* Added to v 1.2.6 */ +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +# define png_sprintf sprintf +# endif +#endif + +#ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +#else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). + */ +# define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, + * and no smaller than png_uint_32. Casts from png_size_t or png_uint_32 + * to png_alloc_size_t are not necessary; in fact, it is recommended + * not to use them at all so that the compiler can complain when something + * turns out to be problematic. + * Casts in the other direction (from png_alloc_size_t to png_size_t or + * png_uint_32) should be explicitly applied; however, we do not expect + * to encounter practical situations that require such conversions. + */ +#if defined(__TURBOC__) && !defined(__FLAT__) + typedef unsigned long png_alloc_size_t; +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + typedef unsigned long png_alloc_size_t; +# else + /* This is an attempt to detect an old Windows system where (int) is + * actually 16 bits, in that case png_malloc must have an argument with a + * bigger size to accomodate the requirements of the library. + */ +# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) && \ + (!defined(INT_MAX) || INT_MAX <= 0x7ffffffeL) + typedef DWORD png_alloc_size_t; +# else + typedef png_size_t png_alloc_size_t; +# endif +# endif +#endif +/* End of memory model/platform independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/lib/png/src/pngerror.c b/lib/png/src/pngerror.c new file mode 100644 index 0000000..bb02b27 --- /dev/null +++ b/lib/png/src/pngerror.c @@ -0,0 +1,402 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.4.0 [January 3, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#include "pngpriv.h" + +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; +#ifdef PNG_WARNINGS_SUPPORTED +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifdef PNG_ERROR_TEXT_SUPPORTED +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + else + error_message += offset; + } + else + { + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +void PNGAPI +png_err(png_structp png_ptr) +{ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, '\0'); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, '\0'); +} +#endif /* PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') + break; + } + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message + offset); + else + png_default_warning(png_ptr, warning_message + offset); +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} +#endif + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +#define PNG_MAX_ERROR_TEXT 64 +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_memcpy(buffer + iout, error_message, PNG_MAX_ERROR_TEXT); + buffer[iout + PNG_MAX_ERROR_TEXT - 1] = '\0'; + } +} + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_chunk_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_chunk_warning(png_ptr, error_message); + else + png_chunk_error(png_ptr, error_message); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#ifdef PNG_SETJMP_SUPPORTED +/* This API only exists if ANSI-C style error handling is used, + * otherwise it is necessary for png_default_error to be overridden. + */ +jmp_buf* PNGAPI +png_set_longjmp_fn(png_structp png_ptr, png_longjmp_ptr longjmp_fn, + size_t jmp_buf_size) +{ + if (png_ptr == NULL || jmp_buf_size != png_sizeof(jmp_buf)) + return NULL; + + png_ptr->longjmp_fn = longjmp_fn; + return &png_ptr->jmpbuf; +} +#endif + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +#endif + { + fprintf(stderr, "libpng error: %s", error_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#endif + +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr && png_ptr->longjmp_fn) + { +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); + png_ptr->longjmp_fn(jmpbuf, 1); + } +# else + png_ptr->longjmp_fn(png_ptr->jmpbuf, 1); +# endif + } +#endif + /* Here if not setjmp support or if png_ptr is null. */ + PNG_ABORT(); +#ifndef PNG_CONSOLE_IO_SUPPORTED + error_message = error_message; /* Make compiler happy */ +#endif +} + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == PNG_LITERAL_SHARP) + { + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +# endif + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + warning_message = warning_message; /* Make compiler happy */ +#endif + png_ptr = png_ptr; /* Make compiler happy */ +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if (png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngerror.d b/lib/png/src/pngerror.d new file mode 100644 index 0000000..34e602b --- /dev/null +++ b/lib/png/src/pngerror.d @@ -0,0 +1,12 @@ +pngerror.o: pngerror.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngget.c b/lib/png/src/pngget.c new file mode 100644 index 0000000..2a43a4d --- /dev/null +++ b/lib/png/src/pngget.c @@ -0,0 +1,925 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.4.2 [May 6, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#include "pngpriv.h" + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + + else + return(0); +} + +png_size_t PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + + else + return(0); +} + +#ifdef PNG_INFO_IMAGE_SUPPORTED +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->width; + + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->height; + + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->bit_depth; + + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->color_type; + + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->filter_type; + + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->interlace_type; + + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->compression_type; + + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + + else + return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + + else + return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + + else + return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); + + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_oFFs_SUPPORTED + + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + + else + return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + + else + return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + + else + return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + + else + return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if (*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#ifdef PNG_bKGD_SUPPORTED +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function", "bKGD"); + + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#ifdef PNG_sRGB_SUPPORTED +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + png_debug1(1, "in %s retrieval function", "sRGB"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#ifdef PNG_iCCP_SUPPORTED +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + png_debug1(1, "in %s retrieval function", "iCCP"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* Compression_type is a dummy so the API won't have to change + * if we introduce multiple compression types later. + */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); + } + return (0); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + png_debug1(1, "in %s retrieval function", "hIST"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + png_debug1(1, "in %s retrieval function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL || width == NULL || + height == NULL || bit_depth == NULL || color_type == NULL) + return (0); + + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + *color_type = info_ptr->color_type; + + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* This is redundant if we can be sure that the info_ptr values were all + * assigned in png_set_IHDR(). We do the check anyhow in case an + * application has ignored our advice not to mess with the members + * of info_ptr directly. + */ + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + return (1); +} + +#ifdef PNG_oFFs_SUPPORTED +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + png_debug1(1, "in %s retrieval function", "oFFs"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + png_debug1(1, "in %s retrieval function", "pCAL"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + png_debug1(1, "in %s retrieval function", "PLTE"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#ifdef PNG_sBIT_SUPPORTED +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + png_debug1(1, "in %s retrieval function", "sBIT"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + + if (num_text != NULL) + *num_text = info_ptr->num_text; + + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + png_debug1(1, "in %s retrieval function", "tIME"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function", "tRNS"); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans_alpha != NULL) + { + *trans_alpha = info_ptr->trans_alpha; + retval |= PNG_INFO_tRNS; + } + + if (trans_color != NULL) + *trans_color = &(info_ptr->trans_color); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_color != NULL) + { + *trans_color = &(info_ptr->trans_color); + retval |= PNG_INFO_tRNS; + } + + if (trans_alpha != NULL) + *trans_alpha = NULL; + } + if (num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); + } + return (0); +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +png_size_t PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_ptr ? png_ptr->zbuf_size : 0L); +} + + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* These functions were added to libpng 1.2.6 and were enabled + * by default in libpng-1.4.0 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +/* This function was added to libpng 1.4.0 */ +png_uint_32 PNGAPI +png_get_chunk_cache_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_cache_max : 0); +} +/* This function was added to libpng 1.4.1 */ +png_alloc_size_t PNGAPI +png_get_chunk_malloc_max (png_structp png_ptr) +{ + return (png_ptr? + png_ptr->user_chunk_malloc_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +/* These functions were added to libpng 1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +png_uint_32 PNGAPI +png_get_io_state (png_structp png_ptr) +{ + return png_ptr->io_state; +} + +png_bytep PNGAPI +png_get_io_chunk_name (png_structp png_ptr) +{ + return png_ptr->chunk_name; +} +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngget.d b/lib/png/src/pngget.d new file mode 100644 index 0000000..42f0ddd --- /dev/null +++ b/lib/png/src/pngget.d @@ -0,0 +1,12 @@ +pngget.o: pngget.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngmem.c b/lib/png/src/pngmem.c new file mode 100644 index 0000000..a7a79ab --- /dev/null +++ b/lib/png/src/pngmem.c @@ -0,0 +1,611 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.4.2 [May 6, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#include "pngpriv.h" + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* If you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, NULL, NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, NULL, NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ +png_voidp PNGAPI +png_calloc(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* Try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if (png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory"); /* Note "O", "M" */ + else + png_warning(png_ptr, "Out Of Memory"); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof(png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory"); /* Note "O", "m" */ + else + png_warning(png_ptr, "Out Of memory"); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory"); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory"); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory"); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). In the default + * configuration, png_ptr is not used, but is passed in case it + * is needed. If ptr is NULL, return without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || ptr == NULL) + return; + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, NULL, NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size, 1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, NULL, NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + */ + +png_voidp PNGAPI +png_calloc(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + * without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_alloc_size_t size) +{ + png_voidp ptr; + png_uint_32 save_flags; + if (png_ptr == NULL) + return (NULL); + + save_flags = png_ptr->flags; + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} + + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngmem.d b/lib/png/src/pngmem.d new file mode 100644 index 0000000..7995179 --- /dev/null +++ b/lib/png/src/pngmem.d @@ -0,0 +1,12 @@ +pngmem.o: pngmem.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngpread.c b/lib/png/src/pngpread.c new file mode 100644 index 0000000..61683cc --- /dev/null +++ b/lib/png/src/pngpread.c @@ -0,0 +1,1765 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.4.3 [June 26, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#include "pngpriv.h" + +/* Push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr == NULL) + return; + + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } + +#ifdef PNG_READ_tEXt_SUPPORTED + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } + +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } + +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } + +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_sCAL; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_sRGB; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_sPLT; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_zTXt; +#endif + + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length != 13) + png_error(png_ptr, "Invalid IHDR length"); + + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } + +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_benign_error(png_ptr, "Too many IDATs found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if (png_ptr == NULL) + return; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i, istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, + (png_size_t)new_max); + if (png_ptr->save_buffer == NULL) + { + png_free(png_ptr, old_buffer); + png_error(png_ptr, "Insufficient memory for save_buffer"); + } + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ + PNG_IDAT; + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + + /* Check for overflow */ + if ((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + + /* Check for overflow */ + if ((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + /* The caller checks for a non-zero buffer length. */ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. + */ + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + + /* Keep going until the decompressed data is all processed + * or the stream marked as finished. + */ + while (png_ptr->zstream.avail_in > 0 && + !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { + int ret; + + /* We have data for zlib, but we must check that zlib + * has somewhere to put the results. It doesn't matter + * if we don't expect any results -- it may be the input + * data is just the LZ end code. + */ + if (!(png_ptr->zstream.avail_out > 0)) + { + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + + /* Using Z_SYNC_FLUSH here means that an unterminated + * LZ stream can still be handled (a stream with a missing + * end code), otherwise (Z_NO_FLUSH) a future zlib + * implementation might defer output and, therefore, + * change the current behavior. (See comments in inflate.c + * for why this doesn't happen at present with zlib 1.2.5.) + */ + ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + + /* Check for any failure before proceeding. */ + if (ret != Z_OK && ret != Z_STREAM_END) + { + /* Terminate the decompression. */ + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + + /* This may be a truncated stream (missing or + * damaged end code). Treat that as a warning. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + png_warning(png_ptr, "Truncated compressed data in IDAT"); + else + png_error(png_ptr, "Decompression error in IDAT"); + + /* Skip the check on unprocessed input */ + return; + } + + /* Did inflate output any data? */ + if (png_ptr->zstream.next_out != png_ptr->row_buf) + { + /* Is this unexpected data after the last row? + * If it is, artificially terminate the LZ output + * here. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + { + /* Extra data. */ + png_warning(png_ptr, "Extra compressed data in IDAT"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + /* Do no more processing; skip the unprocessed + * input check below. + */ + return; + } + + /* Do we have a complete row? */ + if (png_ptr->zstream.avail_out == 0) + png_push_process_row(png_ptr); + } + + /* And check for the end of the stream. */ + if (ret == Z_STREAM_END) + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + } + + /* All the data should have been processed, if anything + * is left at this point we have bytes of IDAT data + * after the zlib end code. + */ + if (png_ptr->zstream.avail_in > 0) + png_warning(png_ptr, "Extra compression data"); +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ + } + + if (png_ptr->pass == 2) /* Pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 2) /* Skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 2: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 3: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 4: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Pass 5 might be empty */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 5: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Skip top generated row */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + + if (png_ptr->pass != 6) + break; + + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset(png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ +} + +#ifdef PNG_READ_tEXt_SUPPORTED +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + info_ptr = info_ptr; /* To quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_size_t)(length + 1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + + else + text_size = png_ptr->current_text_left; + + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#ifdef PNG_MAX_MALLOC_64K + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* Empty loop */ ; + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk"); + } +} +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + info_ptr = info_ptr; /* To quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_size_t)(length + 1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + + else + text_size = png_ptr->current_text_left; + + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* Empty loop */ ; + + /* zTXt can't have zero text */ + if (text >= key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* Check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_ptr->zbuf_size + - png_ptr->zstream.avail_out + key_size + 1)); + + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + png_memcpy(text, key, key_size); + + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_ptr->zbuf_size + - png_ptr->zstream.avail_out + 1)); + + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk"); + } +} +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + info_ptr = info_ptr; /* To quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_size_t)(length + 1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + + else + text_size = png_ptr->current_text_left; + + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#ifdef PNG_MAX_MALLOC_64K + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* Empty loop */ ; + + if (lang < key + png_ptr->current_text_size - 3) + lang++; + + comp_flag = *lang++; + lang++; /* Skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* Empty loop */ ; + + lang_key++; /* Skip NUL separator */ + + text=lang_key; + + if (lang_key < key + png_ptr->current_text_size - 1) + { + for (; *text; text++) + /* Empty loop */ ; + } + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + png_sizeof(png_text)); + + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk"); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip = 0; + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + info_ptr = info_ptr; /* To quiet some compiler warnings */ + } + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name) - 1] + = '\0'; + + png_ptr->unknown_chunk.size = (png_size_t)length; + + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + + else + { + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, + (png_size_t)length); + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); + } + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + { + /* Callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } + + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ + PNG_CONST int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + + if (png_ptr == NULL) + return; + + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/lib/png/src/pngpread.d b/lib/png/src/pngpread.d new file mode 100644 index 0000000..cfc30d5 --- /dev/null +++ b/lib/png/src/pngpread.d @@ -0,0 +1,12 @@ +pngpread.o: pngpread.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngpriv.h b/lib/png/src/pngpriv.h new file mode 100644 index 0000000..ebf8e53 --- /dev/null +++ b/lib/png/src/pngpriv.h @@ -0,0 +1,954 @@ + +/* pngpriv.h - private declarations for use inside libpng + * + * libpng version 1.4.4 - September 23, 2010 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The symbols declared in this file (including the functions declared + * as PNG_EXTERN) are PRIVATE. They are not part of the libpng public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + * Although the libpng users are not forbidden to include this header, + * they should be well aware of the issues that may arise from doing so. + */ + +#ifndef PNGPRIV_H +#define PNGPRIV_H + +#ifndef PNG_VERSION_INFO_ONLY + +#include + +#ifndef PNG_EXTERN +/* The functions exported by PNG_EXTERN are internal functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it + * is possible to have run-time registry of chunk-handling functions, + * some of these will be made available again. +# define PNG_EXTERN extern + */ +# define PNG_EXTERN +#endif + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# ifdef MACOS + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \ + defined(_WIN32) || defined(__WIN32__) +# include /* defines _WINDOWS_ macro */ +#endif + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_QUANTIZE 0x0040 /* formerly PNG_DITHER */ +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* Flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* Flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 + /* 0x1000 unused */ + /* 0x2000 unused */ + /* 0x4000 unused */ +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000L /* Added to libpng-1.4.0 */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ + (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro wherever it's + * needed. + */ +#define PNG_IHDR PNG_CONST png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT PNG_CONST png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND PNG_CONST png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE PNG_CONST png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD PNG_CONST png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM PNG_CONST png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA PNG_CONST png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST PNG_CONST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP PNG_CONST png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt PNG_CONST png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs PNG_CONST png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL PNG_CONST png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL PNG_CONST png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs PNG_CONST png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT PNG_CONST png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT PNG_CONST png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB PNG_CONST png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_sTER PNG_CONST png_byte png_sTER[5] = {115, 84, 69, 82, '\0'} +#define PNG_tEXt PNG_CONST png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME PNG_CONST png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS PNG_CONST png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt PNG_CONST png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* These functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* Function to allocate memory for zlib. PNGAPI is disallowed. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib. PNGAPI is disallowed. */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read the chunk header (length + type name) */ +PNG_EXTERN png_uint_32 png_read_chunk_header PNGARG((png_structp png_ptr)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_size_t chunklength, png_size_t prefix_length, + png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#ifdef PNG_WRITE_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, + png_fixed_point file_gamma)); +#endif +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr, + png_byte bit_depth)); +#endif + +/* Combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* Unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* Finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* Initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* Optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* These are the functions that do the transformations */ +#ifdef PNG_READ_FILLER_SUPPORTED +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +PNG_EXTERN void png_do_quantize PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep quantize_lookup)); + +# ifdef PNG_CORRECT_PALETTE_SUPPORTED +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_color, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_color, png_color_16p background)); +#endif +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* Decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +PNG_EXTERN void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +PNG_EXTERN void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* Handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +/* Added at libpng version 1.4.0 */ +#ifdef PNG_cHRM_SUPPORTED +PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED +/* Added at libpng version 1.2.34 and 1.4.0 */ +PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2, + unsigned long *hi_product, unsigned long *lo_product)); +#endif +#endif + +/* Added at libpng version 1.4.0 */ +PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type)); + +/* Free all memory used by the read (old method - NOT DLL EXPORTED) */ +PNG_EXTERN void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +PNG_EXTERN void png_write_destroy PNGARG((png_structp png_ptr)); + +#ifdef USE_FAR_KEYWORD /* memory model conversion function */ +PNG_EXTERN void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#ifndef _DEBUG +# define _DEBUG +#endif +#ifndef png_debug +#define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +#endif +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ + +#if (PNG_DEBUG > 1) +/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on + * non-ISO compilers + */ +# ifdef __STDC__ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ + } +# endif +# else /* __STDC __ */ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); \ + } +# endif +# endif /* __STDC __ */ +#endif /* (PNG_DEBUG > 1) */ + +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +#endif /* PNGPRIV_H */ diff --git a/lib/png/src/pngread.c b/lib/png/src/pngread.c new file mode 100644 index 0000000..3ec2df4 --- /dev/null +++ b/lib/png/src/pngread.c @@ -0,0 +1,1361 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.4.1 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED +#include "pngpriv.h" + + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory + * needed. + */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; + volatile int png_cleanup_needed = 0; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct"); + +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + malloc_fn, mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +# ifdef PNG_USER_CHUNK_CACHE_MAX + /* Added at libpng-1.2.43 and 1.4.0 */ + png_ptr->user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +# endif +# ifdef PNG_SET_USER_CHUNK_MALLOC_MAX + /* Added at libpng-1.2.43 and 1.4.1 */ + png_ptr->user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; +# endif +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then + encounter a png_error() will longjmp here. Since the jmpbuf is + then meaningless we abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(png_ptr))) /* Sets longjmp to match setjmp */ +#endif + PNG_ABORT(); +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver) + { + i = 0; + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#ifdef PNG_STDIO_SUPPORTED + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_warning(png_ptr, + "Incompatible libpng version in application and library"); + + png_cleanup_needed = 1; + } + } + + if (!png_cleanup_needed) + { + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, + png_ptr->zbuf_size); + if (png_ptr->zbuf == NULL) + png_cleanup_needed = 1; + } + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + if (!png_cleanup_needed) + { + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_warning(png_ptr, "zlib memory error"); + png_cleanup_needed = 1; break; + case Z_VERSION_ERROR: png_warning(png_ptr, "zlib version error"); + png_cleanup_needed = 1; break; + default: png_warning(png_ptr, "Unknown zlib error"); + png_cleanup_needed = 1; + } + } + + if (png_cleanup_needed) + { + /* Clean up PNG structure and deallocate any memory. */ + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, NULL, NULL); + + + return (png_ptr); +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE; +#endif + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for (;;) + { + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_sCAL; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_sPLT; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_sRGB; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_zTXt; +#endif + png_uint_32 length = png_read_chunk_header(png_ptr); + PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(chunk_name, png_IDAT, 4)) + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, chunk_name)) + { + if (!png_memcmp(chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + + png_read_transform_info(png_ptr, info_ptr); +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ + PNG_IDAT; + PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, + 0xff}; + PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + int ret; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_read_row (row %lu, pass %d)", + (unsigned long) png_ptr->row_number, png_ptr->pass); + + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); +#endif + } + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* If interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = + (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_crc_finish(png_ptr, 0); + + png_ptr->idat_size = png_read_chunk_header(png_ptr); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_benign_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if (png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) + /* Old interface (pre-1.0.9): + * png_do_read_interlace(&(png_ptr->row_info), + * png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows"); + + if (png_ptr == NULL) + return; + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if (rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, NULL); + rp++; + } + else if (dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, NULL, dptr); + dp++; + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i, image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled"); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, NULL); + rp++; + } + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_end"); + + if (png_ptr == NULL) + return; + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_sCAL; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_sPLT; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_sRGB; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_zTXt; +#endif + png_uint_32 length = png_read_chunk_header(png_ptr); + PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; + + if (!png_memcmp(chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, chunk_name)) + { + if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_benign_error(png_ptr, "Too many IDATs found"); + } + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_benign_error(png_ptr, "Too many IDATs found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_read_struct"); + + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + if (png_ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#ifdef PNG_TEXT_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#ifdef PNG_READ_TEXT_SUPPORTED + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* Free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy"); + + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->chunkdata); +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->quantize_index); +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + png_free(png_ptr, png_ptr->gamma_table); +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->read_row_fn = read_row_fn; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + + if (png_ptr == NULL) + return; + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr, "Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#ifdef PNG_READ_16_TO_8_SUPPORTED + /* Tell libpng to strip 16 bit/color files down to 8 bits per color. + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or quantizing. + */ + +#ifdef PNG_READ_INVERT_SUPPORTED + /* Invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + /* Flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_SUPPORTED + /* Swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Expand grayscale image to RGB + */ + if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) + png_set_gray_to_rgb(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + if (info_ptr->row_pointers == NULL) + { + png_uint_32 iptr; + + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); + for (iptr=0; iptrheight; iptr++) + info_ptr->row_pointers[iptr] = NULL; + + info_ptr->free_me |= PNG_FREE_ROWS; + + for (row = 0; row < (int)info_ptr->height; row++) + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + transforms = transforms; /* Quiet compiler warnings */ + params = params; + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/lib/png/src/pngread.d b/lib/png/src/pngread.d new file mode 100644 index 0000000..fba71e7 --- /dev/null +++ b/lib/png/src/pngread.d @@ -0,0 +1,12 @@ +pngread.o: pngread.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngrio.c b/lib/png/src/pngrio.c new file mode 100644 index 0000000..990fe3f --- /dev/null +++ b/lib/png/src/pngrio.c @@ -0,0 +1,163 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.4.1 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED +#include "pngpriv.h" + +/* Read the data from whatever input you are using. The default routine + * reads from a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered reads. This should never be asked + * to read more then 64K on a 16 bit machine. + */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4, "reading %d bytes", (int)length); + + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = fread(data, 1, length, (png_FILE_p)png_ptr->io_ptr); + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* This is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + png_byte *n_data; + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + check = fread(n_data, 1, length, io_ptr); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + err = fread(buf, 1, read, io_ptr); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if (err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + * for libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png input data structure + * io_ptr - pointer to user supplied structure containing info about + * the input functions. May be NULL. + * read_data_fn - pointer to a new input function that takes as its + * arguments a pointer to a png_struct, a pointer to + * a location where input data can be stored, and a 32-bit + * unsigned int that is the number of bytes to be read. + * To exit and output any fatal error messages the new write + * function should call png_error(png_ptr, "Error msg"). + * May be NULL, in which case libpng's default function will + * be used. + */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL"); + } + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/lib/png/src/pngrio.d b/lib/png/src/pngrio.d new file mode 100644 index 0000000..f018ac0 --- /dev/null +++ b/lib/png/src/pngrio.d @@ -0,0 +1,12 @@ +pngrio.o: pngrio.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngrtran.c b/lib/png/src/pngrtran.c new file mode 100644 index 0000000..63ac38b --- /dev/null +++ b/lib/png/src/pngrtran.c @@ -0,0 +1,4203 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.4.2 [May 6, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED +#include "pngpriv.h" + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action"); + + if (png_ptr == NULL) + return; + + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + + case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ + png_warning(png_ptr, + "Can't discard critical data on CRC error"); + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + /* Tell libpng how we react to CRC errors in ancillary chunks */ + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* Handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background"); + + if (png_ptr == NULL) + return; + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); +} +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Quantize file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizeing cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_quantize(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_quantize) +{ + png_debug(1, "in png_set_quantize"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_QUANTIZE; + + if (!full_quantize) + { + int i; + + png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->quantize_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + + int i; + + /* Initialize an array to sort colors */ + png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + + /* Initialize the quantize_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->quantize_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->quantize_sort[j]] + < histogram[png_ptr->quantize_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->quantize_sort[j]; + png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; + png_ptr->quantize_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + png_ptr->quantize_index[j] = (png_byte)i; + png_ptr->quantize_index[i] = (png_byte)j; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->quantize_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = png_ptr->quantize_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* Point to closest color */ + png_ptr->quantize_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->quantize_sort); + png_ptr->quantize_sort = NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t = NULL; + + /* Initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + + /* Initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * + png_sizeof(png_dsortp))); + + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_quantize) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->quantize_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->quantize_index[k] == + num_new_palette) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = + (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = + (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index = NULL; + png_ptr->index_to_palette = NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_quantize) + { + int i; + png_bytep distance; + int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + + PNG_QUANTIZE_BLUE_BITS; + int num_red = (1 << PNG_QUANTIZE_RED_BITS); + int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); + int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_calloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof(png_byte))); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma"); + + if (png_ptr == NULL) + return; + + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha + * and its name was changed to png_set_expand_gray_1_2_4_to_8(). + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} + +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_EXPAND; + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} + + + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha"); + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb"); + + png_ptr->transformations |= PNG_GRAY_TO_RGB; + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed = (int)((float)red*100000.0 + 0.5); + int green_fixed = (int)((float)green*100000.0 + 0.5); + if (png_ptr == NULL) + return; + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray"); + + if (png_ptr == NULL) + return; + + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, + "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if (red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if (red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = + (png_uint_16)(32768 - red_int - green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations"); + + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_SHIFT_SUPPORTED) || \ + defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Detect gray background and attempt to enable optimization + * for gray --> RGB case + * + * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + !(color_type & PNG_COLOR_MASK_COLOR)) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } else if ((png_ptr->transformations & PNG_BACKGROUND) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_GRAY_TO_RGB) && + png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } +#endif + + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* Expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_color.gray *= (png_uint_16)0xff; + png_ptr->trans_color.red = png_ptr->trans_color.green + = png_ptr->trans_color.blue = png_ptr->trans_color.gray; + } + break; + + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_color.gray *= (png_uint_16)0x55; + png_ptr->trans_color.red = png_ptr->trans_color.green + = png_ptr->trans_color.blue = png_ptr->trans_color.gray; + } + break; + + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_color.gray *= (png_uint_16)0x11; + png_ptr->trans_color.red = png_ptr->trans_color.green + = png_ptr->trans_color.blue = png_ptr->trans_color.gray; + } + break; + + case 8: + + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#ifdef PNG_READ_EXPAND_SUPPORTED + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* Invert the alpha channel (in tRNS) unless the pixels are + * going to be expanded, in which case leave it for later + */ + int i, istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans_alpha[i] = (png_byte)(255 - png_ptr->trans_alpha[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i, k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans_alpha[i] != 0 && png_ptr->trans_alpha[i] != 0xff) + k=1; /* Partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= ~PNG_GAMMA; + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr, png_ptr->bit_depth); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* Could skip if no transparency */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255.0, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255.0, gs) * 255.0 + + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255.0, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255.0, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255.0, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255.0, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans_alpha[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + /* Prevent the transformations being done again, and make sure + * that the now spurious alpha channel is stripped - the code + * has just reduced background composition and gamma correction + * to a simple alpha channel strip. + */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations &= ~PNG_GAMMA; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* Transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans_alpha[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans_alpha[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans_alpha[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans_alpha[i], back.blue); + } + } + + /* Handled alpha, still need to strip the channel. */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info"); + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#ifdef PNG_READ_FILLER_SUPPORTED + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* If adding a true alpha channel not just filler */ + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if (info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); + +#ifndef PNG_READ_EXPAND_SUPPORTED + if (png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations"); + + if (png_ptr->row_buf == NULL) + { +#ifdef PNG_STDIO_SUPPORTED + char msg[50]; + + png_snprintf2(msg, 50, + "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#ifdef PNG_WARN_UNINITIALIZED_ROW + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + /* Application has failed to call either png_read_start_image() + * or png_read_update_info() after setting transforms that expand + * pixels. This check added to libpng-1.2.19 + */ +#if (PNG_WARN_UNINITIALIZED_ROW==1) + png_error(png_ptr, "Uninitialized row"); +#else + png_warning(png_ptr, "Uninitialized row"); +#endif +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_color)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1); + if (rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* From Andreas Dilger e-mail to png-implement, 26 March 1998: + * + * In most cases, the "simple transparency" should be done prior to doing + * gray-to-RGB, or you will have to test 3x as many bytes to check if a + * pixel is transparent. You would also need to make sure that the + * transparency information is upgraded to RGB. + * + * To summarize, the current flow is: + * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + * with background "in place" if transparent, + * convert to RGB if necessary + * - Gray + alpha -> composite with gray background and remove alpha bytes, + * convert to RGB if necessary + * + * To support RGB backgrounds for gray images we need: + * - Gray + simple transparency -> convert to RGB + simple transparency, + * compare 3 or 6 bytes and composite with + * background "in place" if transparent + * (3x compare/pixel compared to doing + * composite with gray bkgrnd) + * - Gray + alpha -> convert to RGB + alpha, composite with background and + * remove alpha bytes (3x float + * operations/pixel compared with composite + * on gray background) + * + * Greg's change will do this. The reason it wasn't done before is for + * performance, as this increases the per-pixel operations. If we would check + * in advance if the background was gray or RGB, and position the gray-to-RGB + * transform appropriately, then it would save a lot of work/time. + */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons + */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_color), &(png_ptr->background) +#ifdef PNG_READ_GAMMA_SUPPORTED + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->transformations & PNG_GAMMA) && +#ifdef PNG_READ_BACKGROUND_SUPPORTED + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) + { + png_do_quantize((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->quantize_index); + if (png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_quantize returned rowbytes=0"); + } +#endif + +#ifdef PNG_READ_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* User read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if (png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#ifdef PNG_READ_PACK_SUPPORTED +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack"); + + if (row_info->bit_depth < 8) + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift"); + + if ( + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha"); + + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha"); + + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler"); + + if ( + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb"); + + if (row_info->bit_depth >= 8 && + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * (THIS LINK IS DEAD June 2008) + * New link: + * + * Charles Poynton poynton at poynton.com + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue)>>15]; + } + else + *(dp++) = *(sp - 1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } + else + *(dp++) = *(sp - 1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette"); + + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + + case 2: + num_palette = 4; + color_inc = 0x55; + break; + + case 4: + num_palette = 16; + color_inc = 0x11; + break; + + case 8: + num_palette = 256; + color_inc = 1; + break; + + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_color, png_color_16p background +#ifdef PNG_READ_GAMMA_SUPPORTED + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background"); + + if (background != NULL && + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_color))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_color->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + + case 2: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_color->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_color->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + + case 4: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_color->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_color->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + + case 8: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_color->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_color->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + + case 16: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_color->gray) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_color->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_color->red && + *(sp + 1) == trans_color->green && + *(sp + 2) == trans_color->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_color->red && + *(sp + 1) == trans_color->green && + *(sp + 2) == trans_color->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_color->red && g == trans_color->green && + b == trans_color->blue) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_color->red && g == trans_color->green && + b == trans_color->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) +#else + else +#endif + { + /* Background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); + + if ( + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans_alpha, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette"); + + if ( + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans_alpha != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans_alpha[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)((gray&0x01)*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + gray = (png_uint_16)((gray&0x03)*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + gray = (png_uint_16)((gray&0x0f)*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + + else if (row_info->bit_depth == 16) + { + png_byte gray_high = (gray >> 8) & 0xff; + png_byte gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + png_byte red = trans_value->red & 0xff; + png_byte green = trans_value->green & 0xff; + png_byte blue = trans_value->blue & 0xff; + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (trans_value->red >> 8) & 0xff; + png_byte green_high = (trans_value->green >> 8) & 0xff; + png_byte blue_high = (trans_value->blue >> 8) & 0xff; + png_byte red_low = trans_value->red & 0xff; + png_byte green_low = trans_value->green & 0xff; + png_byte blue_low = trans_value->blue & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +void /* PRIVATE */ +png_do_quantize(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep quantize_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_quantize"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + quantize_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = quantize_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +static PNG_CONST int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + * + * See the PNG extensions document for an integer algorithm for creating + * the gamma tables. Maybe we will implement that here someday. + * + * We should only reach this point if + * + * the file_gamma is known (i.e., the gAMA or sRGB chunk is present, + * or the application has provided a file_gamma) + * + * AND + * { + * the screen_gamma is known + * + * OR + * + * RGB_to_gray transformation is being performed + * } + * + * AND + * { + * the screen_gamma is different from the reciprocal of the + * file_gamma by more than the specified threshold + * + * OR + * + * a background color has been specified and the file_gamma + * and screen_gamma are not 1.0, within the specified threshold. + * } + */ + +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr, png_byte bit_depth) +{ + png_debug(1, "in png_build_gamma_table"); + + if (bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + + else + g = png_ptr->gamma; /* Probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + + else + g = png_ptr->gamma; /* Probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/lib/png/src/pngrtran.d b/lib/png/src/pngrtran.d new file mode 100644 index 0000000..a9f287a --- /dev/null +++ b/lib/png/src/pngrtran.d @@ -0,0 +1,12 @@ +pngrtran.o: pngrtran.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngrutil.c b/lib/png/src/pngrutil.c new file mode 100644 index 0000000..6a7437e --- /dev/null +++ b/lib/png/src/pngrutil.c @@ -0,0 +1,3379 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.4.4 [August 26, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED +#include "pngpriv.h" + +# define png_strtod(p,a,b) strtod(a,b) +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ + png_uint_32 i = png_get_uint_32(buf); + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range"); + return (i); +} +#ifndef PNG_USE_READ_MACROS +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 (PNGAPI +png_get_uint_32)(png_bytep buf) +{ + png_uint_32 i = + ((png_uint_32)(*(buf )) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + ((png_uint_32)(*(buf + 3)) ) ; + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format and there + * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore + * the following code does a two's complement to native conversion. + */ +png_int_32 (PNGAPI +png_get_int_32)(png_bytep buf) +{ + png_uint_32 u = png_get_uint_32(buf); + if ((u & 0x80000000) == 0) /* non-negative */ + return u; + + u = (u ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + return -(png_int_32)u; +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 (PNGAPI +png_get_uint_16)(png_bytep buf) +{ + png_uint_16 i = + ((png_uint_32)(*buf) << 8) + + ((png_uint_32)(*(buf + 1))); + + return (i); +} +#endif /* PNG_USE_READ_MACROS */ + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk header is being read. + * PNG_IO_CHUNK_HDR requires a single I/O call. + */ + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* Put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, buf + 4, 4); + + png_debug2(0, "Reading %s chunk, length = %lu", + png_ptr->chunk_name, length); + + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, png_ptr->chunk_name, 4); + + /* Check to see if chunk name is valid */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that chunk data will (possibly) be read. + * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. + */ + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif + + return length; +} + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + if (png_ptr == NULL) + return; + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading a ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_benign_error(png_ptr, "CRC error"); + return (0); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk CRC is being read */ + /* PNG_IO_CHUNK_CRC requires the I/O to be done at once */ + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +static png_size_t +png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size, + png_bytep output, png_size_t output_size) +{ + png_size_t count = 0; + + png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */ + png_ptr->zstream.avail_in = size; + + while (1) + { + int ret, avail; + + /* Reset the output buffer each time round - we empty it + * after every inflate call. + */ + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = png_ptr->zbuf_size; + + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out; + + /* First copy/count any new output - but only if we didn't + * get an error code. + */ + if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0) + { + if (output != 0 && output_size > count) + { + int copy = output_size - count; + if (avail < copy) copy = avail; + png_memcpy(output + count, png_ptr->zbuf, copy); + } + count += avail; + } + + if (ret == Z_OK) + continue; + + /* Termination conditions - always reset the zstream, it + * must be left in inflateInit state. + */ + png_ptr->zstream.avail_in = 0; + inflateReset(&png_ptr->zstream); + + if (ret == Z_STREAM_END) + return count; /* NOTE: may be zero. */ + + /* Now handle the error codes - the API always returns 0 + * and the error message is dumped into the uncompressed + * buffer if available. + */ + { + PNG_CONST char *msg; + if (png_ptr->zstream.msg != 0) + msg = png_ptr->zstream.msg; + else + { +#ifdef PNG_STDIO_SUPPORTED + char umsg[52]; + + switch (ret) + { + case Z_BUF_ERROR: + msg = "Buffer error in compressed datastream in %s chunk"; + break; + case Z_DATA_ERROR: + msg = "Data error in compressed datastream in %s chunk"; + break; + default: + msg = "Incomplete compressed datastream in %s chunk"; + break; + } + + png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name); + msg = umsg; +#else + msg = "Damaged compressed datastream in chunk other than IDAT"; +#endif + } + + png_warning(png_ptr, msg); + } + + /* 0 means an error - notice that this code simple ignores + * zero length compressed chunks as a result. + */ + return 0; + } +} + +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +void /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + /* The caller should guarantee this */ + if (prefix_size > chunklength) + { + /* The recovery is to delete the chunk. */ + png_warning(png_ptr, "invalid chunklength"); + prefix_size = 0; /* To delete everything */ + } + + else if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + png_size_t expanded_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + 0/*output*/, 0/*output size*/); + + /* Now check the limits on this chunk - if the limit fails the + * compressed data will be removed, the prefix will remain. + */ +#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max && + (prefix_size + expanded_size >= png_ptr->user_chunk_malloc_max - 1)) +#else +# ifdef PNG_USER_CHUNK_MALLOC_MAX + if ((PNG_USER_CHUNK_MALLOC_MAX > 0) && + prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1) +# endif +#endif + png_warning(png_ptr, "Exceeded size limit while expanding chunk"); + + /* If the size is zero either there was an error and a message + * has already been output (warning) or the size really is zero + * and we have nothing to do - the code will exit through the + * error case below. + */ +#if defined(PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED) || \ + defined(PNG_USER_CHUNK_MALLOC_MAX) + else +#endif + if (expanded_size > 0) + { + /* Success (maybe) - really uncompress the chunk. */ + png_size_t new_size = 0; + png_charp text = png_malloc_warn(png_ptr, + prefix_size + expanded_size + 1); + + if (text != NULL) + { + png_memcpy(text, png_ptr->chunkdata, prefix_size); + new_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + (png_bytep)(text + prefix_size), expanded_size); + text[prefix_size + expanded_size] = 0; /* just in case */ + + if (new_size == expanded_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + *newlength = prefix_size + expanded_size; + return; /* The success return! */ + } + + png_warning(png_ptr, "png_inflate logic error"); + png_free(png_ptr, text); + } + else + png_warning(png_ptr, "Not enough memory to decompress chunk"); + } + } + + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#ifdef PNG_STDIO_SUPPORTED + char umsg[50]; + + png_snprintf(umsg, sizeof umsg, "Unknown zTXt compression type %d", + comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + /* The recovery is to simply drop the data. */ + } + + /* Generic error return - leave the prefix, delete the compressed + * data, reallocate the chunkdata to remove the potentially large + * amount of compressed data. + */ + { + png_charp text = png_malloc_warn(png_ptr, prefix_size + 1); + if (text != NULL) + { + if (prefix_size > 0) + png_memcpy(text, png_ptr->chunkdata, prefix_size); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + + /* This is an extra zero in the 'uncompressed' part. */ + *(png_ptr->chunkdata + prefix_size) = 0x00; + } + /* Ignore a malloc error here - it is safe. */ + } + + *newlength = prefix_size; +} +#endif + +/* Read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* Check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* Set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* Find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* Set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* Read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* Don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + * whatever the normal CRC configuration tells us. However, if we + * have an RGB image, the PLTE can be considered ancillary, so + * we will act as though it is. + */ +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_benign_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + info_ptr = info_ptr; /* Quiet compiler warnings about unused info_ptr */ +} + +#ifdef PNG_READ_gAMA_SUPPORTED +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#ifdef PNG_READ_sRGB_SUPPORTED + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* Check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#ifdef PNG_READ_sRGB_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED + fprintf(stderr, "gamma = (%d/100000)", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[32]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#ifdef PNG_READ_sRGB_SUPPORTED + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 32); + if (png_crc_finish(png_ptr, 0)) + return; + + uint_x = png_get_uint_32(buf); + uint_y = png_get_uint_32(buf + 4); + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 8); + uint_y = png_get_uint_32(buf + 12); + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 16); + uint_y = png_get_uint_32(buf + 20); + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 24); + uint_y = png_get_uint_32(buf + 28); + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr, "gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr, "wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + (long)int_x_white, (long)int_y_white, + (long)int_x_red, (long)int_y_red); + fprintf(stderr, "gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + (long)int_x_green, (long)int_y_green, + (long)int_x_blue, (long)int_y_blue); +#endif +#endif /* PNG_CONSOLE_IO_SUPPORTED */ + } + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif +} +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* Check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=(%d/100000)\n", + (int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=%f\n", png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_READ_iCCP_SUPPORTED +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (profile = png_ptr->chunkdata; *profile; profile++) + /* Empty loop to find end of name */ ; + + ++profile; + + /* There should be at least one zero (the compression type byte) + * following the separator, and we should be on it + */ + if ( profile >= png_ptr->chunkdata + slength - 1) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* Compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type = 0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - png_ptr->chunkdata; + png_decompress_chunk(png_ptr, compression_type, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(png_ptr->chunkdata + prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC + 1))<<16) | + ((*(pC + 2))<< 8) | + ((*(pC + 3)) ); + + if (profile_size < profile_length) + profile_length = profile_size; + + if (profile_size > profile_length) + { +#ifdef PNG_STDIO_SUPPORTED + char umsg[50]; +#endif + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Ignoring truncated iCCP profile"); +#ifdef PNG_STDIO_SUPPORTED + + png_snprintf(umsg, 50, "declared profile size = %lu", + (unsigned long)profile_size); + png_warning(png_ptr, umsg); + png_snprintf(umsg, 50, "actual profile length = %lu", + (unsigned long)profile_length); + png_warning(png_ptr, umsg); +#endif + return; + } + + png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata, + compression_type, png_ptr->chunkdata + prefix_length, profile_length); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_sPLT_SUPPORTED +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; + entry_start++) + /* Empty loop to find end of name */ ; + ++entry_start; + + /* A sample depth should follow the separator, and we should be on it */ + if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - (png_bytep)png_ptr->chunkdata)); + + /* Integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > + (png_uint_32) (PNG_SIZE_MAX / png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0; i < new_palette.nentries; i++) + { + pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* Discard all chunk data except the name and stash that */ + new_palette.name = png_ptr->chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_tRNS_SUPPORTED +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_color.gray = png_get_uint_16(buf); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_color.red = png_get_uint_16(buf); + png_ptr->trans_color.green = png_get_uint_16(buf + 2); + png_ptr->trans_color.blue = png_get_uint_16(buf + 4); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_color)); +} +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if (info_ptr && info_ptr->num_palette) + { + if (buf[0] >= info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +/* Read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)", + length + 1); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = png_ptr->chunkdata; *buf; buf++) + /* Empty loop */ ; + + endptr = png_ptr->chunkdata + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array"); + params = (png_charpp)png_malloc_warn(png_ptr, + (png_size_t)(nparams * png_sizeof(png_charp))); + if (params == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "No memory for pCAL params"); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d", i); + for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); +} +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +/* Read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)", + length + 1); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ + + ep = png_ptr->chunkdata + 1; /* Skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + png_memcpy(swidth, ep, png_strlen(ep)); +#endif +#endif + + for (ep = png_ptr->chunkdata; *ep; ep++) + /* Empty loop */ ; + ep++; + + if (png_ptr->chunkdata + slength < ep) + { + png_warning(png_ptr, "Truncated sCAL chunk"); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (sheight == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, swidth); + return; + } + png_memcpy(sheight, ep, png_strlen(ep)); +#endif +#endif + + if (png_ptr->chunkdata + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, png_ptr->chunkdata[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for tEXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process text chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + key = png_ptr->chunkdata; + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* Empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk"); +} +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for zTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory processing zTXt chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (text = png_ptr->chunkdata; *text; text++) + /* Empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text >= png_ptr->chunkdata + slength - 2) + { + png_warning(png_ptr, "Truncated zTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* Skip the compression_method byte */ + } + prefix_len = text - png_ptr->chunkdata; + + png_decompress_chunk(png_ptr, comp_type, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process zTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = comp_type; + text_ptr->key = png_ptr->chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = png_ptr->chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk"); +} +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for iTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (lang = png_ptr->chunkdata; *lang; lang++) + /* Empty loop */ ; + lang++; /* Skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + * translated keyword (possibly empty), and possibly some text after the + * keyword + */ + + if (lang >= png_ptr->chunkdata + slength - 3) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* Empty loop */ ; + lang_key++; /* Skip NUL separator */ + + if (lang_key >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + for (text = lang_key; *text; text++) + /* Empty loop */ ; + text++; /* Skip NUL separator */ + if (text >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Malformed iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + prefix_len = text - png_ptr->chunkdata; + + key=png_ptr->chunkdata; + if (comp_flag) + png_decompress_chunk(png_ptr, comp_type, + (size_t)length, prefix_len, &data_len); + else + data_len = png_strlen(png_ptr->chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key); + text_ptr->lang = png_ptr->chunkdata + (lang - key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = png_ptr->chunkdata; + text_ptr->text = png_ptr->chunkdata + prefix_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk"); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for unknown chunk"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + PNG_IDAT; + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* Not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + || (png_ptr->read_user_chunk_fn != NULL) +#endif + ) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] + = '\0'; + png_ptr->unknown_chunk.size = (png_size_t)length; + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + else + { + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); + } +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + { + /* Callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#ifndef PNG_READ_USER_CHUNKS_SUPPORTED + info_ptr = info_ptr; /* Quiet compiler warnings about unused info_ptr */ +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1, "in png_combine_row"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_read_interlace"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); + } +#ifndef PNG_READ_PACKSWAP_SUPPORTED + transformations = transformations; /* Silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row"); + png_debug2(2, "row = %lu, filter = %d", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* Use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row = 0; + break; + } +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + png_debug(1, "in png_read_finish_row"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset(png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { + PNG_IDAT; + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for (;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_size_t row_bytes; + + png_debug(1, "in png_read_start_row"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + } + else +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + } + max_pixel_depth = png_ptr->pixel_depth; + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#ifdef PNG_READ_EXPAND_SUPPORTED + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#ifdef PNG_READ_FILLER_SUPPORTED + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth = png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* Align the width on the next larger 8 pixels. Mainly used + * for interlacing + */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* Calculate the maximum bytes needed, adding a byte and a pixel + * for safety's sake + */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + + if (row_bytes + 48 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + if (png_ptr->interlaced) + png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, + row_bytes + 48); + else + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, + row_bytes + 48); + png_ptr->old_big_row_buf_size = row_bytes + 48; + +#ifdef PNG_ALIGNED_MEMORY_SUPPORTED + /* Use 16-byte aligned memory for row_buf with at least 16 bytes + * of padding before and after row_buf. + */ + png_ptr->row_buf = png_ptr->big_row_buf + 32 + - (((png_alloc_size_t)&(png_ptr->big_row_buf[0]) + 15) % 16); + png_ptr->old_big_row_buf_size = row_bytes + 48; +#else + /* Use 32 bytes of padding before and 16 bytes after row_buf. */ + png_ptr->row_buf = png_ptr->big_row_buf + 32; +#endif + png_ptr->old_big_row_buf_size = row_bytes + 48; + } + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory"); + + if (png_ptr->rowbytes + 1 > png_ptr->old_prev_row_size) + { + png_free(png_ptr, png_ptr->prev_row); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + png_ptr->old_prev_row_size = png_ptr->rowbytes + 1; + } + + png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,", png_ptr->width); + png_debug1(3, "height = %lu,", png_ptr->height); + png_debug1(3, "iwidth = %lu,", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/lib/png/src/pngrutil.d b/lib/png/src/pngrutil.d new file mode 100644 index 0000000..3bdec1b --- /dev/null +++ b/lib/png/src/pngrutil.d @@ -0,0 +1,12 @@ +pngrutil.o: pngrutil.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngset.c b/lib/png/src/pngset.c new file mode 100644 index 0000000..90ef1b4 --- /dev/null +++ b/lib/png/src/pngset.c @@ -0,0 +1,1167 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.4.1 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#include "pngpriv.h" + +#ifdef PNG_bKGD_SUPPORTED +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) +#endif + { + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; + } +} +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=21474.83; + } + else + png_gamma = file_gamma; + info_ptr->gamma = (float)png_gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(png_gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point)PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + png_gamma = 0; + } + else + png_gamma = int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(png_gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped"); + return; + } + + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16)); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data"); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + + info_ptr->free_me |= PNG_FREE_HIST; +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type = (png_byte)color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* Check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = 0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#ifdef PNG_oFFs_SUPPORTED +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_size_t length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose"); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units"); + return; + } + png_memcpy(info_ptr->pcal_units, units, length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_size_t)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params"); + return; + } + + png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp)); + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter"); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], length); + } + + info_ptr->valid |= PNG_INFO_pCAL; + info_ptr->free_me |= PNG_FREE_PCAL; +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_size_t length; + + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL"); + return; + } + png_memcpy(info_ptr->scal_s_width, swidth, length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + png_warning(png_ptr, + "Memory allocation failed while processing sCAL"); + return; + } + png_memcpy(info_ptr->scal_s_height, sheight, length); + info_ptr->valid |= PNG_INFO_sCAL; + info_ptr->free_me |= PNG_FREE_SCAL; +} +#endif +#endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function", "PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file that has + * too-large sample values. + */ + png_ptr->palette = (png_colorp)png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + + info_ptr->free_me |= PNG_FREE_PLTE; + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#ifdef PNG_sBIT_SUPPORTED +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif /* cHRM */ +} +#endif /* sRGB */ + + +#ifdef PNG_iCCP_SUPPORTED +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + png_uint_32 length; + + png_debug1(1, "in %s storage function", "iCCP"); + + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + length = png_strlen(name)+1; + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk"); + return; + } + png_memcpy(new_iccp_name, name, length); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, + "Insufficient memory to process iCCP profile"); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types + */ + info_ptr->iccp_compression = (png_byte)compression_type; + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function", ((png_ptr == NULL || + png_ptr->chunk_name[0] == '\0') ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_size_t)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_size_t)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + return(1); + info_ptr->free_me |= PNG_FREE_TEXT; + } + png_debug1(3, "allocated %d entries for info_ptr->text", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length, key_len; + png_size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + + else +#ifdef PNG_iTXt_SUPPORTED + { + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else /* PNG_iTXt_SUPPORTED */ + { + png_warning(png_ptr, "iTXt chunk not supported"); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_size_t) + (key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text", + (unsigned long)(png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len)); + *(textp->key + key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text = textp->key + key_len + 1; + } + if (text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text + text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans_alpha, int num_trans, png_color_16p trans_color) +{ + png_debug1(1, "in %s storage function", "tRNS"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans_alpha != NULL) + { + /* It may not actually be necessary to set png_ptr->trans_alpha here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans_alpha = info_ptr->trans_alpha = (png_bytep)png_malloc(png_ptr, + (png_size_t)PNG_MAX_PALETTE_LENGTH); + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); + } + + if (trans_color != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + (int)trans_color->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_color->red > sample_max || + (int)trans_color->green > sample_max || + (int)trans_color->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + png_memcpy(&(info_ptr->trans_color), trans_color, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->free_me |= PNG_FREE_TRNS; + } +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * + (png_size_t)png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes"); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + png_uint_32 length; + + length = png_strlen(from->name) + 1; + to->name = (png_charp)png_malloc_warn(png_ptr, (png_size_t)length); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + continue; + } + png_memcpy(to->name, from->name, length); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + (png_size_t)(from->nentries * png_sizeof(png_sPLT_entry))); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr, to->name); + to->name = NULL; + continue; + } + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; + info_ptr->free_me |= PNG_FREE_SPLT; +} +#endif /* PNG_sPLT_SUPPORTED */ + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (png_size_t)((info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk))); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk"); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_memcpy((png_charp)to->name, (png_charp)from->name, + png_sizeof(from->name)); + to->name[png_sizeof(to->name)-1] = '\0'; + to->size = from->size; + /* Note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + + if (from->size == 0) + to->data=NULL; + else + { + to->data = (png_bytep)png_malloc_warn(png_ptr, + (png_size_t)from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk"); + to->size = 0; + } + else + png_memcpy(to->data, from->data, from->size); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; + info_ptr->free_me |= PNG_FREE_UNKN; +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks = png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_size_t) + (5*(num_chunks + old_num_chunks))); + if (png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list + 5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p = new_list + 5*old_num_chunks + 4, i = 0; inum_chunk_list = old_num_chunks + num_chunks; + png_ptr->chunk_list = new_list; + png_ptr->free_me |= PNG_FREE_LIST; +} +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if (row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, + png_size_t size) +{ + if (png_ptr == NULL) + return; + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* This function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) + return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} + +/* This function was added to libpng 1.4.0 */ +void PNGAPI +png_set_chunk_cache_max (png_structp png_ptr, + png_uint_32 user_chunk_cache_max) +{ + if (png_ptr) + png_ptr->user_chunk_cache_max = user_chunk_cache_max; +} + +/* This function was added to libpng 1.4.1 */ +void PNGAPI +png_set_chunk_malloc_max (png_structp png_ptr, + png_alloc_size_t user_chunk_malloc_max) +{ + if (png_ptr) + png_ptr->user_chunk_malloc_max = + (png_size_t)user_chunk_malloc_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + if (allowed) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + else + png_ptr->flags &= ~PNG_FLAG_BENIGN_ERRORS_WARN; +} +#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngset.d b/lib/png/src/pngset.d new file mode 100644 index 0000000..977c03a --- /dev/null +++ b/lib/png/src/pngset.d @@ -0,0 +1,12 @@ +pngset.o: pngset.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngtrans.c b/lib/png/src/pngtrans.c new file mode 100644 index 0000000..bdc10a2 --- /dev/null +++ b/lib/png/src/pngtrans.c @@ -0,0 +1,677 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.4.2 [April 29, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#include "pngpriv.h" + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_uint_16)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* Invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + + if ( + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* Swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + + if ( + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* Remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler"); + + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if (user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED || + PNG_WRITE_USER_TRANSFORM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngtrans.d b/lib/png/src/pngtrans.d new file mode 100644 index 0000000..5489280 --- /dev/null +++ b/lib/png/src/pngtrans.d @@ -0,0 +1,12 @@ +pngtrans.o: pngtrans.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngwio.c b/lib/png/src/pngwio.c new file mode 100644 index 0000000..d7b42fd --- /dev/null +++ b/lib/png/src/pngwio.c @@ -0,0 +1,241 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.4.0 [January 3, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED +#include "pngpriv.h" + +/* Write the data to whatever output you are using. The default routine + * writes to a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered writes. This should never be asked + * to write more than 64K on a 16 bit machine. + */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if (png_ptr == NULL) + return; + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* This is the model-independent version. Since the standard I/O library + * can't handle far buffers in the medium and small models, we have to copy + * the data. + */ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + check = fwrite(near_data, 1, length, io_ptr); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* Copy far buffer to near buffer */ + err = fwrite(buf, 1, written, io_ptr); + if (err != written) + break; + + else + check += err; + + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#ifdef PNG_STDIO_SUPPORTED +void PNGAPI +png_default_flush(png_structp png_ptr) +{ + png_FILE_p io_ptr; + if (png_ptr == NULL) + return; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + fflush(io_ptr); +} +#endif +#endif + +/* This function allows the application to supply new output functions for + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL"); + } +} + +#ifdef USE_FAR_KEYWORD +#ifdef _MSC_VER +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (far_ptr != ptr) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngwio.d b/lib/png/src/pngwio.d new file mode 100644 index 0000000..8aa2c02 --- /dev/null +++ b/lib/png/src/pngwio.d @@ -0,0 +1,12 @@ +pngwio.o: pngwio.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngwrite.c b/lib/png/src/pngwrite.c new file mode 100644 index 0000000..75bfe18 --- /dev/null +++ b/lib/png/src/pngwrite.c @@ -0,0 +1,1457 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.4.0 [January 3, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Get internal access to png.h */ +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED +#include "pngpriv.h" + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + /* Write PNG signature */ + png_write_sig(png_ptr); +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ + (png_ptr->mng_features_permitted)) + { + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; + } +#endif + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type); +#else + 0); +#endif + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + */ +#ifdef PNG_WRITE_gAMA_SUPPORTED + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_sRGB_SUPPORTED + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#ifdef PNG_WRITE_iCCP_SUPPORTED + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#ifdef PNG_WRITE_sBIT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_cHRM_SUPPORTED + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#ifdef PNG_WRITE_tRNS_SUPPORTED + if (info_ptr->valid & PNG_INFO_tRNS) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j = 0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans_alpha[j] = (png_byte)(255 - info_ptr->trans_alpha[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_hIST_SUPPORTED + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#ifdef PNG_WRITE_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif + +#ifdef PNG_sCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_sCAL) +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else /* !FLOATING_POINT */ +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#endif /* FIXED_POINT */ +#endif /* FLOATING_POINT */ +#else /* !WRITE_sCAL */ + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written"); +#endif /* WRITE_sCAL */ +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif /* tEXt */ + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* See if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#ifdef PNG_WRITE_TEXT_SUPPORTED + int i; /* local index variable */ +#endif +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Write end of PNG file */ + png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif +} + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* "tm" structure is not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + volatile int png_cleanup_needed = 0; +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + + png_debug(1, "in png_create_write_struct"); + +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then + encounter a png_error() will longjmp here. Since the jmpbuf is + then meaningless we abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */ +#endif +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, png_sizeof(jmp_buf)); +#endif + PNG_ABORT(); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver) + { + i = 0; + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#ifdef PNG_STDIO_SUPPORTED + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_warning(png_ptr, + "Incompatible libpng version in application and library"); + png_cleanup_needed = 1; + } + } + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + if (!png_cleanup_needed) + { + png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, + png_ptr->zbuf_size); + if (png_ptr->zbuf == NULL) + png_cleanup_needed = 1; + } + if (png_cleanup_needed) + { + /* Clean up PNG structure and deallocate any memory. */ + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } + + png_set_write_fn(png_ptr, NULL, NULL, NULL); + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, NULL, NULL); +#endif + + return (png_ptr); +} + + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* Loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* Loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* Loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_write_row (row %ld, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* Initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row"); + + /* Check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); +#endif + + png_write_start_row(png_ptr); + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* Set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy(png_ptr->row_buf + 1, row, png_ptr->row_info.rowbytes); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* Handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush"); + + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* Free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct"); + + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL) + { + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; + } +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + if (png_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->num_chunk_list = 0; + } +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* Save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy"); + + /* Free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* Free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + + if (png_ptr == NULL) + return; +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter = PNG_FILTER_NONE; break; +#ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter = PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter = PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter = PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter = PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics"); + + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* Avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits = 9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#ifdef PNG_WRITE_INVERT_SUPPORTED + /* Invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + /* Pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + /* Swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + /* Flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + /* Swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* Write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + transforms = transforms; /* Quiet compiler warnings */ + params = params; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngwrite.d b/lib/png/src/pngwrite.d new file mode 100644 index 0000000..e481dc4 --- /dev/null +++ b/lib/png/src/pngwrite.d @@ -0,0 +1,12 @@ +pngwrite.o: pngwrite.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngwtran.c b/lib/png/src/pngwtran.c new file mode 100644 index 0000000..9cf095b --- /dev/null +++ b/lib/png/src/pngwtran.c @@ -0,0 +1,566 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.4.1 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED +#include "pngpriv.h" + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#ifdef PNG_WRITE_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#ifdef PNG_WRITE_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#ifdef PNG_WRITE_PACK_SUPPORTED +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + + if (row_info->bit_depth == 8 && + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); + + if ( + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* With low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngwtran.d b/lib/png/src/pngwtran.d new file mode 100644 index 0000000..e816e81 --- /dev/null +++ b/lib/png/src/pngwtran.d @@ -0,0 +1,12 @@ +pngwtran.o: pngwtran.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/png/src/pngwutil.c b/lib/png/src/pngwutil.c new file mode 100644 index 0000000..dc4aa14 --- /dev/null +++ b/lib/png/src/pngwutil.c @@ -0,0 +1,2786 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.4.1 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED +#include "pngpriv.h" + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +#ifdef PNG_SAVE_INT_32_SUPPORTED +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void PNGAPI +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the signature is being written */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE; +#endif + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if (png_ptr == NULL) + return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, (png_size_t)length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + + png_debug2(0, "Writing %s chunk, length = %lu", chunk_name, + (unsigned long)length); + + if (png_ptr == NULL) + return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk header is being written. + * PNG_IO_CHUNK_HDR requires a single I/O call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR; +#endif + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_memcpy(buf + 4, chunk_name, 4); + png_write_data(png_ptr, buf, (png_size_t)8); + /* Put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, chunk_name, 4); + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, 4); + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that chunk data will (possibly) be written. + * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA; +#endif +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + /* Update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk CRC is being written. + * PNG_IO_CHUNK_CRC requires a single I/O function call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC; +#endif + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* The uncompressed input data */ + int input_len; /* Its length */ + int num_output_ptr; /* Number of output pointers used */ + int max_output_ptr; /* Size of output_ptr */ + png_charpp output_ptr; /* Array of pointers to output */ +} compression_state; + +/* Compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* We may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#ifdef PNG_STDIO_SUPPORTED + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* Set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* This is the same compression loop as in png_write_row() */ + do + { + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* Error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_alloc_size_t) + (comp->max_output_ptr * png_sizeof(png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_alloc_size_t) + (comp->max_output_ptr * png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_alloc_size_t)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* Continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* Finish the compression */ + do + { + /* Tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_alloc_size_t)(comp->max_output_ptr * + png_sizeof(png_charp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_alloc_size_t)(comp->max_output_ptr * + png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_alloc_size_t)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* We got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* Ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* Handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* Write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i], + (png_size_t)png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + /* Write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + + /* Reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ + PNG_IHDR; + int ret; + + png_byte buf[13]; /* Buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); + + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr, + "Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* Save the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* Set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* Pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* Write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* Initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + if (ret != Z_OK) + { + if (ret == Z_VERSION_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- version error"); + if (ret == Z_STREAM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- stream error"); + if (ret == Z_MEM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- mem error"); + png_error(png_ptr, "zlib failed to initialize compressor"); + } + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* Set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* Write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ + PNG_PLTE; + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + + if (( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, + (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this + * instead + */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* Write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ + PNG_IDAT; + + png_debug(1, "in png_write_IDAT"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. + * + * The conditions below are practically always satisfied; + * however, they still must be checked. + */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* Write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ + PNG_IEND; + + png_debug(1, "in png_write_IEND"); + + png_write_chunk(png_ptr, (png_bytep)png_IEND, NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ + PNG_gAMA; + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ + PNG_gAMA; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ + PNG_sRGB; + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ + PNG_iCCP; + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile + 1))<<16) | + ((*( (png_bytep)profile + 2))<< 8) | + ((*( (png_bytep)profile + 3)) ); + + if (embedded_profile_len < 0) + { + png_warning(png_ptr, + "Embedded profile length in iCCP chunk is negative"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, + (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp); + + /* Make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)(name_len + profile_len + 2)); + new_name[name_len + 1] = 0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 2)); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ + PNG_sPLT; + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); + png_size_t palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifndef PNG_POINTER_INDEXING_SUPPORTED + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + + if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0) + return; + + /* Make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ + PNG_sBIT; + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ + PNG_cHRM; + png_byte buf[32]; + + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, + int_green_x, int_green_y, int_blue_x, int_blue_y; + + png_debug(1, "in png_write_cHRM"); + + int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5); + int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5); + int_red_x = (png_uint_32)(red_x * 100000.0 + 0.5); + int_red_y = (png_uint_32)(red_y * 100000.0 + 0.5); + int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5); + int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5); + int_blue_x = (png_uint_32)(blue_x * 100000.0 + 0.5); + int_blue_y = (png_uint_32)(blue_y * 100000.0 + 0.5); + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y, + int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y)) +#endif + { + /* Each value is saved in 1/100,000ths */ + + png_save_uint_32(buf, int_white_x); + png_save_uint_32(buf + 4, int_white_y); + + png_save_uint_32(buf + 8, int_red_x); + png_save_uint_32(buf + 12, int_red_y); + + png_save_uint_32(buf + 16, int_green_x); + png_save_uint_32(buf + 20, int_green_y); + + png_save_uint_32(buf + 24, int_blue_x); + png_save_uint_32(buf + 28, int_blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ + PNG_cHRM; + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + + /* Each value is saved in 1/100,000ths */ +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, + green_x, green_y, blue_x, blue_y)) +#endif + { + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +/* Write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans_alpha, png_color_16p tran, + int num_trans, int color_type) +{ + PNG_tRNS; + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid number of transparent colors specified"); + return; + } + /* Write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans_alpha, + (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* One 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* Three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ + PNG_bKGD; + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ + PNG_hIST; + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, + (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword"); + + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#ifdef PNG_STDIO_SUPPORTED + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if (kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + (*new_key)[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ + PNG_tEXt; + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt"); + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* Make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, + (png_uint_32)(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ + PNG_zTXt; + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_free(png_ptr, new_key); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* Write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, + (png_uint_32)(key_len+text_len + 2)); + /* Write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* Write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* Write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* Close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ + PNG_iTXt; + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang; + png_charp new_key = NULL; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* Make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + + /* Set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* Set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), + (png_size_t)(lang_len + 1)); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), + (png_size_t)(lang_key_len + 1)); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + png_free(png_ptr, new_lang); +} +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ + PNG_oFFs; + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ + PNG_pCAL; + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, + (png_alloc_size_t)(nparams * png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long) params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, + (png_size_t)purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ + PNG_sCAL; + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL"); + + buf[0] = (char)unit; + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ + PNG_sCAL; + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ + PNG_pHYs; + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ + PNG_tIME; + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* Initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row"); + + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1); + + /* Set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)(png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* Set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, + (png_alloc_size_t)buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_size_t)(png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_alloc_size_t)(png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_size_t)(png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int ret; + + png_debug(1, "in png_write_finish_row"); + + /* Next row */ + png_ptr->row_number++; + + /* See if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* Loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* Reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + return; + } + } +#endif + + /* If we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* Tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* Check for an error */ + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ + if (pass < 6) + { + /* Each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* Start at the beginning */ + dp = row; + /* Find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* Loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* Find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* Move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* Next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* Set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter"); + +#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) + { + /* These will never be selected so we need not test them. */ + filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); + } +#endif + + /* Find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifdef PNG_WRITE_FILTER_SUPPORTED + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* Sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* It's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* Up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* Avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_WRITE_FILTER_SUPPORTED */ +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row"); + + png_debug1(2, "filter = %d", filtered_row[0]); + /* Set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* Repeat until we have compressed all the data */ + do + { + int ret; /* Return of zlib */ + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* See if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* Repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* Swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* Finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/lib/png/src/pngwutil.d b/lib/png/src/pngwutil.d new file mode 100644 index 0000000..672a440 --- /dev/null +++ b/lib/png/src/pngwutil.d @@ -0,0 +1,12 @@ +pngwutil.o: pngwutil.c png.h ../../zlib/include/zlib.h \ + ../../zlib/include/zconf.h pngconf.h pngpriv.h + +png.h: + +../../zlib/include/zlib.h: + +../../zlib/include/zconf.h: + +pngconf.h: + +pngpriv.h: diff --git a/lib/readme.txt b/lib/readme.txt new file mode 100644 index 0000000..db5d275 --- /dev/null +++ b/lib/readme.txt @@ -0,0 +1,19 @@ + +libfat: 1.0.5 + frag +libntfs: 3g-2010.8.8 + frag + +jpeg: 8a +png: 1.2.34 + +libfat/lib: + +libfat.a - devkit 17, -O2 +libfat17.a - devkit 17, -Os +libfat22.a - devkit 22, -Os + +libntfs_new/lib: + +libntfs.a - devkit 17, -O3 +libntfs17.a - devkit 17, -Os +libntfs22.a - devkit 22, -Os + diff --git a/lib/zlib/include/zconf.h b/lib/zlib/include/zconf.h new file mode 100644 index 0000000..b234387 --- /dev/null +++ b/lib/zlib/include/zconf.h @@ -0,0 +1,428 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef STDC +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/lib/zlib/include/zlib.h b/lib/zlib/include/zlib.h new file mode 100644 index 0000000..bfbba83 --- /dev/null +++ b/lib/zlib/include/zlib.h @@ -0,0 +1,1613 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.5, April 19th, 2010 + + Copyright (C) 1995-2010 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 + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.5" +#define ZLIB_VERNUM 0x1250 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all the uncompressed data. (The size + of the uncompressed data may have been saved by the compressor for this + purpose.) The next operation on this stream must be inflateEnd to deallocate + the decompression state. The use of Z_FINISH is never required, but can be + used to inform inflate that a faster approach may be used for the single + inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK or Z_TREES is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been + found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the + success case, the application may save the current current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef voidp gzFile; /* opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) Also "a" + can be used instead of "w" to request that the gzip stream that will be + written be appended to the file. "+" will result in an error, since reading + and writing to the same gzip file is not supported. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file was not in gzip format, gzread copies the given number of + bytes into the buffer. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream, or failing that, reading the rest + of the input file directly without decompression. The entire input file + will be read if gzread is called until it returns less than the requested + len. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. This state can change from + false to true while reading the input file if the end of a gzip stream is + reached, but is followed by data that is not another gzip stream. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the for the crc. Pre- and post-conditioning (one's + complement) is performed within this function so it shouldn't be done by the + application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# ifdef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/lib/zlib/src/Makefile b/lib/zlib/src/Makefile new file mode 100644 index 0000000..cdf1194 --- /dev/null +++ b/lib/zlib/src/Makefile @@ -0,0 +1,50 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +include $(DEVKITPPC)/wii_rules + +DEPSDIR := . + +#PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +#CC := $(PREFIX)gcc +#AR := $(PREFIX)ar + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../include +DEST_LIB := ../lib$(DKV) + +INCLUDE := +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -Os -Wall $(MACHDEP) $(INCLUDE) + +LIB := zlib +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +DEPENDS := $(OFILES:.o=.d) +ARC := libz.a +HDR := $(LIB).h zconf.h + +#all : $(OFILES) +# $(AR) -r $(ARC) $(OFILES) + +all : $(ARC) + +$(ARC) : $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) $(DEPENDS) + +install : + mkdir -p $(DEST_LIB) $(DEST_INC) + cp -f $(ARC) $(DEST_LIB)/ + cp -f $(HDR) $(DEST_INC)/ + +#%.o : %.c +# $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/zlib/src/Makefile-orig b/lib/zlib/src/Makefile-orig new file mode 100644 index 0000000..b4411e1 --- /dev/null +++ b/lib/zlib/src/Makefile-orig @@ -0,0 +1,40 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +CC := $(PREFIX)gcc +AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii + +INCLUDE := +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -O2 -Wall $(MACHDEP) $(INCLUDE) + +LIB := zlib +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +ARC := libz.a +HDR := $(LIB).h zconf.h + +all : $(OFILES) + $(AR) -r $(ARC) $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) + +install : + mkdir -p $(LIBOGC_LIB) $(LIBOGC_INC) + cp -f $(ARC) $(LIBOGC_LIB)/ + cp -f $(HDR) $(LIBOGC_INC)/ + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/zlib/src/adler32.c b/lib/zlib/src/adler32.c new file mode 100644 index 0000000..65ad6a5 --- /dev/null +++ b/lib/zlib/src/adler32.c @@ -0,0 +1,169 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2007 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/lib/zlib/src/adler32.d b/lib/zlib/src/adler32.d new file mode 100644 index 0000000..d62a32a --- /dev/null +++ b/lib/zlib/src/adler32.d @@ -0,0 +1,7 @@ +adler32.o: adler32.c zutil.h zlib.h zconf.h + +zutil.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/compress.c b/lib/zlib/src/compress.c new file mode 100644 index 0000000..ea4dfbe --- /dev/null +++ b/lib/zlib/src/compress.c @@ -0,0 +1,80 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/lib/zlib/src/compress.d b/lib/zlib/src/compress.d new file mode 100644 index 0000000..5d81866 --- /dev/null +++ b/lib/zlib/src/compress.d @@ -0,0 +1,5 @@ +compress.o: compress.c zlib.h zconf.h + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/crc32.c b/lib/zlib/src/crc32.c new file mode 100644 index 0000000..91be372 --- /dev/null +++ b/lib/zlib/src/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/lib/zlib/src/crc32.d b/lib/zlib/src/crc32.d new file mode 100644 index 0000000..5815eb1 --- /dev/null +++ b/lib/zlib/src/crc32.d @@ -0,0 +1,9 @@ +crc32.o: crc32.c zutil.h zlib.h zconf.h crc32.h + +zutil.h: + +zlib.h: + +zconf.h: + +crc32.h: diff --git a/lib/zlib/src/crc32.h b/lib/zlib/src/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/lib/zlib/src/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/lib/zlib/src/deflate.c b/lib/zlib/src/deflate.c new file mode 100644 index 0000000..5c4022f --- /dev/null +++ b/lib/zlib/src/deflate.c @@ -0,0 +1,1834 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > s->w_size) { + length = s->w_size; + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/lib/zlib/src/deflate.d b/lib/zlib/src/deflate.d new file mode 100644 index 0000000..60eed44 --- /dev/null +++ b/lib/zlib/src/deflate.d @@ -0,0 +1,9 @@ +deflate.o: deflate.c deflate.h zutil.h zlib.h zconf.h + +deflate.h: + +zutil.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/deflate.h b/lib/zlib/src/deflate.h new file mode 100644 index 0000000..cbf0d1e --- /dev/null +++ b/lib/zlib/src/deflate.h @@ -0,0 +1,342 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2010 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/lib/zlib/src/gzclose.c b/lib/zlib/src/gzclose.c new file mode 100644 index 0000000..caeb99a --- /dev/null +++ b/lib/zlib/src/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/lib/zlib/src/gzclose.d b/lib/zlib/src/gzclose.d new file mode 100644 index 0000000..02c1e13 --- /dev/null +++ b/lib/zlib/src/gzclose.d @@ -0,0 +1,7 @@ +gzclose.o: gzclose.c gzguts.h zlib.h zconf.h + +gzguts.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/gzguts.h b/lib/zlib/src/gzguts.h new file mode 100644 index 0000000..0f8fb79 --- /dev/null +++ b/lib/zlib/src/gzguts.h @@ -0,0 +1,132 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifdef _MSC_VER +# include +# define vsnprintf _vsnprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifdef STDC +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default i/o buffer size -- double this for output when reading */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + z_off64_t pos; /* current position in uncompressed data */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + /* just for reading */ + unsigned have; /* amount of output data unused at next */ + int eof; /* true if end of input file reached */ + z_off64_t start; /* where the gzip data started, for rewinding */ + z_off64_t raw; /* where the raw data started, for seeking */ + int how; /* 0: get header, 1: copy, 2: decompress */ + int direct; /* true if last read direct, false if gzip */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/lib/zlib/src/gzlib.c b/lib/zlib/src/gzlib.c new file mode 100644 index 0000000..603e60e --- /dev/null +++ b/lib/zlib/src/gzlib.c @@ -0,0 +1,537 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const char *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + if (state->mode == GZ_READ) { /* for reading ... */ + state->have = 0; /* no output data available */ + state->eof = 0; /* not at end of file */ + state->how = LOOK; /* look for gzip header */ + state->direct = 1; /* default for empty file */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const char *path; + int fd; + const char *mode; +{ + gz_statep state; + + /* allocate gzFile structure to return */ + state = malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* save the path name for error messages */ + state->path = malloc(strlen(path) + 1); + if (state->path == NULL) { + free(state); + return NULL; + } + strcpy(state->path, path); + + /* open the file with the appropriate mode (or just use fd) */ + state->fd = fd != -1 ? fd : + open(path, +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | ( + state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))), + 0666); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; + sprintf(path, "", fd); /* for debugging */ + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size == 0) + return -1; + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->pos + offset >= state->raw) { + ret = LSEEK(state->fd, offset - state->have, SEEK_CUR); + if (ret == -1) + return -1; + state->have = 0; + state->eof = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->pos += offset; + return state->pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? + (unsigned)offset : state->have; + state->have -= n; + state->next += n; + state->pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? + (state->eof && state->strm.avail_in == 0 && state->have == 0) : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->msg == NULL ? "" : state->msg; +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) + state->eof = 0; + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, save as static string */ + if (err == Z_MEM_ERROR) { + state->msg = (char *)msg; + return; + } + + /* construct error message with path */ + if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { + state->err = Z_MEM_ERROR; + state->msg = (char *)"out of memory"; + return; + } + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/lib/zlib/src/gzlib.d b/lib/zlib/src/gzlib.d new file mode 100644 index 0000000..ce0171d --- /dev/null +++ b/lib/zlib/src/gzlib.d @@ -0,0 +1,7 @@ +gzlib.o: gzlib.c gzguts.h zlib.h zconf.h + +gzguts.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/gzread.c b/lib/zlib/src/gzread.c new file mode 100644 index 0000000..548201a --- /dev/null +++ b/lib/zlib/src/gzread.c @@ -0,0 +1,653 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_next4 OF((gz_statep, unsigned long *)); +local int gz_head OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_make OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + gz_avail() assumes that strm->avail_in == 0. */ +local int gz_avail(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->err != Z_OK) + return -1; + if (state->eof == 0) { + if (gz_load(state, state->in, state->size, + (unsigned *)&(strm->avail_in)) == -1) + return -1; + strm->next_in = state->in; + } + return 0; +} + +/* Get next byte from input, or -1 if end or error. */ +#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \ + (strm->avail_in == 0 ? -1 : \ + (strm->avail_in--, *(strm->next_in)++))) + +/* Get a four-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned and *ret is not modified. */ +local int gz_next4(state, ret) + gz_statep state; + unsigned long *ret; +{ + int ch; + unsigned long val; + z_streamp strm = &(state->strm); + + val = NEXT(); + val += (unsigned)NEXT() << 8; + val += (unsigned long)NEXT() << 16; + ch = NEXT(); + if (ch == -1) + return -1; + val += (unsigned long)ch << 24; + *ret = val; + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->have must be zero. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression, and the gzip header will be skipped so + that the next available input data is the raw deflate stream. If direct + copying, then leftover input data from the input buffer will be copied to + the output buffer. In that case, all further file reads will be directly to + either the output buffer or a user buffer. If decompressing, the inflate + state and the check value will be initialized. gz_head() will return 0 on + success or -1 on failure. Failures may include read errors or gzip header + errors. */ +local int gz_head(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + int flags; + unsigned len; + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get some data in the input buffer */ + if (strm->avail_in == 0) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for the gzip magic header bytes 31 and 139 */ + if (strm->next_in[0] == 31) { + strm->avail_in--; + strm->next_in++; + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in && strm->next_in[0] == 139) { + /* we have a gzip header, woo hoo! */ + strm->avail_in--; + strm->next_in++; + + /* skip rest of header */ + if (NEXT() != 8) { /* compression method */ + gz_error(state, Z_DATA_ERROR, "unknown compression method"); + return -1; + } + flags = NEXT(); + if (flags & 0xe0) { /* reserved flag bits */ + gz_error(state, Z_DATA_ERROR, "unknown header flags set"); + return -1; + } + NEXT(); /* modification time */ + NEXT(); + NEXT(); + NEXT(); + NEXT(); /* extra flags */ + NEXT(); /* operating system */ + if (flags & 4) { /* extra field */ + len = (unsigned)NEXT(); + len += (unsigned)NEXT() << 8; + while (len--) + if (NEXT() < 0) + break; + } + if (flags & 8) /* file name */ + while (NEXT() > 0) + ; + if (flags & 16) /* comment */ + while (NEXT() > 0) + ; + if (flags & 2) { /* header crc */ + NEXT(); + NEXT(); + } + /* an unexpected end of file is not checked for here -- it will be + noticed on the first request for uncompressed data */ + + /* set up for decompression */ + inflateReset(strm); + strm->adler = crc32(0L, Z_NULL, 0); + state->how = GZIP; + state->direct = 0; + return 0; + } + else { + /* not a gzip file -- save first byte (31) and fall to raw i/o */ + state->out[0] = 31; + state->have = 1; + } + } + + /* doing raw i/o, save start of raw data for seeking, copy any leftover + input to output -- this assumes that the output buffer is larger than + the input buffer, which also assures space for gzungetc() */ + state->raw = state->pos; + state->next = state->out; + if (strm->avail_in) { + memcpy(state->next + state->have, strm->next_in, strm->avail_in); + state->have += strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + If the end of the compressed data is reached, then verify the gzip trailer + check value and length (modulo 2^32). state->have and state->next are set + to point to the just decompressed data, and the crc is updated. If the + trailer is verified, state->how is reset to LOOK to look for the next gzip + stream or raw data, once state->have is depleted. Returns 0 on success, -1 + on failure. Failures may include invalid compressed data or a failed gzip + trailer verification. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret; + unsigned had; + unsigned long crc, len; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output and crc check value */ + state->have = had - strm->avail_out; + state->next = strm->next_out - state->have; + strm->adler = crc32(strm->adler, state->next, state->have); + + /* check gzip trailer if at end of deflate stream */ + if (ret == Z_STREAM_END) { + if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + if (crc != strm->adler) { + gz_error(state, Z_DATA_ERROR, "incorrect data check"); + return -1; + } + if (len != (strm->total_out & 0xffffffffL)) { + gz_error(state, Z_DATA_ERROR, "incorrect length check"); + return -1; + } + state->how = LOOK; /* ready for next stream, once have is 0 (leave + state->direct unchanged to remember how) */ + } + + /* good decompression */ + return 0; +} + +/* Make data and put in the output buffer. Assumes that state->have == 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for (and skipped if found) to determine wither to copy or decompress. + Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY + or GZIP unless the end of the input file has been reached and all data has + been processed. */ +local int gz_make(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->how == LOOK) { /* look for gzip header */ + if (gz_head(state) == -1) + return -1; + if (state->have) /* got some data from gz_head() */ + return 0; + } + if (state->how == COPY) { /* straight copy */ + if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1) + return -1; + state->next = state->out; + } + else if (state->how == GZIP) { /* decompress */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->have) { + n = GT_OFF(state->have) || (z_off64_t)state->have > len ? + (unsigned)len : state->have; + state->have -= n; + state->next += n; + state->pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->have) { + n = state->have > len ? len : state->have; + memcpy(buf, state->next, n); + state->next += n; + state->have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) + break; + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + continue; /* no progress yet -- go back to memcpy() above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = buf; + if (gz_decomp(state) == -1) + return -1; + n = state->have; + state->have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->have) { + state->have--; + state->pos++; + return *(state->next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->have == 0) { + state->have = 1; + state->next = state->out + (state->size << 1) - 1; + state->next[0] = c; + state->pos--; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->have == (state->size << 1)) { + gz_error(state, Z_BUF_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->next == state->out) { + unsigned char *src = state->out + state->have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->next = dest; + } + state->have++; + state->next--; + state->next[0] = c; + state->pos--; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->have == 0) { + if (gz_make(state) == -1) + return NULL; /* error */ + if (state->have == 0) { /* end of file */ + if (buf == str) /* got bupkus */ + return NULL; + break; /* got something -- return it */ + } + } + + /* look for end-of-line in current output buffer */ + n = state->have > left ? left : state->have; + eol = memchr(state->next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->next, n); + state->have -= n; + state->next += n; + state->pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* found end-of-line or out of space -- terminate string and return it */ + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return 0; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->how == LOOK && state->have == 0) + (void)gz_head(state); + + /* return 1 if reading direct, 0 if decompressing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/lib/zlib/src/gzread.d b/lib/zlib/src/gzread.d new file mode 100644 index 0000000..1611431 --- /dev/null +++ b/lib/zlib/src/gzread.d @@ -0,0 +1,7 @@ +gzread.o: gzread.c gzguts.h zlib.h zconf.h + +gzguts.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/gzwrite.c b/lib/zlib/src/gzwrite.c new file mode 100644 index 0000000..e8defc6 --- /dev/null +++ b/lib/zlib/src/gzwrite.c @@ -0,0 +1,531 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input and output buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + 15 + 16, 8, state->strategy); + if (ret != Z_OK) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer */ + strm->avail_out = state->size; + strm->next_out = state->out; + state->next = strm->next_out; + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->next); + if (have && ((got = write(state->fd, state->next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + unsigned n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + if (strm->avail_in == 0) + strm->next_in = state->in; + n = state->size - strm->avail_in; + if (n > len) + n = len; + memcpy(strm->next_in + strm->avail_in, buf, n); + strm->avail_in += n; + state->pos += n; + buf = (char *)buf + n; + len -= n; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (voidp)buf; + state->pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (strm->avail_in < state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + strm->next_in[strm->avail_in++] = c; + state->pos++; + return c; + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#ifdef STDC +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) +{ + int size, len; + gz_statep state; + z_streamp strm; + va_list va; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(state->in, format, va); + va_end(va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf(state->in, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(state->in, size, format, va); + va_end(va); + len = strlen(state->in); +# else + len = vsnprintf((char *)(state->in), size, format, va); + va_end(va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#else /* !STDC */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(state->in); +# else + len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = 0; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + ret += gz_zero(state, state->skip); + } + + /* flush, free memory, and close file */ + ret += gz_comp(state, Z_FINISH); + (void)deflateEnd(&(state->strm)); + free(state->out); + free(state->in); + gz_error(state, Z_OK, NULL); + free(state->path); + ret += close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/lib/zlib/src/gzwrite.d b/lib/zlib/src/gzwrite.d new file mode 100644 index 0000000..acd6a88 --- /dev/null +++ b/lib/zlib/src/gzwrite.d @@ -0,0 +1,7 @@ +gzwrite.o: gzwrite.c gzguts.h zlib.h zconf.h + +gzguts.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/infback.c b/lib/zlib/src/infback.c new file mode 100644 index 0000000..af3a8c9 --- /dev/null +++ b/lib/zlib/src/infback.c @@ -0,0 +1,632 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/lib/zlib/src/infback.d b/lib/zlib/src/infback.d new file mode 100644 index 0000000..ac6d88e --- /dev/null +++ b/lib/zlib/src/infback.d @@ -0,0 +1,16 @@ +infback.o: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +zutil.h: + +zlib.h: + +zconf.h: + +inftrees.h: + +inflate.h: + +inffast.h: + +inffixed.h: diff --git a/lib/zlib/src/inffast.c b/lib/zlib/src/inffast.c new file mode 100644 index 0000000..2f1d60b --- /dev/null +++ b/lib/zlib/src/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/lib/zlib/src/inffast.d b/lib/zlib/src/inffast.d new file mode 100644 index 0000000..19a221c --- /dev/null +++ b/lib/zlib/src/inffast.d @@ -0,0 +1,14 @@ +inffast.o: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h + +zutil.h: + +zlib.h: + +zconf.h: + +inftrees.h: + +inflate.h: + +inffast.h: diff --git a/lib/zlib/src/inffast.h b/lib/zlib/src/inffast.h new file mode 100644 index 0000000..e5c1aa4 --- /dev/null +++ b/lib/zlib/src/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/lib/zlib/src/inffixed.h b/lib/zlib/src/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/lib/zlib/src/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/lib/zlib/src/inflate.c b/lib/zlib/src/inflate.c new file mode 100644 index 0000000..a8431ab --- /dev/null +++ b/lib/zlib/src/inflate.c @@ -0,0 +1,1480 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/lib/zlib/src/inflate.d b/lib/zlib/src/inflate.d new file mode 100644 index 0000000..8180bd7 --- /dev/null +++ b/lib/zlib/src/inflate.d @@ -0,0 +1,16 @@ +inflate.o: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ + inffast.h inffixed.h + +zutil.h: + +zlib.h: + +zconf.h: + +inftrees.h: + +inflate.h: + +inffast.h: + +inffixed.h: diff --git a/lib/zlib/src/inflate.h b/lib/zlib/src/inflate.h new file mode 100644 index 0000000..95f4986 --- /dev/null +++ b/lib/zlib/src/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/lib/zlib/src/inftrees.c b/lib/zlib/src/inftrees.c new file mode 100644 index 0000000..11e9c52 --- /dev/null +++ b/lib/zlib/src/inftrees.c @@ -0,0 +1,330 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + here.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = here; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/lib/zlib/src/inftrees.d b/lib/zlib/src/inftrees.d new file mode 100644 index 0000000..669bfbf --- /dev/null +++ b/lib/zlib/src/inftrees.d @@ -0,0 +1,9 @@ +inftrees.o: inftrees.c zutil.h zlib.h zconf.h inftrees.h + +zutil.h: + +zlib.h: + +zconf.h: + +inftrees.h: diff --git a/lib/zlib/src/inftrees.h b/lib/zlib/src/inftrees.h new file mode 100644 index 0000000..baa53a0 --- /dev/null +++ b/lib/zlib/src/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/lib/zlib/src/trees.c b/lib/zlib/src/trees.c new file mode 100644 index 0000000..56e9bb1 --- /dev/null +++ b/lib/zlib/src/trees.c @@ -0,0 +1,1244 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2010 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/lib/zlib/src/trees.d b/lib/zlib/src/trees.d new file mode 100644 index 0000000..5f7e3a8 --- /dev/null +++ b/lib/zlib/src/trees.d @@ -0,0 +1,11 @@ +trees.o: trees.c deflate.h zutil.h zlib.h zconf.h trees.h + +deflate.h: + +zutil.h: + +zlib.h: + +zconf.h: + +trees.h: diff --git a/lib/zlib/src/trees.h b/lib/zlib/src/trees.h new file mode 100644 index 0000000..d35639d --- /dev/null +++ b/lib/zlib/src/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/lib/zlib/src/uncompr.c b/lib/zlib/src/uncompr.c new file mode 100644 index 0000000..ad98be3 --- /dev/null +++ b/lib/zlib/src/uncompr.c @@ -0,0 +1,59 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/lib/zlib/src/uncompr.d b/lib/zlib/src/uncompr.d new file mode 100644 index 0000000..3c008c2 --- /dev/null +++ b/lib/zlib/src/uncompr.d @@ -0,0 +1,5 @@ +uncompr.o: uncompr.c zlib.h zconf.h + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/zconf.h b/lib/zlib/src/zconf.h new file mode 100644 index 0000000..b234387 --- /dev/null +++ b/lib/zlib/src/zconf.h @@ -0,0 +1,428 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef STDC +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/lib/zlib/src/zlib.h b/lib/zlib/src/zlib.h new file mode 100644 index 0000000..bfbba83 --- /dev/null +++ b/lib/zlib/src/zlib.h @@ -0,0 +1,1613 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.5, April 19th, 2010 + + Copyright (C) 1995-2010 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 + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.5" +#define ZLIB_VERNUM 0x1250 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all the uncompressed data. (The size + of the uncompressed data may have been saved by the compressor for this + purpose.) The next operation on this stream must be inflateEnd to deallocate + the decompression state. The use of Z_FINISH is never required, but can be + used to inform inflate that a faster approach may be used for the single + inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK or Z_TREES is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been + found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the + success case, the application may save the current current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef voidp gzFile; /* opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) Also "a" + can be used instead of "w" to request that the gzip stream that will be + written be appended to the file. "+" will result in an error, since reading + and writing to the same gzip file is not supported. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file was not in gzip format, gzread copies the given number of + bytes into the buffer. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream, or failing that, reading the rest + of the input file directly without decompression. The entire input file + will be read if gzread is called until it returns less than the requested + len. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. This state can change from + false to true while reading the input file if the end of a gzip stream is + reached, but is followed by data that is not another gzip stream. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the for the crc. Pre- and post-conditioning (one's + complement) is performed within this function so it shouldn't be done by the + application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# ifdef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/lib/zlib/src/zutil.c b/lib/zlib/src/zutil.c new file mode 100644 index 0000000..898ed34 --- /dev/null +++ b/lib/zlib/src/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/lib/zlib/src/zutil.d b/lib/zlib/src/zutil.d new file mode 100644 index 0000000..5cfc5e9 --- /dev/null +++ b/lib/zlib/src/zutil.d @@ -0,0 +1,7 @@ +zutil.o: zutil.c zutil.h zlib.h zconf.h + +zutil.h: + +zlib.h: + +zconf.h: diff --git a/lib/zlib/src/zutil.h b/lib/zlib/src/zutil.h new file mode 100644 index 0000000..258fa88 --- /dev/null +++ b/lib/zlib/src/zutil.h @@ -0,0 +1,274 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#ifdef STDC +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); +void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/plugins/mighty/Channel Emu Plugin.pnproj b/plugins/mighty/Channel Emu Plugin.pnproj new file mode 100644 index 0000000..c812a2a --- /dev/null +++ b/plugins/mighty/Channel Emu Plugin.pnproj @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/mighty/Channel Emu Plugin.pnps b/plugins/mighty/Channel Emu Plugin.pnps new file mode 100644 index 0000000..b342efc --- /dev/null +++ b/plugins/mighty/Channel Emu Plugin.pnps @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/mighty/Makefile b/plugins/mighty/Makefile new file mode 100644 index 0000000..3d2c1ae --- /dev/null +++ b/plugins/mighty/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source source/codes source/libpng/pngu +DATA := data +INCLUDES := source + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) -DPAL +CXXFLAGS = $(CFLAGS) + +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map -Wl,--section-start,.init=0x80dfff00 + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -ldi -lwiiuse -lbte -lasnd -lfat -logc -lm -lmad + + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(DEVKITPPC)/lib $(CURDIR) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.png.o : %.png +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/plugins/mighty/source/codes/codehandler.h b/plugins/mighty/source/codes/codehandler.h new file mode 100644 index 0000000..a5a80be --- /dev/null +++ b/plugins/mighty/source/codes/codehandler.h @@ -0,0 +1,277 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +const unsigned char codehandler[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x27, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, + 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, + 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, + 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, + 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, + 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, + 0x48, 0x00, 0x06, 0x55, 0x3a, 0xa0, 0x00, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcd, 0x00, 0x63, 0xf2, 0x27, 0x74, 0x80, 0x01, 0x00, 0xac, 0x90, 0x12, 0x00, 0x04, + 0x92, 0xb8, 0x64, 0x3c, 0x48, 0x00, 0x04, 0x2d, 0x41, 0x82, 0x05, 0xa4, 0x2c, 0x1d, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x10, 0x2c, 0x1d, 0x00, 0x01, 0x41, 0x80, 0x05, 0x94, 0x48, 0x00, 0x03, 0x4c, + 0x41, 0x82, 0x04, 0xf0, 0x2c, 0x1d, 0x00, 0x06, 0x41, 0x82, 0x00, 0x8c, 0x2c, 0x1d, 0x00, 0x07, + 0x41, 0x82, 0x03, 0x30, 0x2c, 0x1d, 0x00, 0x08, 0x41, 0x82, 0x05, 0x80, 0x2c, 0x1d, 0x00, 0x09, + 0x41, 0x82, 0x00, 0xa0, 0x2c, 0x1d, 0x00, 0x10, 0x41, 0x82, 0x00, 0x98, 0x2c, 0x1d, 0x00, 0x2f, + 0x41, 0x82, 0x00, 0x70, 0x2c, 0x1d, 0x00, 0x30, 0x41, 0x82, 0x00, 0x78, 0x2c, 0x1d, 0x00, 0x38, + 0x41, 0x82, 0x05, 0x28, 0x2c, 0x1d, 0x00, 0x40, 0x41, 0x82, 0x03, 0x40, 0x2c, 0x1d, 0x00, 0x41, + 0x41, 0x82, 0x03, 0x58, 0x2c, 0x1d, 0x00, 0x44, 0x41, 0x82, 0x00, 0x68, 0x2c, 0x1d, 0x00, 0x50, + 0x41, 0x82, 0x00, 0x20, 0x2c, 0x1d, 0x00, 0x60, 0x41, 0x82, 0x00, 0x24, 0x2c, 0x1d, 0x00, 0x89, + 0x41, 0x82, 0x00, 0x50, 0x2c, 0x1d, 0x00, 0x99, 0x41, 0x82, 0x05, 0x0c, 0x48, 0x00, 0x05, 0x10, + 0x80, 0x72, 0x00, 0x00, 0x48, 0x00, 0x04, 0x29, 0x48, 0x00, 0x05, 0x04, 0x48, 0x00, 0x05, 0x89, + 0x48, 0x00, 0x04, 0xfc, 0x38, 0x80, 0x00, 0x01, 0x90, 0x92, 0x00, 0x00, 0x48, 0x00, 0x04, 0xf0, + 0x48, 0x00, 0x04, 0x09, 0x3a, 0x00, 0x00, 0xa0, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0x14, + 0x38, 0x60, 0x01, 0x20, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0xc9, 0x48, 0x00, 0x04, 0xd0, + 0x2f, 0x1d, 0x00, 0x10, 0x2e, 0x9d, 0x00, 0x44, 0x63, 0xe4, 0x1a, 0xb4, 0x3c, 0x60, 0x80, 0x00, + 0x60, 0x63, 0x03, 0x00, 0x48, 0x00, 0x05, 0x09, 0x38, 0x63, 0x0a, 0x00, 0x48, 0x00, 0x05, 0x01, + 0x38, 0x63, 0x06, 0x00, 0x48, 0x00, 0x04, 0xf9, 0x63, 0xec, 0x27, 0x88, 0x92, 0xac, 0x00, 0x00, + 0x92, 0xac, 0x00, 0x04, 0x92, 0xac, 0x00, 0x08, 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, + 0x80, 0x72, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x02, 0x40, 0x82, 0x00, 0x0c, 0x41, 0x96, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x20, 0x38, 0x60, 0x00, 0x00, 0x90, 0x6c, 0x00, 0x0c, 0x40, 0x82, 0x00, 0x14, + 0x40, 0x96, 0x00, 0x10, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x02, 0x14, + 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, 0x41, 0x96, 0x04, 0x54, 0x41, 0x9a, 0x00, 0x08, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x03, 0x09, 0x40, 0x99, 0x00, 0x10, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x02, 0xf9, 0x63, 0xe4, 0x27, 0x88, + 0x80, 0x64, 0x00, 0x00, 0x80, 0x84, 0x00, 0x04, 0x7c, 0x72, 0xfb, 0xa6, 0x7c, 0x95, 0xfb, 0xa6, + 0x48, 0x00, 0x04, 0x1c, 0x7c, 0x32, 0x43, 0xa6, 0x7c, 0x3a, 0x02, 0xa6, 0x7c, 0x73, 0x43, 0xa6, + 0x7c, 0x7b, 0x02, 0xa6, 0x54, 0x63, 0x05, 0xa8, 0x90, 0x60, 0x27, 0xb0, 0x54, 0x63, 0x06, 0x1e, + 0x60, 0x63, 0x20, 0x00, 0x7c, 0x7b, 0x03, 0xa6, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x1a, 0xe8, + 0x7c, 0x7a, 0x03, 0xa6, 0x4c, 0x00, 0x00, 0x64, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x27, 0x98, + 0x90, 0x23, 0x00, 0x14, 0x7c, 0x61, 0x1b, 0x78, 0x7c, 0x73, 0x42, 0xa6, 0xbc, 0x41, 0x00, 0x24, + 0x7c, 0x24, 0x0b, 0x78, 0x7c, 0x32, 0x42, 0xa6, 0x90, 0x04, 0x00, 0x1c, 0x90, 0x24, 0x00, 0x20, + 0x7c, 0x68, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x9c, 0x7c, 0x60, 0x00, 0x26, 0x90, 0x64, 0x00, 0x00, + 0x7c, 0x61, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x04, 0x7c, 0x69, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x08, + 0x7c, 0x72, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x0c, 0x7c, 0x73, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x10, + 0x39, 0x20, 0x00, 0x00, 0x7d, 0x32, 0xfb, 0xa6, 0x7d, 0x35, 0xfb, 0xa6, 0x3c, 0xa0, 0x80, 0x00, + 0x60, 0xa5, 0x1b, 0x70, 0x3f, 0xe0, 0xd0, 0x04, 0x63, 0xff, 0x00, 0xa0, 0x93, 0xe5, 0x00, 0x00, + 0x7c, 0x00, 0x28, 0x6c, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, 0x4c, 0x00, 0x01, 0x2c, + 0xd0, 0x04, 0x00, 0xa0, 0x3b, 0xff, 0x00, 0x04, 0x3f, 0xff, 0x00, 0x20, 0x57, 0xf0, 0x01, 0x4b, + 0x41, 0x82, 0xff, 0xdc, 0x3f, 0xe0, 0x80, 0x00, 0x63, 0xe5, 0x27, 0x88, 0x82, 0x05, 0x00, 0x00, + 0x82, 0x25, 0x00, 0x04, 0x82, 0x65, 0x00, 0x0c, 0x2c, 0x13, 0x00, 0x00, 0x41, 0x82, 0x00, 0x74, + 0x2c, 0x13, 0x00, 0x02, 0x40, 0x82, 0x00, 0x18, 0x81, 0x24, 0x00, 0x14, 0x39, 0x33, 0x00, 0x03, + 0x91, 0x25, 0x00, 0x00, 0x91, 0x25, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x6c, 0x7c, 0x10, 0x98, 0x00, + 0x41, 0x82, 0x00, 0x38, 0x7c, 0x11, 0x98, 0x00, 0x41, 0x82, 0x00, 0x30, 0x7d, 0x30, 0x8a, 0x14, + 0x91, 0x25, 0x00, 0x0c, 0x82, 0x05, 0x00, 0x08, 0x2c, 0x10, 0x00, 0x00, 0x41, 0x82, 0x00, 0x48, + 0x80, 0x64, 0x00, 0x10, 0x7c, 0x10, 0x18, 0x00, 0x40, 0x82, 0x00, 0x10, 0x3a, 0x00, 0x00, 0x00, + 0x92, 0x05, 0x00, 0x08, 0x48, 0x00, 0x00, 0x30, 0x3a, 0x20, 0x00, 0x00, 0x92, 0x25, 0x00, 0x0c, + 0x81, 0x24, 0x00, 0x18, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x00, 0x30, + 0x7e, 0x12, 0xfb, 0xa6, 0x7e, 0x35, 0xfb, 0xa6, 0x39, 0x20, 0x00, 0x01, 0x91, 0x25, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x1c, 0x38, 0xa0, 0x00, 0x02, 0x63, 0xe4, 0x27, 0x74, 0x90, 0xa4, 0x00, 0x00, + 0x38, 0x60, 0x00, 0x11, 0x48, 0x00, 0x01, 0xb9, 0x4b, 0xff, 0xfc, 0x71, 0x7c, 0x20, 0x00, 0xa6, + 0x54, 0x21, 0x07, 0xfa, 0x54, 0x21, 0x04, 0x5e, 0x7c, 0x20, 0x01, 0x24, 0x63, 0xe1, 0x27, 0x98, + 0x80, 0x61, 0x00, 0x00, 0x7c, 0x6f, 0xf1, 0x20, 0x80, 0x61, 0x00, 0x14, 0x7c, 0x7a, 0x03, 0xa6, + 0x80, 0x61, 0x00, 0x18, 0x7c, 0x7b, 0x03, 0xa6, 0x80, 0x61, 0x00, 0x9c, 0x7c, 0x68, 0x03, 0xa6, + 0xb8, 0x41, 0x00, 0x24, 0x80, 0x01, 0x00, 0x1c, 0x80, 0x21, 0x00, 0x20, 0x4c, 0x00, 0x00, 0x64, + 0x92, 0xb2, 0x00, 0x00, 0x48, 0x00, 0x02, 0x54, 0x2e, 0x9d, 0x00, 0x02, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xfd, 0x80, 0xac, 0x00, 0x00, 0x80, 0x6c, 0x00, 0x04, + 0x98, 0x65, 0x00, 0x00, 0x41, 0x94, 0x00, 0x10, 0xb0, 0x65, 0x00, 0x00, 0x41, 0x96, 0x00, 0x08, + 0x90, 0x65, 0x00, 0x00, 0x7c, 0x00, 0x28, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x02, 0x08, 0x48, 0x00, 0x01, 0x21, 0x38, 0x60, 0x00, 0x04, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xbd, 0x82, 0x0c, 0x00, 0x00, 0x3d, 0x80, 0x80, 0x00, + 0x61, 0x8c, 0x28, 0xb8, 0x48, 0x00, 0x00, 0x1c, 0x48, 0x00, 0x01, 0x01, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0x9d, 0x82, 0x0c, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, + 0x63, 0xfb, 0x27, 0x84, 0x3a, 0x20, 0x0f, 0x80, 0x48, 0x00, 0x02, 0x39, 0x41, 0x82, 0x00, 0x20, + 0x7e, 0x23, 0x8b, 0x78, 0x48, 0x00, 0x00, 0x7d, 0x48, 0x00, 0x00, 0xd1, 0x41, 0x82, 0xff, 0xfc, + 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, 0x41, 0x81, 0xff, 0xe8, 0x80, 0x7b, 0x00, 0x00, + 0x2c, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x08, 0x48, 0x00, 0x00, 0x59, 0x7c, 0x00, 0x60, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x67, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x01, 0x80, + 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xa0, 0x00, 0x48, 0x00, 0x00, 0x15, 0x76, 0x03, 0x08, 0x00, + 0x56, 0x1d, 0x86, 0x3e, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x92, 0xf8, 0x68, 0x14, + 0x90, 0x78, 0x68, 0x24, 0x92, 0xd8, 0x68, 0x20, 0x80, 0xb8, 0x68, 0x20, 0x70, 0xa5, 0x00, 0x01, + 0x40, 0x82, 0xff, 0xf8, 0x82, 0x18, 0x68, 0x24, 0x90, 0xb8, 0x68, 0x14, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x48, 0x00, 0x00, 0x79, + 0x48, 0x00, 0x00, 0x75, 0x4b, 0xff, 0xff, 0xad, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xae, 0x61, 0xae, + 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xe8, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x7c, 0x6c, 0x70, 0xae, + 0x48, 0x00, 0x00, 0x1d, 0x41, 0x82, 0xff, 0xf8, 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xf0, + 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x38, 0x60, 0x00, 0xaa, 0x7f, 0xc8, 0x02, 0xa6, + 0x54, 0x63, 0xa0, 0x16, 0x64, 0x63, 0xb0, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcd, 0x00, 0x4b, 0xff, 0xff, 0x69, 0x56, 0x03, 0x37, 0xff, 0x7f, 0xc8, 0x03, 0xa6, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xd0, 0x00, 0x4b, 0xff, 0xff, 0x51, + 0x56, 0x03, 0x37, 0xff, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x4b, 0xff, 0xff, 0xb9, 0x38, 0x60, 0x00, 0x08, 0x63, 0xec, 0x27, 0x7c, 0x4b, 0xff, 0xff, 0x55, + 0x80, 0xac, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, 0x63, 0xfb, 0x27, 0x84, 0x62, 0xb1, 0xf8, 0x00, + 0x7e, 0x0c, 0x28, 0x50, 0x48, 0x00, 0x00, 0xed, 0x41, 0x81, 0x00, 0x10, 0x82, 0x3b, 0x00, 0x00, + 0x2c, 0x11, 0x00, 0x00, 0x41, 0x82, 0x00, 0x68, 0x7e, 0x23, 0x8b, 0x78, 0x4b, 0xff, 0xff, 0x55, + 0x4b, 0xff, 0xff, 0xa5, 0x4b, 0xff, 0xff, 0xa1, 0x4b, 0xff, 0xfe, 0xd9, 0x41, 0x82, 0xff, 0xf4, + 0x2c, 0x1d, 0x00, 0xcc, 0x41, 0x82, 0x00, 0x48, 0x2c, 0x1d, 0x00, 0xbb, 0x41, 0x82, 0xff, 0xdc, + 0x2c, 0x1d, 0x00, 0xaa, 0x40, 0x82, 0xff, 0xdc, 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, + 0x41, 0x80, 0x00, 0x2c, 0x4b, 0xff, 0xff, 0xb4, 0x7e, 0xb5, 0xfb, 0xa6, 0x7e, 0xb2, 0xfb, 0xa6, + 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, + 0x48, 0x00, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x80, 0x4b, 0xff, 0xff, 0x25, 0x80, 0x92, 0x00, 0x00, + 0x2c, 0x04, 0x00, 0x00, 0x40, 0x82, 0xfa, 0x50, 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, + 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, + 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x7e, 0x23, 0x20, 0x50, + 0x3c, 0xa0, 0x48, 0x00, 0x52, 0x25, 0x01, 0xba, 0x90, 0xa3, 0x00, 0x00, 0x7c, 0x00, 0x18, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x1f, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x70, 0x8b, 0xd7, 0x7d, 0x4b, 0x89, 0xd6, 0x7d, 0x4a, 0x80, 0x50, 0x91, 0x5b, 0x00, 0x00, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xa8, 0x02, 0xa6, 0x3d, 0xe0, 0x80, 0x00, 0x61, 0xef, 0x28, 0xb8, + 0x63, 0xe7, 0x18, 0x08, 0x3c, 0xc0, 0x80, 0x00, 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, + 0x3c, 0x60, 0x00, 0xd0, 0x60, 0x63, 0xc0, 0xde, 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, + 0x40, 0x82, 0x00, 0x18, 0x80, 0x8f, 0x00, 0x04, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, + 0x39, 0xef, 0x00, 0x08, 0x48, 0x00, 0x00, 0x0c, 0x7f, 0xa8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x80, 0x6f, 0x00, 0x00, 0x80, 0x8f, 0x00, 0x04, 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, + 0x2f, 0x89, 0x00, 0x00, 0x39, 0x20, 0x00, 0x00, 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, + 0x74, 0x6b, 0x10, 0x00, 0x54, 0x63, 0x01, 0xfe, 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x08, 0x7e, 0x0c, 0x83, 0x78, 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, + 0x41, 0xa0, 0x00, 0x2c, 0x41, 0xa2, 0x00, 0xe4, 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xac, + 0x41, 0x82, 0x02, 0x50, 0x2c, 0x0a, 0x00, 0x05, 0x41, 0x80, 0x02, 0xd4, 0x41, 0xa2, 0x04, 0xe0, + 0x2c, 0x0a, 0x00, 0x07, 0x41, 0xa0, 0x05, 0x0c, 0x48, 0x00, 0x05, 0xf0, 0x7d, 0x8c, 0x1a, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x48, 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, + 0x2e, 0x05, 0x00, 0x01, 0x41, 0x91, 0x00, 0x2c, 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, + 0x7c, 0x89, 0x61, 0xae, 0x39, 0x29, 0x00, 0x01, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, + 0x39, 0x29, 0x00, 0x02, 0x35, 0x4a, 0xff, 0xff, 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, + 0x55, 0x8c, 0x00, 0x3a, 0x90, 0x8c, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, + 0x40, 0x9e, 0x04, 0xc8, 0x35, 0x29, 0xff, 0xff, 0x41, 0x80, 0x04, 0xc0, 0x7c, 0xa9, 0x78, 0xae, + 0x7c, 0xa9, 0x61, 0xae, 0x4b, 0xff, 0xff, 0xf0, 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, + 0x80, 0xaf, 0xff, 0xf8, 0x81, 0x6f, 0xff, 0xfc, 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, + 0x54, 0xa5, 0x27, 0x3e, 0x2e, 0x85, 0x00, 0x01, 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, + 0x7c, 0x89, 0x61, 0xae, 0x48, 0x00, 0x00, 0x10, 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x89, 0x61, 0x2e, 0x7c, 0x84, 0x5a, 0x14, 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, + 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfe, 0xdc, 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, + 0x55, 0x08, 0xf8, 0x7e, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, + 0x2d, 0x8a, 0x00, 0x05, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xb8, + 0x7d, 0x8c, 0x1a, 0x14, 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, + 0x40, 0x94, 0x00, 0x10, 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, + 0x55, 0x8c, 0x00, 0x3c, 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, + 0x7d, 0x6b, 0x48, 0x38, 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, + 0x41, 0x82, 0x00, 0x18, 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, + 0x40, 0x9a, 0x00, 0x20, 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, + 0x41, 0x99, 0x00, 0x10, 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, + 0x40, 0x8e, 0xfe, 0x40, 0x41, 0x94, 0xfe, 0x3c, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, + 0x70, 0x6c, 0x00, 0x08, 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, + 0x39, 0x8b, 0x00, 0x10, 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, + 0x91, 0x6f, 0xff, 0xf8, 0x4b, 0xff, 0xfe, 0x0c, 0x40, 0xbe, 0xfe, 0x08, 0x54, 0x69, 0x16, 0xba, + 0x54, 0x6e, 0x87, 0xfe, 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, + 0x2e, 0x8e, 0x00, 0x02, 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, + 0x7c, 0x84, 0x7a, 0x14, 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, + 0x2e, 0x8e, 0x00, 0x01, 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, + 0x41, 0x82, 0x00, 0x3c, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, + 0x7c, 0x84, 0x82, 0x14, 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, + 0x4b, 0xff, 0xfd, 0x80, 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x78, 0x40, 0x90, 0x00, 0x0c, + 0x7c, 0x86, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x6c, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x64, + 0x54, 0x89, 0x1e, 0x78, 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x48, + 0x54, 0x6b, 0x50, 0x03, 0x41, 0x82, 0x00, 0x14, 0x41, 0x81, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, + 0x41, 0xbe, 0xfd, 0x40, 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x38, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x81, 0x00, 0x10, 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x24, + 0x7d, 0xe7, 0x49, 0x2e, 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, + 0x4b, 0xff, 0xfd, 0x10, 0x40, 0xbe, 0xfd, 0x0c, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, + 0x54, 0x64, 0x04, 0x3e, 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xf4, + 0x81, 0x25, 0x00, 0x04, 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe8, 0x39, 0x29, 0xff, 0xff, + 0x91, 0x25, 0x00, 0x04, 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd8, 0x40, 0xbe, 0xfc, 0xd4, + 0x54, 0x6b, 0x16, 0xba, 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, + 0x41, 0x92, 0x00, 0x84, 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, + 0x40, 0x90, 0x00, 0x90, 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0x8c, 0x22, 0x14, 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, + 0x41, 0x9a, 0x00, 0x0c, 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, + 0x48, 0x00, 0x00, 0xf0, 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, + 0x41, 0xb9, 0x00, 0x20, 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x18, 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, + 0x91, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, + 0x4b, 0xff, 0xfc, 0x40, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, + 0x71, 0xc5, 0x00, 0x01, 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, + 0x54, 0x6a, 0x87, 0xbe, 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, + 0x3a, 0x6f, 0xff, 0xfc, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x08, 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, + 0x7d, 0x33, 0x4b, 0x78, 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, + 0x2c, 0x05, 0x00, 0x09, 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, + 0x48, 0x00, 0x00, 0x40, 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, + 0x48, 0x00, 0x00, 0x30, 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, + 0x48, 0x00, 0x00, 0x20, 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, + 0x48, 0x00, 0x00, 0x10, 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, + 0x90, 0x9a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x8c, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x84, + 0xc0, 0x5a, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, + 0x48, 0x00, 0x00, 0x08, 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x64, + 0x7d, 0x48, 0x02, 0xa6, 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, + 0x81, 0x33, 0x00, 0x00, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x44, + 0x54, 0x69, 0xc0, 0x3e, 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, + 0x7e, 0x31, 0x22, 0x14, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, + 0x38, 0xa0, 0x00, 0x00, 0x41, 0x82, 0xfb, 0x1c, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, + 0x38, 0xa5, 0x00, 0x01, 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, + 0x55, 0x31, 0x36, 0xba, 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, + 0x7d, 0xd1, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, + 0x2c, 0x09, 0x00, 0x3c, 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, + 0x41, 0x96, 0x00, 0x08, 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x28, 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, + 0x4b, 0xff, 0xff, 0xad, 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, + 0x7e, 0x24, 0x20, 0x38, 0x4b, 0xff, 0xfb, 0xc4, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xbc, + 0x7c, 0x9a, 0x23, 0x78, 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, + 0x7d, 0xe8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, + 0x55, 0xef, 0x00, 0x38, 0x4b, 0xff, 0xfa, 0x6c, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, + 0x3c, 0xa0, 0x48, 0x00, 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, + 0x40, 0xbe, 0xfa, 0x50, 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, + 0x50, 0x65, 0x07, 0xfe, 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x38, 0x40, 0xbe, 0xff, 0xbc, + 0x7d, 0x2c, 0x78, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, + 0x7d, 0x6f, 0x22, 0x14, 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, + 0x90, 0xab, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, + 0x4b, 0xff, 0xfb, 0x28, 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, + 0x7c, 0x0c, 0x20, 0x00, 0x41, 0x80, 0xfb, 0xa8, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0xa0, + 0x4b, 0xff, 0xf9, 0xe0, 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, + 0x7c, 0x05, 0x18, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, + 0x57, 0x45, 0xff, 0xff, 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, + 0x53, 0x48, 0x07, 0xfe, 0x4b, 0xff, 0xf9, 0xac, 0x2c, 0x0b, 0x00, 0x00, 0x41, 0x82, 0x01, 0x38, + 0x2c, 0x05, 0x00, 0x01, 0x41, 0x82, 0x00, 0x18, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x82, 0x00, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x70, 0x4b, 0xff, 0xf9, 0x80, 0x54, 0xcc, 0x00, 0x0c, + 0x54, 0x97, 0x46, 0x3e, 0x54, 0x98, 0xc4, 0x3e, 0x54, 0x84, 0x06, 0x3e, 0x40, 0x9e, 0x00, 0xfc, + 0x56, 0xf9, 0x06, 0x31, 0x7d, 0x9a, 0x63, 0x78, 0x7f, 0x43, 0xd2, 0x14, 0x57, 0x5a, 0x00, 0x3a, + 0x41, 0x82, 0x00, 0x18, 0x7e, 0xf7, 0x07, 0x74, 0x7e, 0xf7, 0x00, 0xd0, 0x1f, 0x37, 0x00, 0x02, + 0x3b, 0x39, 0x00, 0x04, 0x7f, 0x59, 0xd0, 0x50, 0x2c, 0x17, 0x00, 0x00, 0x41, 0x82, 0x00, 0x1c, + 0x3b, 0x20, 0x00, 0x00, 0x7e, 0xe9, 0x03, 0xa6, 0xa3, 0x7a, 0x00, 0x04, 0x7f, 0x79, 0xca, 0x78, + 0x3b, 0x5a, 0x00, 0x02, 0x42, 0x00, 0xff, 0xf4, 0x7c, 0x18, 0xc8, 0x00, 0x40, 0x82, 0x00, 0xac, + 0x4b, 0xff, 0xfe, 0x90, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x9c, 0x54, 0x77, 0xb0, 0x03, + 0x41, 0x81, 0x00, 0x88, 0x41, 0x80, 0x00, 0x8c, 0x54, 0x7e, 0x06, 0x3e, 0x1f, 0xde, 0x00, 0x02, + 0x54, 0x97, 0x00, 0x1e, 0x6e, 0xf8, 0x80, 0x00, 0x2c, 0x18, 0x00, 0x00, 0x40, 0x82, 0x00, 0x08, + 0x62, 0xf7, 0x30, 0x00, 0x54, 0x98, 0x80, 0x1e, 0x1f, 0x3e, 0x00, 0x04, 0x7f, 0x19, 0xc0, 0x50, + 0x3b, 0x20, 0x00, 0x00, 0x1f, 0x59, 0x00, 0x04, 0x7f, 0x6f, 0xd0, 0x2e, 0x7f, 0x57, 0xd0, 0x2e, + 0x3b, 0x39, 0x00, 0x01, 0x7c, 0x17, 0xc0, 0x40, 0x41, 0x81, 0x00, 0x34, 0x7c, 0x19, 0xf0, 0x00, + 0x41, 0x81, 0x00, 0x14, 0x7c, 0x1a, 0xd8, 0x00, 0x41, 0x82, 0xff, 0xdc, 0x3a, 0xf7, 0x00, 0x04, + 0x4b, 0xff, 0xff, 0xd0, 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x03, 0x00, 0x90, 0x6f, 0xff, 0xf8, + 0x92, 0xef, 0xff, 0xfc, 0x7e, 0xf0, 0xbb, 0x78, 0x48, 0x00, 0x00, 0x1c, 0x80, 0x6f, 0xff, 0xf8, + 0x60, 0x63, 0x01, 0x00, 0x90, 0x6f, 0xff, 0xf8, 0x61, 0x08, 0x00, 0x01, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x90, 0x23, 0x78, 0x54, 0x64, 0x06, 0x3e, 0x1c, 0x84, 0x00, 0x08, 0x7d, 0xe4, 0x7a, 0x14, + 0x4b, 0xff, 0xf8, 0x70, 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, + 0x54, 0x69, 0x06, 0xff, 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, 0x55, 0x17, 0xff, 0xff, + 0x40, 0x82, 0x00, 0x08, 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0xa6, 0x2b, 0x78, 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, + 0x4b, 0xff, 0xf8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; +const int codehandler_size = sizeof(codehandler); diff --git a/plugins/mighty/source/codes/codehandleronly.h b/plugins/mighty/source/codes/codehandleronly.h new file mode 100644 index 0000000..d125bae --- /dev/null +++ b/plugins/mighty/source/codes/codehandleronly.h @@ -0,0 +1,180 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +const unsigned char codehandleronly[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x21, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, + 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, + 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, + 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, + 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, + 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, + 0x7f, 0xa8, 0x02, 0xa6, 0x3d, 0xe0, 0x80, 0x00, 0x61, 0xef, 0x22, 0xa8, 0x63, 0xe7, 0x18, 0x08, + 0x3c, 0xc0, 0x80, 0x00, 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x00, 0xd0, + 0x60, 0x63, 0xc0, 0xde, 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x18, + 0x80, 0x8f, 0x00, 0x04, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, 0x39, 0xef, 0x00, 0x08, + 0x48, 0x00, 0x00, 0x4c, 0x7f, 0xa8, 0x03, 0xa6, 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, + 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, + 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x80, 0x6f, 0x00, 0x00, + 0x80, 0x8f, 0x00, 0x04, 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, + 0x39, 0x20, 0x00, 0x00, 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, 0x74, 0x6b, 0x10, 0x00, + 0x54, 0x63, 0x01, 0xfe, 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x08, + 0x7e, 0x0c, 0x83, 0x78, 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, 0x41, 0xa0, 0x00, 0x2c, + 0x41, 0xa2, 0x00, 0xe4, 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xac, 0x41, 0x82, 0x02, 0x50, + 0x2c, 0x0a, 0x00, 0x05, 0x41, 0x80, 0x02, 0xd4, 0x41, 0xa2, 0x04, 0xe0, 0x2c, 0x0a, 0x00, 0x07, + 0x41, 0xa0, 0x05, 0x0c, 0x48, 0x00, 0x05, 0xf0, 0x7d, 0x8c, 0x1a, 0x14, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x82, 0x00, 0x48, 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, 0x2e, 0x05, 0x00, 0x01, + 0x41, 0x91, 0x00, 0x2c, 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, 0x7c, 0x89, 0x61, 0xae, + 0x39, 0x29, 0x00, 0x01, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, 0x39, 0x29, 0x00, 0x02, + 0x35, 0x4a, 0xff, 0xff, 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, 0x55, 0x8c, 0x00, 0x3a, + 0x90, 0x8c, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, 0x40, 0x9e, 0x04, 0xc8, + 0x35, 0x29, 0xff, 0xff, 0x41, 0x80, 0x04, 0xc0, 0x7c, 0xa9, 0x78, 0xae, 0x7c, 0xa9, 0x61, 0xae, + 0x4b, 0xff, 0xff, 0xf0, 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, 0x80, 0xaf, 0xff, 0xf8, + 0x81, 0x6f, 0xff, 0xfc, 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, 0x54, 0xa5, 0x27, 0x3e, + 0x2e, 0x85, 0x00, 0x01, 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, 0x7c, 0x89, 0x61, 0xae, + 0x48, 0x00, 0x00, 0x10, 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, 0x7c, 0x89, 0x61, 0x2e, + 0x7c, 0x84, 0x5a, 0x14, 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, + 0x4b, 0xff, 0xfe, 0xdc, 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, 0x55, 0x08, 0xf8, 0x7e, + 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, 0x2d, 0x8a, 0x00, 0x05, + 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xb8, 0x7d, 0x8c, 0x1a, 0x14, + 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, 0x40, 0x94, 0x00, 0x10, + 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, 0x55, 0x8c, 0x00, 0x3c, + 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, 0x7d, 0x6b, 0x48, 0x38, + 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, 0x41, 0x82, 0x00, 0x18, + 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, 0x40, 0x9a, 0x00, 0x20, + 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, 0x41, 0x99, 0x00, 0x10, + 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, 0x40, 0x8e, 0xfe, 0x40, + 0x41, 0x94, 0xfe, 0x3c, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, 0x70, 0x6c, 0x00, 0x08, + 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, 0x39, 0x8b, 0x00, 0x10, + 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, 0x91, 0x6f, 0xff, 0xf8, + 0x4b, 0xff, 0xfe, 0x0c, 0x40, 0xbe, 0xfe, 0x08, 0x54, 0x69, 0x16, 0xba, 0x54, 0x6e, 0x87, 0xfe, + 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, 0x2e, 0x8e, 0x00, 0x02, + 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, 0x7c, 0x84, 0x7a, 0x14, + 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, + 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, 0x2e, 0x8e, 0x00, 0x01, + 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, 0x41, 0x82, 0x00, 0x3c, + 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, 0x7c, 0x84, 0x82, 0x14, + 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, + 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x80, + 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x78, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x86, 0x23, 0x78, + 0x4b, 0xff, 0xfd, 0x6c, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x64, 0x54, 0x89, 0x1e, 0x78, + 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x48, 0x54, 0x6b, 0x50, 0x03, + 0x41, 0x82, 0x00, 0x14, 0x41, 0x81, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, 0x41, 0xbe, 0xfd, 0x40, + 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x38, 0x2c, 0x05, 0x00, 0x03, 0x41, 0x81, 0x00, 0x10, + 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x24, 0x7d, 0xe7, 0x49, 0x2e, + 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, 0x4b, 0xff, 0xfd, 0x10, + 0x40, 0xbe, 0xfd, 0x0c, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, 0x54, 0x64, 0x04, 0x3e, + 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xf4, 0x81, 0x25, 0x00, 0x04, + 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe8, 0x39, 0x29, 0xff, 0xff, 0x91, 0x25, 0x00, 0x04, + 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd8, 0x40, 0xbe, 0xfc, 0xd4, 0x54, 0x6b, 0x16, 0xba, + 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, 0x41, 0x92, 0x00, 0x84, + 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, 0x40, 0x90, 0x00, 0x90, + 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, + 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x0c, + 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf0, + 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, 0x41, 0xb9, 0x00, 0x20, + 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, 0x48, 0x00, 0x00, 0x18, + 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, 0x91, 0x24, 0x00, 0x00, + 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfc, 0x40, + 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, 0x71, 0xc5, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, 0x54, 0x6a, 0x87, 0xbe, + 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, 0x3a, 0x6f, 0xff, 0xfc, + 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, 0x7d, 0x33, 0x4b, 0x78, + 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, 0x2c, 0x05, 0x00, 0x09, + 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, 0x48, 0x00, 0x00, 0x40, + 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, 0x48, 0x00, 0x00, 0x30, + 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, 0x48, 0x00, 0x00, 0x20, + 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, 0x48, 0x00, 0x00, 0x10, + 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, 0x90, 0x9a, 0x00, 0x00, + 0x4b, 0xff, 0xfb, 0x8c, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x84, 0xc0, 0x5a, 0x00, 0x00, + 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, 0x48, 0x00, 0x00, 0x08, + 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x64, 0x7d, 0x48, 0x02, 0xa6, + 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, + 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x44, 0x54, 0x69, 0xc0, 0x3e, + 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, 0x7e, 0x31, 0x22, 0x14, + 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, 0x38, 0xa0, 0x00, 0x00, + 0x41, 0x82, 0xfb, 0x1c, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, 0x38, 0xa5, 0x00, 0x01, + 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, 0x55, 0x31, 0x36, 0xba, + 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xd1, 0x73, 0x78, + 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, 0x2c, 0x09, 0x00, 0x3c, + 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, + 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, 0x40, 0x80, 0x00, 0x28, + 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, 0x4b, 0xff, 0xff, 0xad, + 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, 0x7e, 0x24, 0x20, 0x38, + 0x4b, 0xff, 0xfb, 0xc4, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xbc, 0x7c, 0x9a, 0x23, 0x78, + 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, 0x7d, 0xe8, 0x03, 0xa6, + 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, 0x55, 0xef, 0x00, 0x38, + 0x4b, 0xff, 0xfa, 0x6c, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, 0x3c, 0xa0, 0x48, 0x00, + 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, 0x40, 0xbe, 0xfa, 0x50, + 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, 0x50, 0x65, 0x07, 0xfe, + 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x38, 0x40, 0xbe, 0xff, 0xbc, 0x7d, 0x2c, 0x78, 0x50, + 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, 0x7d, 0x6f, 0x22, 0x14, + 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xab, 0x00, 0x00, + 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, 0x4b, 0xff, 0xfb, 0x28, + 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, 0x7c, 0x0c, 0x20, 0x00, + 0x41, 0x80, 0xfb, 0xa8, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0xa0, 0x4b, 0xff, 0xf9, 0xe0, + 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, 0x7c, 0x05, 0x18, 0x00, + 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, 0x57, 0x45, 0xff, 0xff, + 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, 0x53, 0x48, 0x07, 0xfe, + 0x4b, 0xff, 0xf9, 0xac, 0x2c, 0x0b, 0x00, 0x00, 0x41, 0x82, 0x01, 0x38, 0x2c, 0x05, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x18, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x82, 0x00, 0x14, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x82, 0x00, 0x70, 0x4b, 0xff, 0xf9, 0x40, 0x54, 0xcc, 0x00, 0x0c, 0x54, 0x97, 0x46, 0x3e, + 0x54, 0x98, 0xc4, 0x3e, 0x54, 0x84, 0x06, 0x3e, 0x40, 0x9e, 0x00, 0xfc, 0x56, 0xf9, 0x06, 0x31, + 0x7d, 0x9a, 0x63, 0x78, 0x7f, 0x43, 0xd2, 0x14, 0x57, 0x5a, 0x00, 0x3a, 0x41, 0x82, 0x00, 0x18, + 0x7e, 0xf7, 0x07, 0x74, 0x7e, 0xf7, 0x00, 0xd0, 0x1f, 0x37, 0x00, 0x02, 0x3b, 0x39, 0x00, 0x04, + 0x7f, 0x59, 0xd0, 0x50, 0x2c, 0x17, 0x00, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x3b, 0x20, 0x00, 0x00, + 0x7e, 0xe9, 0x03, 0xa6, 0xa3, 0x7a, 0x00, 0x04, 0x7f, 0x79, 0xca, 0x78, 0x3b, 0x5a, 0x00, 0x02, + 0x42, 0x00, 0xff, 0xf4, 0x7c, 0x18, 0xc8, 0x00, 0x40, 0x82, 0x00, 0xac, 0x4b, 0xff, 0xfe, 0x90, + 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x9c, 0x54, 0x77, 0xb0, 0x03, 0x41, 0x81, 0x00, 0x88, + 0x41, 0x80, 0x00, 0x8c, 0x54, 0x7e, 0x06, 0x3e, 0x1f, 0xde, 0x00, 0x02, 0x54, 0x97, 0x00, 0x1e, + 0x6e, 0xf8, 0x80, 0x00, 0x2c, 0x18, 0x00, 0x00, 0x40, 0x82, 0x00, 0x08, 0x62, 0xf7, 0x30, 0x00, + 0x54, 0x98, 0x80, 0x1e, 0x1f, 0x3e, 0x00, 0x04, 0x7f, 0x19, 0xc0, 0x50, 0x3b, 0x20, 0x00, 0x00, + 0x1f, 0x59, 0x00, 0x04, 0x7f, 0x6f, 0xd0, 0x2e, 0x7f, 0x57, 0xd0, 0x2e, 0x3b, 0x39, 0x00, 0x01, + 0x7c, 0x17, 0xc0, 0x40, 0x41, 0x81, 0x00, 0x34, 0x7c, 0x19, 0xf0, 0x00, 0x41, 0x81, 0x00, 0x14, + 0x7c, 0x1a, 0xd8, 0x00, 0x41, 0x82, 0xff, 0xdc, 0x3a, 0xf7, 0x00, 0x04, 0x4b, 0xff, 0xff, 0xd0, + 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x03, 0x00, 0x90, 0x6f, 0xff, 0xf8, 0x92, 0xef, 0xff, 0xfc, + 0x7e, 0xf0, 0xbb, 0x78, 0x48, 0x00, 0x00, 0x1c, 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x01, 0x00, + 0x90, 0x6f, 0xff, 0xf8, 0x61, 0x08, 0x00, 0x01, 0x48, 0x00, 0x00, 0x08, 0x7c, 0x90, 0x23, 0x78, + 0x54, 0x64, 0x06, 0x3e, 0x1c, 0x84, 0x00, 0x08, 0x7d, 0xe4, 0x7a, 0x14, 0x4b, 0xff, 0xf8, 0x70, + 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, 0x54, 0x69, 0x06, 0xff, + 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, 0x55, 0x17, 0xff, 0xff, 0x40, 0x82, 0x00, 0x08, + 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xa6, 0x2b, 0x78, + 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, 0x4b, 0xff, 0xf8, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; +const int codehandleronly_size = sizeof(codehandleronly); diff --git a/plugins/mighty/source/codes/codehandlerslota.h b/plugins/mighty/source/codes/codehandlerslota.h new file mode 100644 index 0000000..1f45411 --- /dev/null +++ b/plugins/mighty/source/codes/codehandlerslota.h @@ -0,0 +1,277 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +const unsigned char codehandlerslota[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x27, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, + 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, + 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, + 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, + 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, + 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, + 0x48, 0x00, 0x06, 0x55, 0x3a, 0xa0, 0x00, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcd, 0x00, 0x63, 0xf2, 0x27, 0x74, 0x80, 0x01, 0x00, 0xac, 0x90, 0x12, 0x00, 0x04, + 0x92, 0xb8, 0x64, 0x3c, 0x48, 0x00, 0x04, 0x2d, 0x41, 0x82, 0x05, 0xa4, 0x2c, 0x1d, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x10, 0x2c, 0x1d, 0x00, 0x01, 0x41, 0x80, 0x05, 0x94, 0x48, 0x00, 0x03, 0x4c, + 0x41, 0x82, 0x04, 0xf0, 0x2c, 0x1d, 0x00, 0x06, 0x41, 0x82, 0x00, 0x8c, 0x2c, 0x1d, 0x00, 0x07, + 0x41, 0x82, 0x03, 0x30, 0x2c, 0x1d, 0x00, 0x08, 0x41, 0x82, 0x05, 0x80, 0x2c, 0x1d, 0x00, 0x09, + 0x41, 0x82, 0x00, 0xa0, 0x2c, 0x1d, 0x00, 0x10, 0x41, 0x82, 0x00, 0x98, 0x2c, 0x1d, 0x00, 0x2f, + 0x41, 0x82, 0x00, 0x70, 0x2c, 0x1d, 0x00, 0x30, 0x41, 0x82, 0x00, 0x78, 0x2c, 0x1d, 0x00, 0x38, + 0x41, 0x82, 0x05, 0x28, 0x2c, 0x1d, 0x00, 0x40, 0x41, 0x82, 0x03, 0x40, 0x2c, 0x1d, 0x00, 0x41, + 0x41, 0x82, 0x03, 0x58, 0x2c, 0x1d, 0x00, 0x44, 0x41, 0x82, 0x00, 0x68, 0x2c, 0x1d, 0x00, 0x50, + 0x41, 0x82, 0x00, 0x20, 0x2c, 0x1d, 0x00, 0x60, 0x41, 0x82, 0x00, 0x24, 0x2c, 0x1d, 0x00, 0x89, + 0x41, 0x82, 0x00, 0x50, 0x2c, 0x1d, 0x00, 0x99, 0x41, 0x82, 0x05, 0x0c, 0x48, 0x00, 0x05, 0x10, + 0x80, 0x72, 0x00, 0x00, 0x48, 0x00, 0x04, 0x29, 0x48, 0x00, 0x05, 0x04, 0x48, 0x00, 0x05, 0x89, + 0x48, 0x00, 0x04, 0xfc, 0x38, 0x80, 0x00, 0x01, 0x90, 0x92, 0x00, 0x00, 0x48, 0x00, 0x04, 0xf0, + 0x48, 0x00, 0x04, 0x09, 0x3a, 0x00, 0x00, 0xa0, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0x14, + 0x38, 0x60, 0x01, 0x20, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0xc9, 0x48, 0x00, 0x04, 0xd0, + 0x2f, 0x1d, 0x00, 0x10, 0x2e, 0x9d, 0x00, 0x44, 0x63, 0xe4, 0x1a, 0xb4, 0x3c, 0x60, 0x80, 0x00, + 0x60, 0x63, 0x03, 0x00, 0x48, 0x00, 0x05, 0x09, 0x38, 0x63, 0x0a, 0x00, 0x48, 0x00, 0x05, 0x01, + 0x38, 0x63, 0x06, 0x00, 0x48, 0x00, 0x04, 0xf9, 0x63, 0xec, 0x27, 0x88, 0x92, 0xac, 0x00, 0x00, + 0x92, 0xac, 0x00, 0x04, 0x92, 0xac, 0x00, 0x08, 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, + 0x80, 0x72, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x02, 0x40, 0x82, 0x00, 0x0c, 0x41, 0x96, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x20, 0x38, 0x60, 0x00, 0x00, 0x90, 0x6c, 0x00, 0x0c, 0x40, 0x82, 0x00, 0x14, + 0x40, 0x96, 0x00, 0x10, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x02, 0x14, + 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, 0x41, 0x96, 0x04, 0x54, 0x41, 0x9a, 0x00, 0x08, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x03, 0x09, 0x40, 0x99, 0x00, 0x10, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x02, 0xf9, 0x63, 0xe4, 0x27, 0x88, + 0x80, 0x64, 0x00, 0x00, 0x80, 0x84, 0x00, 0x04, 0x7c, 0x72, 0xfb, 0xa6, 0x7c, 0x95, 0xfb, 0xa6, + 0x48, 0x00, 0x04, 0x1c, 0x7c, 0x32, 0x43, 0xa6, 0x7c, 0x3a, 0x02, 0xa6, 0x7c, 0x73, 0x43, 0xa6, + 0x7c, 0x7b, 0x02, 0xa6, 0x54, 0x63, 0x05, 0xa8, 0x90, 0x60, 0x27, 0xb0, 0x54, 0x63, 0x06, 0x1e, + 0x60, 0x63, 0x20, 0x00, 0x7c, 0x7b, 0x03, 0xa6, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x1a, 0xe8, + 0x7c, 0x7a, 0x03, 0xa6, 0x4c, 0x00, 0x00, 0x64, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x27, 0x98, + 0x90, 0x23, 0x00, 0x14, 0x7c, 0x61, 0x1b, 0x78, 0x7c, 0x73, 0x42, 0xa6, 0xbc, 0x41, 0x00, 0x24, + 0x7c, 0x24, 0x0b, 0x78, 0x7c, 0x32, 0x42, 0xa6, 0x90, 0x04, 0x00, 0x1c, 0x90, 0x24, 0x00, 0x20, + 0x7c, 0x68, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x9c, 0x7c, 0x60, 0x00, 0x26, 0x90, 0x64, 0x00, 0x00, + 0x7c, 0x61, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x04, 0x7c, 0x69, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x08, + 0x7c, 0x72, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x0c, 0x7c, 0x73, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x10, + 0x39, 0x20, 0x00, 0x00, 0x7d, 0x32, 0xfb, 0xa6, 0x7d, 0x35, 0xfb, 0xa6, 0x3c, 0xa0, 0x80, 0x00, + 0x60, 0xa5, 0x1b, 0x70, 0x3f, 0xe0, 0xd0, 0x04, 0x63, 0xff, 0x00, 0xa0, 0x93, 0xe5, 0x00, 0x00, + 0x7c, 0x00, 0x28, 0x6c, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, 0x4c, 0x00, 0x01, 0x2c, + 0xd0, 0x04, 0x00, 0xa0, 0x3b, 0xff, 0x00, 0x04, 0x3f, 0xff, 0x00, 0x20, 0x57, 0xf0, 0x01, 0x4b, + 0x41, 0x82, 0xff, 0xdc, 0x3f, 0xe0, 0x80, 0x00, 0x63, 0xe5, 0x27, 0x88, 0x82, 0x05, 0x00, 0x00, + 0x82, 0x25, 0x00, 0x04, 0x82, 0x65, 0x00, 0x0c, 0x2c, 0x13, 0x00, 0x00, 0x41, 0x82, 0x00, 0x74, + 0x2c, 0x13, 0x00, 0x02, 0x40, 0x82, 0x00, 0x18, 0x81, 0x24, 0x00, 0x14, 0x39, 0x33, 0x00, 0x03, + 0x91, 0x25, 0x00, 0x00, 0x91, 0x25, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x6c, 0x7c, 0x10, 0x98, 0x00, + 0x41, 0x82, 0x00, 0x38, 0x7c, 0x11, 0x98, 0x00, 0x41, 0x82, 0x00, 0x30, 0x7d, 0x30, 0x8a, 0x14, + 0x91, 0x25, 0x00, 0x0c, 0x82, 0x05, 0x00, 0x08, 0x2c, 0x10, 0x00, 0x00, 0x41, 0x82, 0x00, 0x48, + 0x80, 0x64, 0x00, 0x10, 0x7c, 0x10, 0x18, 0x00, 0x40, 0x82, 0x00, 0x10, 0x3a, 0x00, 0x00, 0x00, + 0x92, 0x05, 0x00, 0x08, 0x48, 0x00, 0x00, 0x30, 0x3a, 0x20, 0x00, 0x00, 0x92, 0x25, 0x00, 0x0c, + 0x81, 0x24, 0x00, 0x18, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x00, 0x30, + 0x7e, 0x12, 0xfb, 0xa6, 0x7e, 0x35, 0xfb, 0xa6, 0x39, 0x20, 0x00, 0x01, 0x91, 0x25, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x1c, 0x38, 0xa0, 0x00, 0x02, 0x63, 0xe4, 0x27, 0x74, 0x90, 0xa4, 0x00, 0x00, + 0x38, 0x60, 0x00, 0x11, 0x48, 0x00, 0x01, 0xb9, 0x4b, 0xff, 0xfc, 0x71, 0x7c, 0x20, 0x00, 0xa6, + 0x54, 0x21, 0x07, 0xfa, 0x54, 0x21, 0x04, 0x5e, 0x7c, 0x20, 0x01, 0x24, 0x63, 0xe1, 0x27, 0x98, + 0x80, 0x61, 0x00, 0x00, 0x7c, 0x6f, 0xf1, 0x20, 0x80, 0x61, 0x00, 0x14, 0x7c, 0x7a, 0x03, 0xa6, + 0x80, 0x61, 0x00, 0x18, 0x7c, 0x7b, 0x03, 0xa6, 0x80, 0x61, 0x00, 0x9c, 0x7c, 0x68, 0x03, 0xa6, + 0xb8, 0x41, 0x00, 0x24, 0x80, 0x01, 0x00, 0x1c, 0x80, 0x21, 0x00, 0x20, 0x4c, 0x00, 0x00, 0x64, + 0x92, 0xb2, 0x00, 0x00, 0x48, 0x00, 0x02, 0x54, 0x2e, 0x9d, 0x00, 0x02, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xfd, 0x80, 0xac, 0x00, 0x00, 0x80, 0x6c, 0x00, 0x04, + 0x98, 0x65, 0x00, 0x00, 0x41, 0x94, 0x00, 0x10, 0xb0, 0x65, 0x00, 0x00, 0x41, 0x96, 0x00, 0x08, + 0x90, 0x65, 0x00, 0x00, 0x7c, 0x00, 0x28, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x02, 0x08, 0x48, 0x00, 0x01, 0x21, 0x38, 0x60, 0x00, 0x04, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xbd, 0x82, 0x0c, 0x00, 0x00, 0x3d, 0x80, 0x80, 0x00, + 0x61, 0x8c, 0x28, 0xb8, 0x48, 0x00, 0x00, 0x1c, 0x48, 0x00, 0x01, 0x01, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0x9d, 0x82, 0x0c, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, + 0x63, 0xfb, 0x27, 0x84, 0x3a, 0x20, 0x0f, 0x80, 0x48, 0x00, 0x02, 0x39, 0x41, 0x82, 0x00, 0x20, + 0x7e, 0x23, 0x8b, 0x78, 0x48, 0x00, 0x00, 0x7d, 0x48, 0x00, 0x00, 0xd1, 0x41, 0x82, 0xff, 0xfc, + 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, 0x41, 0x81, 0xff, 0xe8, 0x80, 0x7b, 0x00, 0x00, + 0x2c, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x08, 0x48, 0x00, 0x00, 0x59, 0x7c, 0x00, 0x60, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x67, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x01, 0x80, + 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xa0, 0x00, 0x48, 0x00, 0x00, 0x15, 0x76, 0x03, 0x08, 0x00, + 0x56, 0x1d, 0x86, 0x3e, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x92, 0xf8, 0x68, 0x00, + 0x90, 0x78, 0x68, 0x10, 0x92, 0xd8, 0x68, 0x0c, 0x80, 0xb8, 0x68, 0x0c, 0x70, 0xa5, 0x00, 0x01, + 0x40, 0x82, 0xff, 0xf8, 0x82, 0x18, 0x68, 0x10, 0x90, 0xb8, 0x68, 0x00, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x48, 0x00, 0x00, 0x79, + 0x48, 0x00, 0x00, 0x75, 0x4b, 0xff, 0xff, 0xad, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xae, 0x61, 0xae, + 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xe8, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x7c, 0x6c, 0x70, 0xae, + 0x48, 0x00, 0x00, 0x1d, 0x41, 0x82, 0xff, 0xf8, 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xf0, + 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x38, 0x60, 0x00, 0xaa, 0x7f, 0xc8, 0x02, 0xa6, + 0x54, 0x63, 0xa0, 0x16, 0x64, 0x63, 0xb0, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcd, 0x00, 0x4b, 0xff, 0xff, 0x69, 0x56, 0x03, 0x37, 0xff, 0x7f, 0xc8, 0x03, 0xa6, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xd0, 0x00, 0x4b, 0xff, 0xff, 0x51, + 0x56, 0x03, 0x37, 0xff, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x4b, 0xff, 0xff, 0xb9, 0x38, 0x60, 0x00, 0x08, 0x63, 0xec, 0x27, 0x7c, 0x4b, 0xff, 0xff, 0x55, + 0x80, 0xac, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, 0x63, 0xfb, 0x27, 0x84, 0x62, 0xb1, 0xf8, 0x00, + 0x7e, 0x0c, 0x28, 0x50, 0x48, 0x00, 0x00, 0xed, 0x41, 0x81, 0x00, 0x10, 0x82, 0x3b, 0x00, 0x00, + 0x2c, 0x11, 0x00, 0x00, 0x41, 0x82, 0x00, 0x68, 0x7e, 0x23, 0x8b, 0x78, 0x4b, 0xff, 0xff, 0x55, + 0x4b, 0xff, 0xff, 0xa5, 0x4b, 0xff, 0xff, 0xa1, 0x4b, 0xff, 0xfe, 0xd9, 0x41, 0x82, 0xff, 0xf4, + 0x2c, 0x1d, 0x00, 0xcc, 0x41, 0x82, 0x00, 0x48, 0x2c, 0x1d, 0x00, 0xbb, 0x41, 0x82, 0xff, 0xdc, + 0x2c, 0x1d, 0x00, 0xaa, 0x40, 0x82, 0xff, 0xdc, 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, + 0x41, 0x80, 0x00, 0x2c, 0x4b, 0xff, 0xff, 0xb4, 0x7e, 0xb5, 0xfb, 0xa6, 0x7e, 0xb2, 0xfb, 0xa6, + 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, + 0x48, 0x00, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x80, 0x4b, 0xff, 0xff, 0x25, 0x80, 0x92, 0x00, 0x00, + 0x2c, 0x04, 0x00, 0x00, 0x40, 0x82, 0xfa, 0x50, 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, + 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, + 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x7e, 0x23, 0x20, 0x50, + 0x3c, 0xa0, 0x48, 0x00, 0x52, 0x25, 0x01, 0xba, 0x90, 0xa3, 0x00, 0x00, 0x7c, 0x00, 0x18, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x1f, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x70, 0x8b, 0xd7, 0x7d, 0x4b, 0x89, 0xd6, 0x7d, 0x4a, 0x80, 0x50, 0x91, 0x5b, 0x00, 0x00, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xa8, 0x02, 0xa6, 0x3d, 0xe0, 0x80, 0x00, 0x61, 0xef, 0x28, 0xb8, + 0x63, 0xe7, 0x18, 0x08, 0x3c, 0xc0, 0x80, 0x00, 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, + 0x3c, 0x60, 0x00, 0xd0, 0x60, 0x63, 0xc0, 0xde, 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, + 0x40, 0x82, 0x00, 0x18, 0x80, 0x8f, 0x00, 0x04, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, + 0x39, 0xef, 0x00, 0x08, 0x48, 0x00, 0x00, 0x0c, 0x7f, 0xa8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x80, 0x6f, 0x00, 0x00, 0x80, 0x8f, 0x00, 0x04, 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, + 0x2f, 0x89, 0x00, 0x00, 0x39, 0x20, 0x00, 0x00, 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, + 0x74, 0x6b, 0x10, 0x00, 0x54, 0x63, 0x01, 0xfe, 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x08, 0x7e, 0x0c, 0x83, 0x78, 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, + 0x41, 0xa0, 0x00, 0x2c, 0x41, 0xa2, 0x00, 0xe4, 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xac, + 0x41, 0x82, 0x02, 0x50, 0x2c, 0x0a, 0x00, 0x05, 0x41, 0x80, 0x02, 0xd4, 0x41, 0xa2, 0x04, 0xe0, + 0x2c, 0x0a, 0x00, 0x07, 0x41, 0xa0, 0x05, 0x0c, 0x48, 0x00, 0x05, 0xf0, 0x7d, 0x8c, 0x1a, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x48, 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, + 0x2e, 0x05, 0x00, 0x01, 0x41, 0x91, 0x00, 0x2c, 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, + 0x7c, 0x89, 0x61, 0xae, 0x39, 0x29, 0x00, 0x01, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, + 0x39, 0x29, 0x00, 0x02, 0x35, 0x4a, 0xff, 0xff, 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, + 0x55, 0x8c, 0x00, 0x3a, 0x90, 0x8c, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, + 0x40, 0x9e, 0x04, 0xc8, 0x35, 0x29, 0xff, 0xff, 0x41, 0x80, 0x04, 0xc0, 0x7c, 0xa9, 0x78, 0xae, + 0x7c, 0xa9, 0x61, 0xae, 0x4b, 0xff, 0xff, 0xf0, 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, + 0x80, 0xaf, 0xff, 0xf8, 0x81, 0x6f, 0xff, 0xfc, 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, + 0x54, 0xa5, 0x27, 0x3e, 0x2e, 0x85, 0x00, 0x01, 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, + 0x7c, 0x89, 0x61, 0xae, 0x48, 0x00, 0x00, 0x10, 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x89, 0x61, 0x2e, 0x7c, 0x84, 0x5a, 0x14, 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, + 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfe, 0xdc, 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, + 0x55, 0x08, 0xf8, 0x7e, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, + 0x2d, 0x8a, 0x00, 0x05, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xb8, + 0x7d, 0x8c, 0x1a, 0x14, 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, + 0x40, 0x94, 0x00, 0x10, 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, + 0x55, 0x8c, 0x00, 0x3c, 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, + 0x7d, 0x6b, 0x48, 0x38, 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, + 0x41, 0x82, 0x00, 0x18, 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, + 0x40, 0x9a, 0x00, 0x20, 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, + 0x41, 0x99, 0x00, 0x10, 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, + 0x40, 0x8e, 0xfe, 0x40, 0x41, 0x94, 0xfe, 0x3c, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, + 0x70, 0x6c, 0x00, 0x08, 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, + 0x39, 0x8b, 0x00, 0x10, 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, + 0x91, 0x6f, 0xff, 0xf8, 0x4b, 0xff, 0xfe, 0x0c, 0x40, 0xbe, 0xfe, 0x08, 0x54, 0x69, 0x16, 0xba, + 0x54, 0x6e, 0x87, 0xfe, 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, + 0x2e, 0x8e, 0x00, 0x02, 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, + 0x7c, 0x84, 0x7a, 0x14, 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, + 0x2e, 0x8e, 0x00, 0x01, 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, + 0x41, 0x82, 0x00, 0x3c, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, + 0x7c, 0x84, 0x82, 0x14, 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, + 0x4b, 0xff, 0xfd, 0x80, 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x78, 0x40, 0x90, 0x00, 0x0c, + 0x7c, 0x86, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x6c, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x64, + 0x54, 0x89, 0x1e, 0x78, 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x48, + 0x54, 0x6b, 0x50, 0x03, 0x41, 0x82, 0x00, 0x14, 0x41, 0x81, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, + 0x41, 0xbe, 0xfd, 0x40, 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x38, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x81, 0x00, 0x10, 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x24, + 0x7d, 0xe7, 0x49, 0x2e, 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, + 0x4b, 0xff, 0xfd, 0x10, 0x40, 0xbe, 0xfd, 0x0c, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, + 0x54, 0x64, 0x04, 0x3e, 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xf4, + 0x81, 0x25, 0x00, 0x04, 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe8, 0x39, 0x29, 0xff, 0xff, + 0x91, 0x25, 0x00, 0x04, 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd8, 0x40, 0xbe, 0xfc, 0xd4, + 0x54, 0x6b, 0x16, 0xba, 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, + 0x41, 0x92, 0x00, 0x84, 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, + 0x40, 0x90, 0x00, 0x90, 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0x8c, 0x22, 0x14, 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, + 0x41, 0x9a, 0x00, 0x0c, 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, + 0x48, 0x00, 0x00, 0xf0, 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, + 0x41, 0xb9, 0x00, 0x20, 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x18, 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, + 0x91, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, + 0x4b, 0xff, 0xfc, 0x40, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, + 0x71, 0xc5, 0x00, 0x01, 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, + 0x54, 0x6a, 0x87, 0xbe, 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, + 0x3a, 0x6f, 0xff, 0xfc, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x08, 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, + 0x7d, 0x33, 0x4b, 0x78, 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, + 0x2c, 0x05, 0x00, 0x09, 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, + 0x48, 0x00, 0x00, 0x40, 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, + 0x48, 0x00, 0x00, 0x30, 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, + 0x48, 0x00, 0x00, 0x20, 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, + 0x48, 0x00, 0x00, 0x10, 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, + 0x90, 0x9a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x8c, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x84, + 0xc0, 0x5a, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, + 0x48, 0x00, 0x00, 0x08, 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x64, + 0x7d, 0x48, 0x02, 0xa6, 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, + 0x81, 0x33, 0x00, 0x00, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x44, + 0x54, 0x69, 0xc0, 0x3e, 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, + 0x7e, 0x31, 0x22, 0x14, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, + 0x38, 0xa0, 0x00, 0x00, 0x41, 0x82, 0xfb, 0x1c, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, + 0x38, 0xa5, 0x00, 0x01, 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, + 0x55, 0x31, 0x36, 0xba, 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, + 0x7d, 0xd1, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, + 0x2c, 0x09, 0x00, 0x3c, 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, + 0x41, 0x96, 0x00, 0x08, 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x28, 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, + 0x4b, 0xff, 0xff, 0xad, 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, + 0x7e, 0x24, 0x20, 0x38, 0x4b, 0xff, 0xfb, 0xc4, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xbc, + 0x7c, 0x9a, 0x23, 0x78, 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, + 0x7d, 0xe8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, + 0x55, 0xef, 0x00, 0x38, 0x4b, 0xff, 0xfa, 0x6c, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, + 0x3c, 0xa0, 0x48, 0x00, 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, + 0x40, 0xbe, 0xfa, 0x50, 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, + 0x50, 0x65, 0x07, 0xfe, 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x38, 0x40, 0xbe, 0xff, 0xbc, + 0x7d, 0x2c, 0x78, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, + 0x7d, 0x6f, 0x22, 0x14, 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, + 0x90, 0xab, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, + 0x4b, 0xff, 0xfb, 0x28, 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, + 0x7c, 0x0c, 0x20, 0x00, 0x41, 0x80, 0xfb, 0xa8, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0xa0, + 0x4b, 0xff, 0xf9, 0xe0, 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, + 0x7c, 0x05, 0x18, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, + 0x57, 0x45, 0xff, 0xff, 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, + 0x53, 0x48, 0x07, 0xfe, 0x4b, 0xff, 0xf9, 0xac, 0x2c, 0x0b, 0x00, 0x00, 0x41, 0x82, 0x01, 0x38, + 0x2c, 0x05, 0x00, 0x01, 0x41, 0x82, 0x00, 0x18, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x82, 0x00, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x70, 0x4b, 0xff, 0xf9, 0x80, 0x54, 0xcc, 0x00, 0x0c, + 0x54, 0x97, 0x46, 0x3e, 0x54, 0x98, 0xc4, 0x3e, 0x54, 0x84, 0x06, 0x3e, 0x40, 0x9e, 0x00, 0xfc, + 0x56, 0xf9, 0x06, 0x31, 0x7d, 0x9a, 0x63, 0x78, 0x7f, 0x43, 0xd2, 0x14, 0x57, 0x5a, 0x00, 0x3a, + 0x41, 0x82, 0x00, 0x18, 0x7e, 0xf7, 0x07, 0x74, 0x7e, 0xf7, 0x00, 0xd0, 0x1f, 0x37, 0x00, 0x02, + 0x3b, 0x39, 0x00, 0x04, 0x7f, 0x59, 0xd0, 0x50, 0x2c, 0x17, 0x00, 0x00, 0x41, 0x82, 0x00, 0x1c, + 0x3b, 0x20, 0x00, 0x00, 0x7e, 0xe9, 0x03, 0xa6, 0xa3, 0x7a, 0x00, 0x04, 0x7f, 0x79, 0xca, 0x78, + 0x3b, 0x5a, 0x00, 0x02, 0x42, 0x00, 0xff, 0xf4, 0x7c, 0x18, 0xc8, 0x00, 0x40, 0x82, 0x00, 0xac, + 0x4b, 0xff, 0xfe, 0x90, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x9c, 0x54, 0x77, 0xb0, 0x03, + 0x41, 0x81, 0x00, 0x88, 0x41, 0x80, 0x00, 0x8c, 0x54, 0x7e, 0x06, 0x3e, 0x1f, 0xde, 0x00, 0x02, + 0x54, 0x97, 0x00, 0x1e, 0x6e, 0xf8, 0x80, 0x00, 0x2c, 0x18, 0x00, 0x00, 0x40, 0x82, 0x00, 0x08, + 0x62, 0xf7, 0x30, 0x00, 0x54, 0x98, 0x80, 0x1e, 0x1f, 0x3e, 0x00, 0x04, 0x7f, 0x19, 0xc0, 0x50, + 0x3b, 0x20, 0x00, 0x00, 0x1f, 0x59, 0x00, 0x04, 0x7f, 0x6f, 0xd0, 0x2e, 0x7f, 0x57, 0xd0, 0x2e, + 0x3b, 0x39, 0x00, 0x01, 0x7c, 0x17, 0xc0, 0x40, 0x41, 0x81, 0x00, 0x34, 0x7c, 0x19, 0xf0, 0x00, + 0x41, 0x81, 0x00, 0x14, 0x7c, 0x1a, 0xd8, 0x00, 0x41, 0x82, 0xff, 0xdc, 0x3a, 0xf7, 0x00, 0x04, + 0x4b, 0xff, 0xff, 0xd0, 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x03, 0x00, 0x90, 0x6f, 0xff, 0xf8, + 0x92, 0xef, 0xff, 0xfc, 0x7e, 0xf0, 0xbb, 0x78, 0x48, 0x00, 0x00, 0x1c, 0x80, 0x6f, 0xff, 0xf8, + 0x60, 0x63, 0x01, 0x00, 0x90, 0x6f, 0xff, 0xf8, 0x61, 0x08, 0x00, 0x01, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x90, 0x23, 0x78, 0x54, 0x64, 0x06, 0x3e, 0x1c, 0x84, 0x00, 0x08, 0x7d, 0xe4, 0x7a, 0x14, + 0x4b, 0xff, 0xf8, 0x70, 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, + 0x54, 0x69, 0x06, 0xff, 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, 0x55, 0x17, 0xff, 0xff, + 0x40, 0x82, 0x00, 0x08, 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0xa6, 0x2b, 0x78, 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, + 0x4b, 0xff, 0xf8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; +const int codehandlerslota_size = sizeof(codehandlerslota); diff --git a/plugins/mighty/source/codes/codes.c b/plugins/mighty/source/codes/codes.c new file mode 100644 index 0000000..16f8399 --- /dev/null +++ b/plugins/mighty/source/codes/codes.c @@ -0,0 +1,313 @@ +/* +config_bytes[2] hooktypes +0 no +1 VBI +2 KPAD read +3 Joypad Hook +4 GXDraw Hook +5 GXFlush Hook +6 OSSleepThread Hook +7 AXNextFrame Hook +8 Default + +config_bytes[4] ocarina +0 no +1 yes + +config_bytes[5] paused start +0 no +1 yes + + +config_bytes[7] debugger +0 no +1 yes +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include "codehandler.h" +#include "codehandlerslota.h" +#include "codehandleronly.h" +#include "multidol.h" +#include "patchcode.h" + +#include "tools.h" +#include "config.h" +#include "codes.h" + +static const u8 *codelistend = (u8 *) 0x80003000; + +void *codelist; + +DISC_INTERFACE storage; + +void storage_shutdown(){ + //if (ocarinaoption < 4 || wbfsdevice != 1) + { + storage.shutdown(); + } +} + + +s32 load_codes(char *filename, u32 maxsize, u8 *buffer){ + char text[4]; + + if (ocarinaoption == 1){ + text[0] = 'S'; + text[1] = 'D'; + text[2] = 0; + text[3] = 0; + storage = __io_wiisd; + }else{ + text[0] = 'U'; + text[1] = 'S'; + text[2] = 'B'; + text[3] = 0; + storage = __io_usbstorage; + } + + FILE *fp; + u32 filesize; + u32 ret; + char buf[128]; + + ret = storage.startup(); + if (ret < 0) + { + printf("%s Error\n", text); + //write_font(185, 346, "%s Error", text); + return ret; + } + ret = fatMountSimple("fat", &storage); + + if (ret < 0) + { + storage_shutdown(); + printf("FAT Error\n"); + //write_font(185, 346, "FAT Error"); + return ret; + } + + fflush(stdout); + + sprintf(buf, "fat:/usb-loader/codes/%s.gct", filename); + printf("Ocaraina trying to open file %s\n", buf); + fp = fopen(buf, "rb"); + + if (!fp){ + printf("Failed to open %s\n", buf); + sprintf(buf, "fat:/apps/USBLoader/codes/%s.gct", filename); + fp = fopen(buf, "rb"); + } + + if (!fp){ + printf("Failed to open %s\n", buf); + sprintf(buf, "fat:/codes/%s.gct", filename); + fp = fopen(buf, "rb"); + } + + if (!fp){ + printf("Failed to open %s\n", buf); + sprintf(buf, "fat:/config/mighty/cheats/%s.gct", filename); + fp = fopen(buf, "rb"); + } + + if (!fp){ + fatUnmount("fat"); + storage_shutdown(); + printf("Failed to open %s\n", buf); + printf("No %s codes found\n", text); + sleep(3); + //write_font(185, 346, "No %s codes found", text); + return -1; + } + + printf("Ocaraina using %s\n", buf); + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (filesize > maxsize){ + fatUnmount("fat"); + storage_shutdown(); + printf("Too many codes\n"); + //write_font(185, 346, ""Too many codes"); + return -1; + } + + ret = fread(buffer, 1, filesize, fp); + if(ret != filesize) + { + fclose(fp); + fatUnmount("fat"); + storage_shutdown(); + printf("%s Code Error\n", text); + //write_font(185, 346, "%s Code Error", text); + return -1; + } + + fclose(fp); + + fatUnmount("fat"); + storage_shutdown(); + + return 0; +} + + + +//--------------------------------------------------------------------------------- +void load_handler() +//--------------------------------------------------------------------------------- +{ + if (hooktypeoption != 0x00) + { + if (debuggeroption == 0x01) + { + switch(gecko_channel) + { + case 0: // Slot A + memset((void*)0x80001800,0,codehandlerslota_size); + memcpy((void*)0x80001800,codehandlerslota,codehandlerslota_size); + if (pausedstartoption == 0x01) + *(u32*)0x80002798 = 1; + memcpy((void*)0x80001CDE, &codelist, 2); + memcpy((void*)0x80001CE2, ((u8*) &codelist) + 2, 2); + memcpy((void*)0x80001F5A, &codelist, 2); + memcpy((void*)0x80001F5E, ((u8*) &codelist) + 2, 2); + DCFlushRange((void*)0x80001800,codehandlerslota_size); + break; + + case 1: // slot B + memset((void*)0x80001800,0,codehandler_size); + memcpy((void*)0x80001800,codehandler,codehandler_size); + if (pausedstartoption == 0x01) + *(u32*)0x80002798 = 1; + memcpy((void*)0x80001CDE, &codelist, 2); + memcpy((void*)0x80001CE2, ((u8*) &codelist) + 2, 2); + memcpy((void*)0x80001F5A, &codelist, 2); + memcpy((void*)0x80001F5E, ((u8*) &codelist) + 2, 2); + DCFlushRange((void*)0x80001800,codehandler_size); + break; + + case 2: + memset((void*)0x80001800,0,codehandler_size); + memcpy((void*)0x80001800,codehandler,codehandler_size); + if (pausedstartoption == 0x01) + *(u32*)0x80002798 = 1; + memcpy((void*)0x80001CDE, &codelist, 2); + memcpy((void*)0x80001CE2, ((u8*) &codelist) + 2, 2); + memcpy((void*)0x80001F5A, &codelist, 2); + memcpy((void*)0x80001F5E, ((u8*) &codelist) + 2, 2); + DCFlushRange((void*)0x80001800,codehandler_size); + break; + } + } + else + { + memset((void*)0x80001800,0,codehandleronly_size); + memcpy((void*)0x80001800,codehandleronly,codehandleronly_size); + memcpy((void*)0x80001906, &codelist, 2); + memcpy((void*)0x8000190A, ((u8*) &codelist) + 2, 2); + DCFlushRange((void*)0x80001800,codehandleronly_size); + } + // Load multidol handler + memset((void*)0x80001000,0,multidol_size); + memcpy((void*)0x80001000,multidol,multidol_size); + DCFlushRange((void*)0x80001000,multidol_size); + switch(hooktypeoption) + { + case 0x01: + memcpy((void*)0x8000119C,viwiihooks,12); + memcpy((void*)0x80001198,viwiihooks+3,4); + break; + case 0x02: + memcpy((void*)0x8000119C,kpadhooks,12); + memcpy((void*)0x80001198,kpadhooks+3,4); + break; + case 0x03: + memcpy((void*)0x8000119C,joypadhooks,12); + memcpy((void*)0x80001198,joypadhooks+3,4); + break; + case 0x04: + memcpy((void*)0x8000119C,gxdrawhooks,12); + memcpy((void*)0x80001198,gxdrawhooks+3,4); + break; + case 0x05: + memcpy((void*)0x8000119C,gxflushhooks,12); + memcpy((void*)0x80001198,gxflushhooks+3,4); + break; + case 0x06: + memcpy((void*)0x8000119C,ossleepthreadhooks,12); + memcpy((void*)0x80001198,ossleepthreadhooks+3,4); + break; + case 0x07: + memcpy((void*)0x8000119C,axnextframehooks,12); + memcpy((void*)0x80001198,axnextframehooks+3,4); + break; + case 0x08: + //if (customhooksize == 16) + //{ + // memcpy((void*)0x8000119C,customhook,12); + // memcpy((void*)0x80001198,customhook+3,4); + //} + break; + case 0x09: + //memcpy((void*)0x8000119C,wpadbuttonsdownhooks,12); + //memcpy((void*)0x80001198,wpadbuttonsdownhooks+3,4); + break; + case 0x0A: + //memcpy((void*)0x8000119C,wpadbuttonsdown2hooks,12); + //memcpy((void*)0x80001198,wpadbuttonsdown2hooks+3,4); + break; + } + DCFlushRange((void*)0x80001198,16); + } +} + + +void do_codes(u64 titleid) +{ + s32 ret = 0; + char gameidbuffer[8]; + memset(gameidbuffer, 0, 8); + gameidbuffer[0] = (titleid & 0xff000000) >> 24; + gameidbuffer[1] = (titleid & 0x00ff0000) >> 16; + gameidbuffer[2] = (titleid & 0x0000ff00) >> 8; + gameidbuffer[3] = titleid & 0x000000ff; + + if (debuggeroption == 0x00) + codelist = (u8 *) 0x800022A8; + else + codelist = (u8 *) 0x800028B8; + + load_handler(); + memcpy((void *)0x80001800, gameidbuffer, 6); + + if (ocarinaoption != 0) + { + memset(codelist, 0, (u32)codelistend - (u32)codelist); + + ret = load_codes(gameidbuffer, (u32)codelistend - (u32)codelist, codelist); + if (ret >= 0) + { + printf("Codes found. Applying\n"); + //write_font(185, 346, "Codes found. Applying"); + } + sleep(3); + } + + DCFlushRange((void*)0x80000000, 0x3f00); +} + diff --git a/plugins/mighty/source/codes/codes.h b/plugins/mighty/source/codes/codes.h new file mode 100644 index 0000000..6e2e9cb --- /dev/null +++ b/plugins/mighty/source/codes/codes.h @@ -0,0 +1,7 @@ +#include + +#define gecko_channel 1 +#define pausedstartoption 0 + +void do_codes(u64 titleid); + diff --git a/plugins/mighty/source/codes/multidol.c b/plugins/mighty/source/codes/multidol.c new file mode 100644 index 0000000..eada5c2 --- /dev/null +++ b/plugins/mighty/source/codes/multidol.c @@ -0,0 +1,36 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +const unsigned char multidol[] = { + 0x7f, 0xe8, 0x03, 0xa6, 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, + 0x90, 0x01, 0x00, 0x0c, 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, + 0x90, 0x01, 0x00, 0x14, 0xbc, 0x61, 0x00, 0x18, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x18, 0xa8, + 0x3c, 0xe0, 0x80, 0x00, 0x60, 0xe7, 0x11, 0x98, 0x3e, 0x60, 0x80, 0x00, 0x62, 0x73, 0x11, 0x88, + 0x3e, 0x40, 0x4e, 0x80, 0x62, 0x52, 0x00, 0x20, 0x81, 0xc7, 0x00, 0x04, 0x81, 0xe7, 0x00, 0x08, + 0x82, 0x07, 0x00, 0x0c, 0x82, 0x27, 0x00, 0x00, 0x3c, 0x80, 0x80, 0x00, 0x3c, 0xa0, 0x81, 0x33, + 0x38, 0x84, 0xff, 0xfc, 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x04, 0x28, 0x00, 0x40, 0x80, 0x00, 0x4c, + 0x7c, 0x06, 0x70, 0x00, 0x40, 0x82, 0xff, 0xf0, 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x06, 0x78, 0x00, + 0x40, 0x82, 0xff, 0xe0, 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x06, 0x80, 0x00, 0x40, 0x82, 0xff, 0xd4, + 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x06, 0x88, 0x00, 0x40, 0x82, 0xff, 0xc8, 0x84, 0xc4, 0x00, 0x04, + 0x7c, 0x04, 0x28, 0x00, 0x40, 0x80, 0x00, 0x14, 0x7c, 0x06, 0x90, 0x00, 0x40, 0x82, 0xff, 0xf0, + 0x48, 0x00, 0x00, 0xad, 0x4b, 0xff, 0xff, 0xb0, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x10, 0x00, + 0x3e, 0x60, 0x80, 0x00, 0x62, 0x73, 0x11, 0x90, 0x3c, 0xe0, 0x80, 0x00, 0x60, 0xe7, 0x11, 0xa8, + 0x81, 0xc7, 0x00, 0x04, 0x81, 0xe7, 0x00, 0x08, 0x82, 0x07, 0x00, 0x0c, 0x82, 0x27, 0x00, 0x00, + 0x3c, 0x80, 0x80, 0x00, 0x3c, 0xa0, 0x81, 0x40, 0x38, 0x84, 0xff, 0xfc, 0x84, 0xc4, 0x00, 0x04, + 0x7c, 0x04, 0x28, 0x00, 0x40, 0x80, 0x00, 0x38, 0x7c, 0x06, 0x70, 0x00, 0x40, 0x82, 0xff, 0xf0, + 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x06, 0x78, 0x00, 0x40, 0x82, 0xff, 0xe0, 0x84, 0xc4, 0x00, 0x04, + 0x7c, 0x06, 0x80, 0x00, 0x40, 0x82, 0xff, 0xd4, 0x84, 0xc4, 0x00, 0x04, 0x7c, 0x06, 0x88, 0x00, + 0x40, 0x82, 0xff, 0xc8, 0x48, 0x00, 0x00, 0x39, 0x4b, 0xff, 0xff, 0xc4, 0x80, 0x01, 0x00, 0xac, + 0x7c, 0x08, 0x03, 0xa6, 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, + 0x7c, 0x09, 0x03, 0xa6, 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, + 0x80, 0x01, 0x00, 0x08, 0x38, 0x21, 0x00, 0xa8, 0x48, 0x00, 0x07, 0x50, 0x7e, 0x44, 0x18, 0x50, + 0x3c, 0xc0, 0x48, 0x00, 0x52, 0x46, 0x01, 0xba, 0x90, 0xc4, 0x00, 0x00, 0x90, 0xd3, 0x00, 0x00, + 0x90, 0x93, 0x00, 0x04, 0x7c, 0x00, 0x20, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x27, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xc7, 0x00, 0x4c, 0x7c, 0xe3, 0x3b, 0x78, + 0x38, 0x87, 0x00, 0x34, 0x38, 0xa7, 0x00, 0x38, 0x4e, 0x80, 0x04, 0x20, 0x7c, 0x00, 0x04, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x7f, 0xe9, 0x03, 0xa6 +}; +const int multidol_size = sizeof(multidol); diff --git a/plugins/mighty/source/codes/multidol.h b/plugins/mighty/source/codes/multidol.h new file mode 100644 index 0000000..6984bc3 --- /dev/null +++ b/plugins/mighty/source/codes/multidol.h @@ -0,0 +1,14 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +//--------------------------------------------------------------------------------- +#ifndef _multidol_h_ +#define _multidol_h_ +//--------------------------------------------------------------------------------- +extern const unsigned char multidol[]; +extern const int multidol_size; +//--------------------------------------------------------------------------------- +#endif //_multidol_h_ +//--------------------------------------------------------------------------------- diff --git a/plugins/mighty/source/codes/patchcode.c b/plugins/mighty/source/codes/patchcode.c new file mode 100644 index 0000000..d90f152 --- /dev/null +++ b/plugins/mighty/source/codes/patchcode.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include +#include + +#include "patchcode.h" +#include "config.h" +#include "codes.h" + +extern void patchhook(u32 address, u32 len); +extern void patchhook2(u32 address, u32 len); +extern void patchhook3(u32 address, u32 len); + +/* +extern void multidolpatchone(u32 address, u32 len); +extern void multidolpatchtwo(u32 address, u32 len); +*/ + +/* +extern void regionfreejap(u32 address, u32 len); +extern void regionfreeusa(u32 address, u32 len); +extern void regionfreepal(u32 address, u32 len); + +extern void removehealthcheck(u32 address, u32 len); + +extern void copyflagcheck1(u32 address, u32 len); +extern void copyflagcheck2(u32 address, u32 len); +extern void copyflagcheck3(u32 address, u32 len); +extern void copyflagcheck4(u32 address, u32 len); +extern void copyflagcheck5(u32 address, u32 len); + +extern void patchupdatecheck(u32 address, u32 len); + +extern void movedvdhooks(u32 address, u32 len); +*/ + +extern void multidolhook(u32 address); + +/* +extern void langvipatch(u32 address, u32 len, u8 langbyte); +extern void vipatch(u32 address, u32 len); +*/ + +static const u32 multidolpatch1[2] = { + 0x3C03FFB4,0x28004F43 +}; +/* +static const u32 healthcheckhook[2] = { + 0x41810010,0x881D007D +}; + +static const u32 updatecheckhook[3] = { + 0x80650050,0x80850054,0xA0A50058 +}; +*/ +static const u32 multidolpatch2[2] = { + 0x3F608000, 0x807B0018 +}; +/* +static const u32 recoveryhooks[3] = { + 0xA00100AC,0x5400073E,0x2C00000F +}; + +static const u32 nocopyflag1[3] = { + 0x540007FF, 0x4182001C, 0x80630068 +}; + +static const u32 nocopyflag2[3] = { + 0x540007FF, 0x41820024, 0x387E12E2 +}; + +// this one is for the GH3 and VC saves +//static const u32 nocopyflag3[5] = { +// 0x2C030000, 0x40820010, 0x88010020, 0x28000002, 0x41820234 +//}; + +static const u32 nocopyflag3[5] = { + 0x2C030000, 0x41820200,0x48000058,0x38610100 +}; +// this removes the display warning for no copy VC and GH3 saves +static const u32 nocopyflag4[4] = { + 0x80010008, 0x2C000000, 0x4182000C, 0x3BE00001 +}; + +static const u32 nocopyflag5[3] = { + 0x801D0024,0x540007FF,0x41820024 +}; + +static const u32 movedvdpatch[3] = { + 0x2C040000, 0x41820120, 0x3C608109 +}; + +static const u32 regionfreehooks[5] = { + 0x7C600774, 0x2C000001, 0x41820030,0x40800010,0x2C000000 +}; + +static const u32 cIOScode[16] = { + 0x7f06c378, 0x7f25cb78, 0x387e02c0, 0x4cc63182 +}; + +static const u32 cIOSblock[16] = { + 0x2C1800F9, 0x40820008, 0x3B000024 +}; + +static const u32 fwritepatch[8] = { + 0x9421FFD0,0x7C0802A6,0x90010034,0xBF210014,0x7C9B2378,0x7CDC3378,0x7C7A1B78,0x7CB92B78 // bushing fwrite +}; + +static const u32 vipatchcode[3] = { +0x4182000C,0x4180001C,0x48000018 +}; +*/ +const u32 viwiihooks[4] = { + 0x7CE33B78,0x38870034,0x38A70038,0x38C7004C +}; + +const u32 kpadhooks[4] = { + 0x9A3F005E,0x38AE0080,0x389FFFFC,0x7E0903A6 +}; + +const u32 kpadoldhooks[6] = { + 0x801D0060, 0x901E0060, 0x801D0064, 0x901E0064, 0x801D0068, 0x901E0068 +}; + +const u32 joypadhooks[4] = { + 0x3AB50001, 0x3A73000C, 0x2C150004, 0x3B18000C +}; + +const u32 gxdrawhooks[4] = { + 0x3CA0CC01, 0x38000061, 0x3C804500, 0x98058000 +}; + +const u32 gxflushhooks[4] = { + 0x90010014, 0x800305FC, 0x2C000000, 0x41820008 +}; + +const u32 ossleepthreadhooks[4] = { + 0x90A402E0, 0x806502E4, 0x908502E4, 0x2C030000 +}; + +const u32 axnextframehooks[4] = { + 0x3800000E, 0x7FE3FB78, 0xB0050000, 0x38800080 +}; + +const u32 wpadbuttonsdownhooks[4] = { + 0x7D6B4A14, 0x816B0010, 0x7D635B78, 0x4E800020 +}; + +const u32 wpadbuttonsdown2hooks[4] = { + 0x7D6B4A14, 0x800B0010, 0x7C030378, 0x4E800020 +}; + +/* +const u32 multidolhooks[4] = { + 0x7C0004AC, 0x4C00012C, 0x7FE903A6, 0x4E800420 +}; +*/ +const u32 multidolchanhooks[4] = { + 0x4200FFF4, 0x48000004, 0x38800000, 0x4E800020 +}; + + +/* +const u32 langpatch[3] = { + 0x7C600775, 0x40820010, 0x38000000 +}; + +static const u32 oldpatch002[3] = { + 0x2C000000, 0x40820214, 0x3C608000 +}; + +static const u32 newpatch002[3] = { + 0x2C000000, 0x48000214, 0x3C608000 +}; +*/ +/* +//--------------------------------------------------------------------------------- +void dogamehooks(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + switch(hooktypeoption) + { + + case 0x00: + + break; + + case 0x01: + if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x02: + + if(memcmp(addr_start, kpadhooks, sizeof(kpadhooks))==0){ + patchhook((u32)addr_start, len); + } + + if(memcmp(addr_start, kpadoldhooks, sizeof(kpadoldhooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x03: + + if(memcmp(addr_start, joypadhooks, sizeof(joypadhooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x04: + + if(memcmp(addr_start, gxdrawhooks, sizeof(gxdrawhooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x05: + + if(memcmp(addr_start, gxflushhooks, sizeof(gxflushhooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x06: + + if(memcmp(addr_start, ossleepthreadhooks, sizeof(ossleepthreadhooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x07: + + if(memcmp(addr_start, axnextframehooks, sizeof(axnextframehooks))==0){ + patchhook((u32)addr_start, len); + } + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + + case 0x08: + + //if(memcmp(addr_start, customhook, customhooksize)==0){ + // patchhook((u32)addr_start, len); + //} + if(memcmp(addr_start, multidolhooks, sizeof(multidolhooks))==0){ + multidolhook((u32)addr_start+sizeof(multidolhooks)-4); + } + break; + } + addr_start += 4; + } +} +*/ +/* +//--------------------------------------------------------------------------------- +void patchmenu(void *addr, u32 len, int patchnum) +//--------------------------------------------------------------------------------- +{ + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + switch (patchnum) + { + case 0: + if(memcmp(addr_start, recoveryhooks, sizeof(recoveryhooks))==0){ + patchhook3((u32)addr_start, len); + } + break; + + case 1: + if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0){ + patchhook2((u32)addr_start, len); + } + + break; + + case 2: + // jap region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreejap((u32)addr_start, len); + } + + // usa region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreeusa((u32)addr_start, len); + } + + // pal region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreepal((u32)addr_start, len); + } + + // skip disc update + if(memcmp(addr_start, updatecheckhook, sizeof(updatecheckhook))==0){ + patchupdatecheck((u32)addr_start, len); + } + break; + + + case 3: + if(memcmp(addr_start, healthcheckhook, sizeof(healthcheckhook))==0){ + removehealthcheck((u32)addr_start, len); + } + break; + + // no copy flags + case 4: + // Remove the actual flag so can copy back + if(memcmp(addr_start, nocopyflag5, sizeof(nocopyflag5))==0){ + copyflagcheck5((u32)addr_start, len); + } + + + if(memcmp(addr_start, nocopyflag1, sizeof(nocopyflag1))==0){ + copyflagcheck1((u32)addr_start, len); + } + + if(memcmp(addr_start, nocopyflag2, sizeof(nocopyflag2))==0){ + copyflagcheck2((u32)addr_start, len); + } + + // no VC and GH3 save + if(memcmp(addr_start, nocopyflag3, sizeof(nocopyflag2))==0){ + copyflagcheck3((u32)addr_start, len); + } + // no VC and GH3 save display remove + if(memcmp(addr_start, nocopyflag4, sizeof(nocopyflag4))==0){ + copyflagcheck4((u32)addr_start, len); + } + + break; + + case 5: + if(memcmp(addr_start, movedvdpatch, sizeof(movedvdpatch))==0){ + movedvdhooks((u32)addr_start, len); + } + break; + + // multidol + case 6: + if(memcmp(addr_start, multidolpatch1, sizeof(multidolpatch1))==0){ + multidolpatchone((u32)addr_start, len); + } + if(memcmp(addr_start, multidolpatch2, sizeof(multidolpatch2))==0){ + multidolpatchtwo((u32)addr_start, len); + } + break; + case 7: + if(memcmp(addr_start, cIOScode, sizeof(cIOScode))==0) + memcpy(addr_start, cIOSblock, sizeof(cIOSblock)); + break; + } + addr_start += 4; + } +} +*/ +/* +//--------------------------------------------------------------------------------- +void langpatcher(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, langpatch, sizeof(langpatch))==0) { + langvipatch((u32)addr_start, len, config_bytes[0]); + } + addr_start += 4; + } +} + +//--------------------------------------------------------------------------------- +void vidolpatcher(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, vipatchcode, sizeof(vipatchcode))==0) { + vipatch((u32)addr_start, len); + } + addr_start += 4; + } +} +*/ +//--------------------------------------------------------------------------------- +bool dochannelhooks(void *addr, u32 len, bool bootcontentloaded) +//--------------------------------------------------------------------------------- +{ + void *addr_start = addr; + void *addr_end = addr+len; + bool patched = false; + bool multidolpatched = false; + + while(addr_start < addr_end) + { + switch(hooktypeoption) + { + + case 0x00: + + break; + + case 0x01: + if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x02: + + if(memcmp(addr_start, kpadhooks, sizeof(kpadhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + + if(memcmp(addr_start, kpadoldhooks, sizeof(kpadoldhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x03: + + if(memcmp(addr_start, joypadhooks, sizeof(joypadhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x04: + + if(memcmp(addr_start, gxdrawhooks, sizeof(gxdrawhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x05: + + if(memcmp(addr_start, gxflushhooks, sizeof(gxflushhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x06: + + if(memcmp(addr_start, ossleepthreadhooks, sizeof(ossleepthreadhooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x07: + + if(memcmp(addr_start, axnextframehooks, sizeof(axnextframehooks))==0) + { + patchhook((u32)addr_start, len); + patched = true; + } + break; + + case 0x08: + + //if(memcmp(addr_start, customhook, customhooksize)==0){ + // patchhook((u32)addr_start, len); + //patched = true; + //} + break; + } + if (hooktypeoption != 0) + { + if(memcmp(addr_start, multidolchanhooks, sizeof(multidolchanhooks))==0) + { + *(((u32*)addr_start)+1) = 0x7FE802A6; + DCFlushRange(((u32*)addr_start)+1, 4); + multidolhook((u32)addr_start+sizeof(multidolchanhooks)-4); + multidolpatched = true; + } + } + + addr_start += 4; + } + + if (bootcontentloaded) + { + return multidolpatched; + } else + { + return patched; + } +} +/* +//--------------------------------------------------------------------------------- +void patch_002(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, oldpatch002, sizeof(oldpatch002))==0){ + memcpy(addr_start, newpatch002, sizeof(newpatch002)); + DCFlushRange(addr_start,sizeof(newpatch002)); + break; + } + + addr_start += 4; + } +} +*/ +/* +//--------------------------------------------------------------------------------- +void patchdebug(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + + if(memcmp(addr_start, fwritepatch, sizeof(fwritepatch))==0) { + + if (gecko_channel == 0) + memcpy(addr_start,fwrite_patch_slota_bin,fwrite_patch_slota_bin_len); + else if (gecko_channel == 1) + memcpy(addr_start,fwrite_patch_bin,fwrite_patch_bin_len); + // apply patch + } + addr_start += 4; + } +} +*/ +/* +//--------------------------------------------------------------------------------- +void determine_libogc_hook(void *addr, u32 len) +//--------------------------------------------------------------------------------- +{ + void *addr_start = addr; + void *addr_end = addr+len; + + hooktypeoption = 0x09; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, wpadbuttonsdown2hooks, sizeof(wpadbuttonsdown2hooks))==0){ + hooktypeoption = 0x0A; + break; + } + + addr_start += 4; + } +} +*/ + diff --git a/plugins/mighty/source/codes/patchcode.h b/plugins/mighty/source/codes/patchcode.h new file mode 100644 index 0000000..c043fc3 --- /dev/null +++ b/plugins/mighty/source/codes/patchcode.h @@ -0,0 +1,25 @@ +#ifndef __PATCHCODE_H__ +#define __PATCHCODE_H__ + +//--------------------------------------------------------------------------------- +extern const u32 viwiihooks[4]; +extern const u32 kpadhooks[4]; +extern const u32 joypadhooks[4]; +extern const u32 gxdrawhooks[4]; +extern const u32 gxflushhooks[4]; +extern const u32 ossleepthreadhooks[4]; +extern const u32 axnextframehooks[4]; +//extern const u32 wpadbuttonsdownhooks[4]; +//extern const u32 wpadbuttonsdown2hooks[4]; +//--------------------------------------------------------------------------------- +//void dogamehooks(void *addr, u32 len); +//void patchmenu(void *addr, u32 len, int patchnum); +//void langpatcher(void *addr, u32 len); +//void vidolpatcher(void *addr, u32 len); +bool dochannelhooks(void *addr, u32 len, bool bootcontentloaded); +//void patch_002(void *addr, u32 len); +//void patchdebug(void *addr, u32 len); +//void determine_libogc_hook(void *addr, u32 len); +//--------------------------------------------------------------------------------- + +#endif diff --git a/plugins/mighty/source/codes/patchhook.S b/plugins/mighty/source/codes/patchhook.S new file mode 100644 index 0000000..abdc4af --- /dev/null +++ b/plugins/mighty/source/codes/patchhook.S @@ -0,0 +1,505 @@ +.text +.set r0,0; .set sp,1; .set r2,2; .set r3,3; .set r4,4 +.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9 +.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14 +.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19 +.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24 +.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29 +.set r30,30; .set r31,31 + + +.globl patchhook # r3 address +patchhook: + mtctr r4 + lis r6, 0x4E80 + ori r6, r6, 0x0020 # blr +findblr: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebranch + addi r3, r3, 4 # next word + bdnz findblr # loop length + b exit # stop unhooked game hanging + +writebranch: + lis r4, 0x8000 # 800018A0 hook location (source) + ori r4, r4, 0x18A8 + subf r4, r3, r4 # subtract r3 from r4 and place in r4 + lis r5, 0x3FF + ori r5, r5, 0xFFFF # 0x3FFFFFF + and r4, r4, r5 + lis r5, 0x4800 # 0x48000000 + or r4, r4, r5 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit: + blr # return + + .globl patchhook2 # r3 address +patchhook2: + mtctr r4 + lis r6, 0x4E80 + ori r6, r6, 0x0020 # blr +findblr2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebranch2 + addi r3, r3, 4 # next word + bdnz findblr2 # loop length + b exit2 # stop unhooked game hanging + +writebranch2: + lis r4, 0x8000 # 81700000 our temp patcher + ori r4, r4, 0x18a8 + subf r4, r3, r4 # subtract r3 from r4 and place in r4 + lis r5, 0x3FF + ori r5, r5, 0xFFFF # 0x3FFFFFF + and r4, r4, r5 + lis r5, 0x4800 # 0x48000000 + or r4, r4, r5 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit2: + blr # return + +.globl patchhook3 # r3 address +patchhook3: + mtctr r4 + lis r6, 0x4BFF + ori r6, r6, 0xE955 # blr +findbne: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebl + addi r3, r3, 4 # next word + bdnz findbne # loop length + b exit3 # stop unhooked game hanging + +writebl: + lis r4, 0x4BFF # 81700000 our temp patcher + ori r4, r4, 0xEA91 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit3: + blr # return + +.globl multidolpatchone # r3 address +multidolpatchone: + mtctr r4 + lis r6, 0x3800 + ori r6, r6, 0x0001 # (li r0,1) +findmulti: + lwz r5, 0(r3) + cmpw r6, r5 + beq writemulti + subi r3, r3, 4 # go back + bdnz findmulti # loop length + b exit5 # stop unhooked game hanging + +writemulti: + lis r4, 0x8170 # 81700000 + ori r4, r4, 0x0020 + subf r18, r3, r4 # subf r18,(source),(dest) + lis r6, 0x4800 + ori r6,r6,1 + rlwimi r6,r18,0,6,29 + stw r6,0(r3) + stw r6,0(r19) + stw r3,4(r19) + dcbf r0, r3 + sync + icbi r0, r3 + isync +exit5: + blr # return + +.globl multidolpatchtwo # r3 address +multidolpatchtwo: + mtctr r4 + lis r6, 0x3F60 + ori r6, r6, 0x8000 # (lis r27,-32768) +findmulti2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writemulti2 + addi r3, r3, 4 # go forward + bdnz findmulti2 # loop length + b exit6 # stop unhooked game hanging + +writemulti2: + lis r4, 0x8170 # 81700020 + ori r4, r4, 0x0000 + subf r18, r3, r4 # subf r18,(source),(dest) + lis r6, 0x4800 + ori r6,r6,1 + rlwimi r6,r18,0,6,29 + stw r6,0(r3) + stw r6,0(r19) + stw r3,4(r19) + dcbf r0, r3 + sync + icbi r0, r3 + isync +exit6: + blr # return + +.globl multidolhook # r3 address +multidolhook: + lis r4, 0x8000 # 80001000 hook location (source) + ori r4, r4, 0x1000 + subf r4, r3, r4 # subtract r3 from r4 and place in r4 + lis r5, 0x3FF + ori r5, r5, 0xFFFF # 0x3FFFFFF + and r4, r4, r5 + lis r5, 0x4800 # 0x48000000 + or r4, r4, r5 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 + blr # return + + +.globl langvipatch # r3 address, r4 len, r5 lang byte +langvipatch: + mtctr r4 + lis r6, 0x8861 + ori r6, r6, 0x0008 # lbz r3, 8(sp) +findlang: + lwz r7, 0(r3) + cmpw r6, r7 + beq patchlang + addi r3, r3, 4 # next word + bdnz findlang # loop length + b exitlang # stop unhooked game hanging + +patchlang: + + lis r4, 0x3860 # 0x38600001 li %r3, 1 # eng + add r4, r4, r5 +gofinal: + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitlang: + blr # return + +.globl vipatch # r3 address +vipatch: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0xFFFE +findvi: + lwz r5, 0(r3) + cmpw r6, r5 + beq patchvi + addi r3, r3, 4 # next word + bdnz findvi # loop length + b exitvi # stop unhooked game hanging + +patchvi: + lis r4, 0x8000 + ori r4, r4, 0x0003 + lbz r5, 0(r4) + cmpwi r5, 0x45 # USA + beq patchusa + cmpwi r5, 0x4A + beq patchjap2 # JAP + b exitvi +patchjap2: + lis r4, 0x3800 + ori r4, r4, 0x0001 + b gofinal2 +patchusa: + lis r4, 0x3800 + ori r4, r4, 0x0000 +gofinal2: + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitvi: + blr # return + +.globl regionfreejap # r3 address +regionfreejap: + mtctr r4 + lis r6, 0x2C1B + ori r6, r6, 0x0000 # blr +findjap: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop + addi r3, r3, 4 # next word + bdnz findjap # loop length + b exitjap # stop unhooked game hanging + +writenop: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitjap: + blr # return + +.globl regionfreeusa # r3 address +regionfreeusa: + mtctr r4 + lis r6, 0x281B + ori r6, r6, 0x0001 # blr +findusa: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop1 + addi r3, r3, 4 # next word + bdnz findusa # loop length + b exitusa # stop unhooked game hanging + +writenop1: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitusa: + blr # return + +.globl regionfreepal # r3 address +regionfreepal: + mtctr r4 + lis r6, 0x281B + ori r6, r6, 0x0002 # blr +findpal: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop2 + addi r3, r3, 4 # next word + bdnz findpal # loop length + b exitpal # stop unhooked game hanging + +writenop2: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 + + lis r6, 0x4082 + ori r6, r6, 0x001C # bne loc_81377A2C +findextra: #this is just the bne to b patch + lwz r5, 0(r3) + cmpw r6, r5 + beq writeb + addi r3, r3, 4 # next word + bdnz findextra # loop length + b exitpal # stop unhooked game hanging + +writeb: + addi r3, r3, 4 # next word + lis r4, 0x4800 + ori r4, r4, 0x001c # b loc_81377A2C + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitpal: + blr # return + +.globl removehealthcheck # r3 address +removehealthcheck: + mtctr r4 + lis r6, 0x4182 + ori r6, r6, 0x004C # blr +findhe: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebhe + addi r3, r3, 4 # next word + bdnz findhe # loop length + b exithe # stop unhooked game hanging + +writebhe: + lis r4, 0x6000 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exithe: + blr # return + + + +.globl patchupdatecheck # r3 address +patchupdatecheck: + mtctr r4 + lis r6, 0x4082 + ori r6, r6, 0x0020 # blr +finduc: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenopuc + addi r3, r3, 4 # next word + bdnz finduc # loop length + b exituc # stop unhooked game hanging + +writenopuc: + lis r4, 0x6000 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exituc: + blr # return + + + + +.globl copyflagcheck1 # r3 address +copyflagcheck1: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0x07FF +findncf1: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf1 + subi r3, r3, 4 # next word + bdnz findncf1 # loop length + b exitncf1 # stop unhooked game hanging + +writencf1: + lis r4, 0x7C00 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf1: + blr # return + +.globl copyflagcheck2 # r3 address +copyflagcheck2: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0x07FF +findncf2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf2 + subi r3, r3, 4 # next word + bdnz findncf2 # loop length + b exitncf2 # stop unhooked game hanging + +writencf2: + lis r4, 0x7C00 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf2: + blr # return + + +.globl copyflagcheck3 # r3 address +copyflagcheck3: +findncf3: + addi r3, r3, 20 # go back one dword (4 bytes) + lwz r5, 0(r3) +writencf3: + lis r4, 0x3860 + ori r4, r4, 0x0001 # li r3,1 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf3: + blr # return + + +.globl copyflagcheck4 # r3 address +copyflagcheck4: + mtctr r4 + lis r6, 0x3BE0 + ori r6, r6, 0x0001 # li r31,1 +findncf4: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf4 + addi r3, r3, 4 # next word + bdnz findncf4 # loop length + b exitncf4 # stop unhooked game hanging + +writencf4: + lis r4, 0x3BE0 + ori r4, r4, 0x0000 # change this to 3BE00000 (li r31,0) + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf4: + blr # return + +.globl copyflagcheck5 # r3 address +copyflagcheck5: + mtctr r4 + lis r6, 0x4182 + ori r6, r6, 0x0024 # beq loc_8134AA60 +findncf5: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf5 + addi r3, r3, 4 # next word + bdnz findncf5 # loop length + b exitncf5 # stop unhooked game hanging + +writencf5: + #addi r3, r3, 8 # skip 2 + + lis r4, 0x801D + ori r4, r4, 0x0024 # change to 801D0024 (lwz r0,36(r29)) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x5400 + ori r4, r4, 0x003C # change to 5400003C (rlwinm r0,r0,0,0,30) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x901D + ori r4, r4, 0x0024 # change to 901D0024 (stw r0,36(r29)) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x4800 + ori r4, r4, 0x0018 # change to 48000018 (b 0x8134aa60) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 +exitncf5: + blr # return + +.globl movedvdhooks # r3 address +movedvdhooks: + lis r6, 0x4182 + ori r6, r6, 0x0120 # beq loc_813A7938 +findmd1: + addi r3, r3, 4 # next word + lwz r5, 0(r3) +writemd1: + lis r4, 0x6000 + ori r4, r4, 0x0000 # nop + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitmd1: + blr # return diff --git a/plugins/mighty/source/config.h b/plugins/mighty/source/config.h new file mode 100644 index 0000000..182e7e9 --- /dev/null +++ b/plugins/mighty/source/config.h @@ -0,0 +1,27 @@ +/******************************************************************************* + * config.h + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#include + +s32 videooption; +u32 languageoption; +u32 videopatchoption; + +u32 hooktypeoption; +u32 debuggeroption; +u32 ocarinaoption; + +u32 bootmethodoption; + diff --git a/plugins/mighty/source/fat_mine.c b/plugins/mighty/source/fat_mine.c new file mode 100644 index 0000000..2303f0d --- /dev/null +++ b/plugins/mighty/source/fat_mine.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include //for mkdir + +#define DEV_MOUNT "fat" +#include "fat_mine.h" +#include +#include "usbstorage.h" +extern const DISC_INTERFACE __io_usb2storage; + +const DISC_INTERFACE* interface; +/*typedef struct { + // Device mount point + char *mount; + + // Device interface + const DISC_INTERFACE *interface; +} fatDevice;*/ + +s32 Fat_Mount(int dev){ + s32 ret; + + if(dev==SD) + interface=&__io_wiisd; + else if(dev==USB) + interface=&__io_wiiums; + else + return -1; + + + // Initialize SDHC interface + ret = interface->startup(); + if (!ret) + return -1; + + // Mount device + ret = fatMountSimple(DEV_MOUNT, interface); + if (!ret) + return -2; + + return 0; +} + +s32 Fat_Unmount(void){ + s32 ret; + + // Unmount device + fatUnmount(DEV_MOUNT); + + // Shutdown SDHC interface + ret = interface->shutdown(); + if (!ret) + return -1; + + return 0; +} + +s32 Fat_ReadFile(const char *filepath, void **outbuf){ + FILE *fp = NULL; + void *buffer = NULL; + + u32 filelen; + + s32 ret; + + /* Open file */ + fp = fopen(filepath, "rb"); + if (!fp) + goto err; + + /* Get filesize */ + fseek(fp, 0, SEEK_END); + filelen = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* Allocate memory */ + buffer = malloc(filelen); + if (!buffer) + goto err; + + /* Read file */ + ret = fread(buffer, 1, filelen, fp); + if (ret != filelen) + goto err; + + /* Set pointer */ + *outbuf = buffer; + + goto out; + +err: + /* Free memory */ + if (buffer) + free(buffer); + + /* Error code */ + ret = -1; + +out: + /* Close file */ + if (fp) + fclose(fp); + + return ret; +} + +int Fat_MakeDir(const char *dirname){ + int ret=-1; + DIR *dir; + + //if(dirname[strlen(dirname)-2]=='/') + // return false; + + dir=opendir(dirname); + if(dir){ + ret=1; + closedir(dir); + }else{ + mkdir(dirname, S_IREAD | S_IWRITE); + ret=0; + } + + return ret; +} + +bool Fat_CheckFile(const char *filepath){ + FILE *fp = NULL; + + /* Open file */ + fp = fopen(filepath, "rb"); + if (!fp) + return false; + + fclose(fp); + + return true; +} + +s32 Fat_SaveFile(const char *filepath, void **outbuf, u32 outlen){ + s32 ret; + FILE *fd; + fd = fopen(filepath, "wb"); + if(fd){ + ret=fwrite(*outbuf, 1, outlen, fd); + fclose(fd); + //printf(" FWRITE: %d ",ret); + }else{ + ret=-1; + } + + return ret; +} \ No newline at end of file diff --git a/plugins/mighty/source/fat_mine.h b/plugins/mighty/source/fat_mine.h new file mode 100644 index 0000000..4cdab86 --- /dev/null +++ b/plugins/mighty/source/fat_mine.h @@ -0,0 +1,15 @@ +#ifndef _FAT_H_ +#define _FAT_H_ + +#define SD 1 +#define USB 2 + +/* Prototypes */ +s32 Fat_Mount(int); +s32 Fat_Unmount(void); +s32 Fat_ReadFile(const char *, void **); +int Fat_MakeDir(const char *); +bool Fat_CheckFile(const char *); +s32 Fat_SaveFile(const char *, void **, u32); + +#endif diff --git a/plugins/mighty/source/lz77.c b/plugins/mighty/source/lz77.c new file mode 100644 index 0000000..57a5863 --- /dev/null +++ b/plugins/mighty/source/lz77.c @@ -0,0 +1,219 @@ +/******************************************************************************* + * lz77.c + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "lz77.h" +#include "tools.h" + +u32 getLowestMem2Address() { return (u32) SYS_GetArena2Lo(); } + +u32 packBytes(int a, int b, int c, int d) +{ + return (d << 24) | (c << 16) | (b << 8) | (a); +} + +s32 __decompressLZ77_11(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) +{ + int x, y; + + u8 *out = NULL; + + u32 compressedPos = 0x4; + u32 decompressedPos = 0x0; + u32 decompressedSize = 0; + + decompressedSize = packBytes(in[0], in[1], in[2], in[3]) >> 8; + + if (!decompressedSize) + { + decompressedSize = packBytes(in[4], in[5], in[6], in[7]); + compressedPos += 0x4; + } + + printf("Decompressed size : %i\n", decompressedSize); + + out = allocate_memory(decompressedSize); + if (out == NULL) + { + printf("Out of memory\n"); + return -1; + } + + while (compressedPos < inputLen && decompressedPos < decompressedSize) + { + u8 byteFlag = in[compressedPos]; + compressedPos++; + + for (x = 7; x >= 0; x--) + { + if ((byteFlag & (1 << x)) > 0) + { + u8 first = in[compressedPos]; + u8 second = in[compressedPos + 1]; + + u32 pos, copyLen; + + if (first < 0x20) + { + u8 third = in[compressedPos + 2]; + + if (first >= 0x10) + { + u32 fourth = in[compressedPos + 3]; + + pos = (u32)(((third & 0xF) << 8) | fourth) + 1; + copyLen = (u32)((second << 4) | ((first & 0xF) << 12) | (third >> 4)) + 273; + + compressedPos += 4; + } else + { + pos = (u32)(((second & 0xF) << 8) | third) + 1; + copyLen = (u32)(((first & 0xF) << 4) | (second >> 4)) + 17; + + compressedPos += 3; + } + } else + { + pos = (u32)(((first & 0xF) << 8) | second) + 1; + copyLen = (u32)(first >> 4) + 1; + + compressedPos += 2; + } + + for (y = 0; y < copyLen; y++) + { + out[decompressedPos + y] = out[decompressedPos - pos + y]; + } + + decompressedPos += copyLen; + } else + { + out[decompressedPos] = in[compressedPos]; + + decompressedPos++; + compressedPos++; + } + + if (compressedPos >= inputLen || decompressedPos >= decompressedSize) + break; + } + } + *output = out; + *outputLen = decompressedSize; + return 0; +} + +s32 __decompressLZ77_10(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) +{ + int x, y; + + u8 *out = NULL; + + u32 compressedPos = 0; + u32 decompressedSize = 0x4; + u32 decompressedPos = 0; + + decompressedSize = packBytes(in[0], in[1], in[2], in[3]) >> 8; + + //int compressionType = (packBytes(in[0], in[1], in[2], in[3]) >> 4) & 0xF; + + printf("Decompressed size : %i\n", decompressedSize); + + out = allocate_memory(decompressedSize); + if (out == NULL) + { + printf("Out of memory\n"); + return -1; + } + + compressedPos += 0x4; + + while (decompressedPos < decompressedSize) + { + u8 flag = *(u8*)(in + compressedPos); + compressedPos += 1; + + for (x = 0; x < 8; x++) + { + if (flag & 0x80) + { + u8 first = in[compressedPos]; + u8 second = in[compressedPos + 1]; + + u16 pos = (u16)((((first << 8) + second) & 0xFFF) + 1); + u8 copyLen = (u8)(3 + ((first >> 4) & 0xF)); + + for (y = 0; y < copyLen; y++) + { + out[decompressedPos + y] = out[decompressedPos - pos + (y % pos)]; + } + + compressedPos += 2; + decompressedPos += copyLen; + } else + { + out[decompressedPos] = in[compressedPos]; + compressedPos += 1; + decompressedPos += 1; + } + + flag <<= 1; + + if (decompressedPos >= decompressedSize) + break; + } + } + + *output = out; + *outputLen = decompressedSize; + return 0; +} + +int isLZ77compressed(u8 *buffer) +{ + if ((buffer[0] == LZ77_0x10_FLAG) || (buffer[0] == LZ77_0x11_FLAG)) + { + return 1; + } + + return 0; +} + +int decompressLZ77content(u8 *buffer, u32 length, u8 **output, u32 *outputLen) +{ + int ret; + switch (buffer[0]) + { + case LZ77_0x10_FLAG: + printf("LZ77 variant 0x10 compressed content...unpacking may take a while...\n"); + ret = __decompressLZ77_10(buffer, length, output, outputLen); + break; + case LZ77_0x11_FLAG: + printf("LZ77 variant 0x11 compressed content...unpacking may take a while...\n"); + ret = __decompressLZ77_11(buffer, length, output, outputLen); + break; + default: + printf("Not compressed ...\n"); + ret = -1; + break; + } + return ret; +} diff --git a/plugins/mighty/source/lz77.h b/plugins/mighty/source/lz77.h new file mode 100644 index 0000000..27b1c0b --- /dev/null +++ b/plugins/mighty/source/lz77.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * lz77.h + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#ifndef _LZ77_MODULE +#define _LZ77_MODULE + +#define LZ77_0x10_FLAG 0x10 +#define LZ77_0x11_FLAG 0x11 + +int isLZ77compressed(u8 *buffer); +int decompressLZ77content(u8 *buffer, u32 length, u8 **output, u32 *outputLen); + +#endif + diff --git a/plugins/mighty/source/menu.c b/plugins/mighty/source/menu.c new file mode 100644 index 0000000..5b5f4f4 --- /dev/null +++ b/plugins/mighty/source/menu.c @@ -0,0 +1,808 @@ +/* menu.c + * + * Triiforce mod by Marc + * + * Copyright (c) 2009 The Lemon Man, Nicksasa & WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + *********************************************/ +#include +#include +#include +#include +#include +#include +#include +#include //for network +#include +#include //for mkdir + +#include "tools.h" +#include "lz77.h" +#include "config.h" +#include "patch.h" +#include "codes/codes.h" +#include "codes/patchcode.h" +#include "nand.h" +#include "fat_mine.h" +#include "video.h" +#include "sonido.h" +#include "menu.h" +#include "fat.h" + +// Constants +#define ARROWS_X 24 +#define ARROWS_Y 210 +#define ARROWS_WIDTH 20 +#define MIGHTY_PATH "fat:/config/mighty/" +#define IMAGES_PREFIX "channels" +#define MIGHTY_CONFIG_FILE "mighty_channels.cfg" +#define MAXGAMES 800 +#define EMPTY -1 + +#define BLACK 0x000000FF +#define YELLOW 0xFFFF00FF +#define WHITE 0xFFFFFFFF +#define ORANGE 0xeab000ff + + +// Variables for MIGHTY +static char tempString[128]; +static int nandMode=0; + +extern GXRModeObj *vmode; +extern u32* framebuffer; + +const u8 COLS[]={3, 4}; +#define ROWS 3 +const u8 FIRSTCOL[]={136, 112}; +#define FIRSTROW 96 +const u8 SEPARACIONX[]={180, 136}; +#define SEPARACIONY 112 +const u8 ANCHOIMAGEN[]={154, 116}; +#define ALTOIMAGEN 90 + + + +u8 Video_Mode; + +u32 entryPoint; + + +void* dolchunkoffset[64]; //TODO: variable size +u32 dolchunksize[64]; //TODO: variable size +u32 dolchunkcount; + +void _unstub_start(); + + +typedef void (*entrypoint) (void); + +typedef struct _dolheader{ + u32 text_pos[7]; + u32 data_pos[11]; + u32 text_start[7]; + u32 data_start[11]; + u32 text_size[7]; + u32 data_size[11]; + u32 bss_start; + u32 bss_size; + u32 entry_point; +} dolheader; + + +bool __Check_HBC(void){ + u32 *stub = (u32 *)0x80001800; + + // Check HBC stub + if (*stub) + return true; + return false; +} + +s32 check_dol(u64 titleid, char *out, u16 bootcontent){ + s32 cfd; + s32 ret; + u32 num; + dirent_t *list; + char contentpath[ISFS_MAXPATH]; + char path[ISFS_MAXPATH]; + int cnt = 0; + + u8 LZ77_0x10 = 0x10; + u8 LZ77_0x11 = 0x11; + u8 *decompressed; + u8 *compressed; + u32 size_out = 0; + u32 decomp_size = 0; + + + + u8 *buffer = memalign(32, 32); + if (buffer == NULL){ + printf("Out of memory\n"); + return -1; + } + + u8 check[6] = {0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; + + sprintf(contentpath, "/title/%08x/%08x/content", TITLE_UPPER(titleid), TITLE_LOWER(titleid)); + ret = getdir(contentpath, &list, &num); + if (ret < 0){ + printf("Reading folder of the title failed\n"); + free(buffer); + return ret; + } + for(cnt=0; cnt < num; cnt++){ + if ((strstr(list[cnt].name, ".app") != NULL || strstr(list[cnt].name, ".APP") != NULL) && (strtol(list[cnt].name, NULL, 16) != bootcontent)){ + memset(buffer, 0x00, 32); + sprintf(path, "/title/%08x/%08x/content/%s", TITLE_UPPER(titleid), TITLE_LOWER(titleid), list[cnt].name); + + cfd = ISFS_Open(path, ISFS_OPEN_READ); + if (cfd < 0) + { + printf("ISFS_Open for %s failed %d\n", path, cfd); + continue; + } + + ret = ISFS_Read(cfd, buffer, 32); + if (ret < 0) + { + printf("ISFS_Read for %s failed %d\n", path, ret); + ISFS_Close(cfd); + continue; + } + + ISFS_Close(cfd); + + if (buffer[0] == LZ77_0x10 || buffer[0] == LZ77_0x11) + { + if (buffer[0] == LZ77_0x10) + { + printf("Found LZ77 0x10 compressed content --> %s\n", list[cnt].name); + } else + { + printf("Found LZ77 0x11 compressed content --> %s\n", list[cnt].name); + } + printf("This is most likely the main DOL, decompressing for checking\n"); + ret = read_file(path, &compressed, &size_out); + if (ret < 0) + { + printf("Reading file failed\n"); + free(list); + free(buffer); + return ret; + } + printf("read file\n"); + ret = decompressLZ77content(compressed, 32, &decompressed, &decomp_size); + if (ret < 0) + { + printf("Decompressing failed\n"); + free(list); + free(buffer); + return ret; + } + memcpy(buffer, decompressed, 8); + } + + ret = memcmp(buffer, check, 6); + if(ret == 0) + { + printf("Found DOL --> %s\n", list[cnt].name); + sprintf(out, "%s", path); + free(buffer); + free(list); + return 0; + } + } + } + + free(buffer); + free(list); + + printf("No .dol found\n"); + return -1; +} + +void patch_dol(bool bootcontent){ + int i; + bool hookpatched = false; + + for (i=0;i < dolchunkcount;i++) { + if (!bootcontent){ + if (languageoption != -1){ + patch_language(dolchunkoffset[i], dolchunksize[i], languageoption); + } + + if (videopatchoption != 0){ + search_video_modes(dolchunkoffset[i], dolchunksize[i]); + patch_video_modes_to(vmode, videopatchoption); + } + } + + if (hooktypeoption != 0){ + // Before this can be done, the codehandler needs to be in memory, and the code to patch needs to be in the right pace + if (dochannelhooks(dolchunkoffset[i], dolchunksize[i], bootcontent)){ + hookpatched = true; + } + } + } + if (hooktypeoption != 0 && !hookpatched){ + printf("Error: Could not patch the hook\n"); + printf("Ocarina and debugger won't work\n"); + sleep(5); + } +} + + +u32 load_dol(u8 *buffer){ + dolchunkcount = 0; + + dolheader *dolfile; + dolfile = (dolheader *)buffer; + + printf("Entrypoint: %08x\n", dolfile->entry_point); + printf("BSS: %08x, size = %08x(%u)\n", dolfile->bss_start, dolfile->bss_size, dolfile->bss_size); + + memset((void *)dolfile->bss_start, 0, dolfile->bss_size); + DCFlushRange((void *)dolfile->bss_start, dolfile->bss_size); + + printf("BSS cleared\n"); + + u32 doloffset; + u32 memoffset; + u32 restsize; + u32 size; + + int i; + for (i = 0; i < 7; i++){ + if(dolfile->text_pos[i] < sizeof(dolheader)) + continue; + + dolchunkoffset[dolchunkcount] = (void *)dolfile->text_start[i]; + dolchunksize[dolchunkcount] = dolfile->text_size[i]; + dolchunkcount++; + + doloffset = (u32)buffer + dolfile->text_pos[i]; + memoffset = dolfile->text_start[i]; + restsize = dolfile->text_size[i]; + + printf("Moving text section %u from %08x to %08x-%08x...", i, dolfile->text_pos[i], dolfile->text_start[i], dolfile->text_start[i]+dolfile->text_size[i]); + fflush(stdout); + + while (restsize > 0){ + if (restsize > 2048){ + size = 2048; + }else{ + size = restsize; + } + restsize -= size; + ICInvalidateRange ((void *)memoffset, size); + memcpy((void *)memoffset, (void *)doloffset, size); + DCFlushRange((void *)memoffset, size); + + doloffset += size; + memoffset += size; + } + + printf("done\n"); + fflush(stdout); + } + + for(i = 0; i < 11; i++){ + if(dolfile->data_pos[i] < sizeof(dolheader)) + continue; + + dolchunkoffset[dolchunkcount] = (void *)dolfile->data_start[i]; + dolchunksize[dolchunkcount] = dolfile->data_size[i]; + dolchunkcount++; + + doloffset = (u32)buffer + dolfile->data_pos[i]; + memoffset = dolfile->data_start[i]; + restsize = dolfile->data_size[i]; + + printf("Moving data section %u from %08x to %08x-%08x...", i, dolfile->data_pos[i], dolfile->data_start[i], dolfile->data_start[i]+dolfile->data_size[i]); + fflush(stdout); + + while (restsize > 0){ + if (restsize > 2048){ + size = 2048; + }else{ + size = restsize; + } + restsize -= size; + ICInvalidateRange ((void *)memoffset, size); + memcpy((void *)memoffset, (void *)doloffset, size); + DCFlushRange((void *)memoffset, size); + + doloffset += size; + memoffset += size; + } + + printf("done\n"); + fflush(stdout); + } + return dolfile->entry_point; +} + + +s32 search_and_read_dol(u64 titleid, u8 **contentBuf, u32 *contentSize, bool skip_bootcontent){ + char filepath[ISFS_MAXPATH] ATTRIBUTE_ALIGN(0x20); + int ret; + u16 bootindex; + u16 bootcontent; + bool bootcontent_loaded; + + u8 *tmdBuffer = NULL; + u32 tmdSize; + tmd_content *p_cr; + + printf("Reading TMD..."); + + sprintf(filepath, "/title/%08x/%08x/content/title.tmd", TITLE_UPPER(titleid), TITLE_LOWER(titleid)); + ret = read_file(filepath, &tmdBuffer, &tmdSize); + if (ret < 0) + { + printf("Reading TMD failed\n"); + return ret; + } + printf("done\n"); + + bootindex = ((tmd *)SIGNATURE_PAYLOAD((signed_blob *)tmdBuffer))->boot_index; + p_cr = TMD_CONTENTS(((tmd *)SIGNATURE_PAYLOAD((signed_blob *)tmdBuffer))); + bootcontent = p_cr[bootindex].cid; + + free(tmdBuffer); + + // Write bootcontent to filepath and overwrite it in case another .dol is found + sprintf(filepath, "/title/%08x/%08x/content/%08x.app", TITLE_UPPER(titleid), TITLE_LOWER(titleid), bootcontent); + + if (skip_bootcontent) + { + bootcontent_loaded = false; + printf("Searching for main DOL...\n"); + + ret = check_dol(titleid, filepath, bootcontent); + if (ret < 0) + { + printf("Searching for main.dol failed\n"); + printf("Press any button to load NAND loader instead...\n"); + bootcontent_loaded = true; + } + } else + { + bootcontent_loaded = true; + } + + printf("Loading DOL: %s\n", filepath); + + ret = read_file(filepath, contentBuf, contentSize); + if (ret < 0) + { + printf("Reading .dol failed\n"); + return ret; + } + + if (isLZ77compressed(*contentBuf)) + { + u8 *decompressed; + ret = decompressLZ77content(*contentBuf, *contentSize, &decompressed, contentSize); + if (ret < 0) + { + printf("Decompression failed\n"); + free(*contentBuf); + return ret; + } + free(*contentBuf); + *contentBuf = decompressed; + } + + if(bootcontent_loaded){ + return 1; + }else{ + return 0; + } +} + + +void determineVideoMode(u64 titleid){ + if (videooption == 0 || videooption == 1) { + // Get rmode and Video_Mode for system settings first + u32 tvmode = CONF_GetVideo(); + + // Attention: This returns &TVNtsc480Prog for all progressive video modes + vmode = VIDEO_GetPreferredMode(0); + + switch (tvmode) + { + case CONF_VIDEO_PAL: + if (CONF_GetEuRGB60() > 0) + { + Video_Mode = VI_EURGB60; + } + else + { + Video_Mode = VI_PAL; + } + break; + + case CONF_VIDEO_MPAL: + Video_Mode = VI_MPAL; + break; + + case CONF_VIDEO_NTSC: + default: + Video_Mode = VI_NTSC; + + } + + // Overwrite rmode and Video_Mode when Default Video Mode is selected and Wii region doesn't match the channel region + u32 low; + low = TITLE_LOWER(titleid); + char Region = low % 256; + if (*(char *)&low != 'W') // Don't overwrite video mode for WiiWare + { + switch (Region) + { + case 'P': + case 'D': + case 'F': + case 'X': + case 'Y': + if (CONF_GetVideo() != CONF_VIDEO_PAL) + { + Video_Mode = VI_EURGB60; + + if (CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable()) + { + vmode = &TVNtsc480Prog; // This seems to be correct! + } + else + { + vmode = &TVEurgb60Hz480IntDf; + } + } + break; + + case 'E': + case 'J': + case 'T': + if (CONF_GetVideo() != CONF_VIDEO_NTSC) + { + Video_Mode = VI_NTSC; + if (CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable()) + { + vmode = &TVNtsc480Prog; + } + else + { + vmode = &TVNtsc480IntDf; + } + } + } + } + } else { + + if (videooption == 2){ + vmode = &TVPal528IntDf; + } else if (videooption == 3){ + vmode = &TVEurgb60Hz480IntDf; + } else if (videooption == 4){ + vmode = &TVNtsc480IntDf; + } else if (videooption == 5){ + vmode = &TVNtsc480Prog; + } + Video_Mode = (vmode->viTVMode) >> 2; + } +} + +void setVideoMode(){ + *(u32 *)0x800000CC = Video_Mode; + DCFlushRange((void*)0x800000CC, sizeof(u32)); + + // Overwrite all progressive video modes as they are broken in libogc + if (videomode_interlaced(vmode) == 0){ + vmode = &TVNtsc480Prog; + } + + VIDEO_Configure(vmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (vmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); +} + + +#define MAXAPPLOADERGAMES 7 +const char games[MAXAPPLOADERGAMES][4]={ + "WAL", //ArtSyle: light trax + "WDH", //ArtSyle: Rotohex + "WOB", //ArtSyle: ORBIENT + "WPR", //ArtSyle: CUBELLO + "WA8", //ArtSyle: Penta Tentacles + "WB7", //Midnight Pool + "WSP" //Pokemon Rumble +}; + +bool checkApploaderGame(const char* id){ + int i; + + for(i=0; iidInt)); + + + if(memcmp(game->id, "WTR", 3)==0 || memcmp(game->id, "W8C", 3)==0 || memcmp(game->id, "WVB", 3)==0){ //Bit Trip BEAT/CORE/VOID + Fat_MakeDir(dataPath); + ret+=__CreateNullFile(dataPath, "banner.bin", 61600); + ret+=__CreateNullFile(dataPath, "savefile.dat", 512); + }else if(memcmp(game->id, "WRU", 3)==0){ //Bit Trip RUNNER + Fat_MakeDir(dataPath); + ret+=__CreateNullFile(dataPath, "banner.bin", 61600); + ret+=__CreateNullFile(dataPath, "savefile.dat", 1024); + }else if(memcmp(game->id, "WBF", 3)==0){ //Bit Trip FATE + Fat_MakeDir(dataPath); + ret+=__CreateNullFile(dataPath, "banner.bin", 61600); + ret+=__CreateNullFile(dataPath, "savefile.dat", 192); + }else if(memcmp(game->id, "WTF", 3)==0){ //Bit Trip FLUX + Fat_MakeDir(dataPath); + ret+=__CreateNullFile(dataPath, "banner.bin", 61600); + ret+=__CreateNullFile(dataPath, "savefile.dat", 160); + } + + return ret; +} + + + +void __Start_Game(title game, int ios, int mode) { + + nandMode = mode; + + if(nandMode==EMU_USB) + Fat_Mount(USB); + else + Fat_Mount(SD); + + int ret = 0; + + entrypoint appJump; + u32 requested_ios; + u8 *dolbuffer; + u32 dolsize; + bool bootcontentloaded; + + printf("Setting game info...\n"); + printf("GameID: %08x\n", TITLE_LOWER(game.idInt)); + + u64 titleid=game.idInt; + videooption=game.videoMode; + videopatchoption=game.videoPatch; + languageoption=game.language-1; + hooktypeoption=game.hooktype; + ocarinaoption=game.ocarina; + debuggeroption=game.debugger; + bootmethodoption=game.bootMethod; + + printf("Check savegame...\n"); + + if(nandMode!=REAL_NAND){ + ret=checkNeededSave(&game); + } + + //Unmount FAT + Fat_Unmount(); + + //Initialize NAND + if(nandMode!=REAL_NAND){ + ret=Enable_Emu(nandMode); + if(ret<0){ + printf("ERROR: I can't enable NAND emulator (ret=%d).\n", ret); + goto out; + } + } + + ISFS_Initialize(); + + ret = search_and_read_dol(titleid, &dolbuffer, &dolsize, (bootmethodoption == 0)); + if (ret < 0){ + printf(".dol loading failed\n"); + return; + } + bootcontentloaded = (ret == 1); + + determineVideoMode(titleid); + + entryPoint = load_dol(dolbuffer); + + free(dolbuffer); + + printf(".dol loaded\n"); + + ret = identify(titleid, &requested_ios); + if (ret < 0) + { + printf("Identify failed\n"); + return; + } + + ISFS_Deinitialize(); + + // Set the clock + settime(secs_to_ticks(time(NULL) - 946684800)); + + if (entryPoint != 0x3400){ + printf("Setting bus speed\n"); + *(u32*)0x800000F8 = 0x0E7BE2C0; + printf("Setting cpu speed\n"); + *(u32*)0x800000FC = 0x2B73A840; + + DCFlushRange((void*)0x800000F8, 0xFF); + } + + // Remove 002 error + printf("Fake IOS Version(%u)\n", requested_ios); + *(u16 *)0x80003140 = requested_ios; + *(u16 *)0x80003142 = 0xffff; + *(u16 *)0x80003188 = requested_ios; + *(u16 *)0x8000318A = 0xffff; + + DCFlushRange((void*)0x80003140, 4); + DCFlushRange((void*)0x80003188, 4); + + /*ret = ES_SetUID(titleid); + if (ret < 0){ + printf("ES_SetUID failed %d", ret); + return; + }*/ + printf("ES_SetUID successful\n"); + + + if(hooktypeoption != 0){ + do_codes(titleid); + } + + patch_dol(bootcontentloaded); + + printf("Loading complete, booting...\n"); + + appJump = (entrypoint)entryPoint; + + //sleep(5); + + setVideoMode(); + + //---GREENSCREEN FIX--- + VIDEO_Configure(vmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(TRUE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + //--------------------- + + SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + + if (entryPoint != 0x3400) + { + if (hooktypeoption != 0) + { + __asm__( + "lis %r3, entryPoint@h\n" + "ori %r3, %r3, entryPoint@l\n" + "lwz %r3, 0(%r3)\n" + "mtlr %r3\n" + "lis %r3, 0x8000\n" + "ori %r3, %r3, 0x18A8\n" + "mtctr %r3\n" + "bctr\n" + ); + + } else + { + appJump(); + } + } else + { + if (hooktypeoption != 0) + { + __asm__( + "lis %r3, returnpoint@h\n" + "ori %r3, %r3, returnpoint@l\n" + "mtlr %r3\n" + "lis %r3, 0x8000\n" + "ori %r3, %r3, 0x18A8\n" + "mtctr %r3\n" + "bctr\n" + "returnpoint:\n" + "bl DCDisable\n" + "bl ICDisable\n" + "li %r3, 0\n" + "mtsrr1 %r3\n" + "lis %r4, entryPoint@h\n" + "ori %r4,%r4,entryPoint@l\n" + "lwz %r4, 0(%r4)\n" + "mtsrr0 %r4\n" + "rfi\n" + ); + }else{ + _unstub_start(); + } + } + + out: + printf("ret=%d\n", ret); + return; +} + +#define CONFIGITEMS 7 +int __Config_Game(void){ + + //Obtener informacion del juego + //title *config = NULL; + //char* gameId=config->id; + + /*u32 optioncount[CONFIGITEMS]={8,4,11,8,3,2,2}; + u32 optionselected[CONFIGITEMS] = {config->videoMode, config->videoPatch, config->language, config->hooktype, config->ocarina, config->debugger, config->bootMethod}; + char *videooptions[8]={"Default Video Mode", "Force NTSC480i", "Force NTSC480p", "Force PAL480i", "Force PAL480p", "Force PAL576i", "Force MPAL480i", "Force MPAL480p"}; + char *videopatchoptions[4]={"No Video patches", "Smart Video patching", "More Video patching", "Full Video patching" }; + char *languageoptions[11]={"Default Language", "Japanese", "English", "German", "French", "Spanish", "Italian", "Dutch", "S. Chinese", "T. Chinese", "Korean"}; + char *hooktypeoptions[8]={"No Ocarina&debugger", "Hooktype: VBI", "Hooktype: KPAD", "Hooktype: Joypad", "Hooktype: GXDraw", "Hooktype: GXFlush", "Hooktype: OSSleepThread", "Hooktype: AXNextFrame"}; + char *ocarinaoptions[3]={"No Ocarina", "Ocarina from SD", "Ocarina from USB"}; + char *debuggeroptions[2]={"No debugger", "Debugger enabled"}; + char *bootmethodoptions[2]={"Normal boot method", "Load apploader"};*/ + + return MENU_SELECT_GAME; +} + +int __Update_File_From_Real_Nand(char* filepath){ + u8 *inBuffer = NULL; + u32 inSize; + int ret=0; + + //ISFS init + ret=ISFS_Initialize(); + if(ret<0){ + return ret; + } + + ret=read_file(filepath, &inBuffer, &inSize); + ISFS_Deinitialize(); + if(ret<0){ + return ret; + } + + sprintf(tempString, "fat:/%s/%s", Get_Path(), filepath); + ret=Fat_SaveFile(tempString, (void *)&inBuffer, inSize); + + return ret; +} \ No newline at end of file diff --git a/plugins/mighty/source/menu.h b/plugins/mighty/source/menu.h new file mode 100644 index 0000000..a88dd80 --- /dev/null +++ b/plugins/mighty/source/menu.h @@ -0,0 +1,36 @@ +#ifndef _MENU_H_ +#define _MENU_H_ + +//#define DEBUG_MODE 1 +#define MENU_SELECT_GAME 0 +#define MENU_CONFIG_GAME 1 +#define MENU_SORT_GAMES 2 +#define MENU_SHOW_GAME 3 +#define MENU_START_GAME 4 +#define MENU_HOME 5 +#define MENU_MANAGE 6 +#define MENU_DOWNLOAD 7 +#define MENU_EXIT 8 + +#define MIGHTY_VERSION 11 +#define HOTSPOT_LEFT MAXHOTSPOTS-2 +#define HOTSPOT_RIGHT MAXHOTSPOTS-1 + +// Prototypes +typedef struct{ + u64 idInt; + char id[5]; + char* title; + + u8 videoMode; + u8 videoPatch; + u8 language; + u8 hooktype; + u8 ocarina; + u8 debugger; + u8 bootMethod; +} title; //32 bytes! + +void __Start_Game(title game, int ios, int mode); + +#endif diff --git a/plugins/mighty/source/mighty_channels.c b/plugins/mighty/source/mighty_channels.c new file mode 100644 index 0000000..638d4ca --- /dev/null +++ b/plugins/mighty/source/mighty_channels.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include //for parsing parameters +#include + +#include "nand.h" +#include "menu.h" +#include "video.h" +#include "tools.h" + +int main(int argc, char **argv){ + int mode=-1, ios=249; + + title gameToLaunch; + + // Initialize system + Video_Init(); + + Con_Init(CONSOLE_X, CONSOLE_Y, CONSOLE_WIDTH, CONSOLE_HEIGHT); + + printf("Mighty Channels Plugin v4\n"); + + // Parse parameters + if(argc>0){ + int i; + for(i=0; i +#include +#include +#include + +#include "nand.h" + +/* Buffer */ +static u32 inbuf[8] ATTRIBUTE_ALIGN(32); + +static nandDevice ndevList[] = { + { "Disable", 0, 0x00, 0x00 }, + { "SD/SDHC Card", 1, 0xF0, 0xF1 }, + { "USB 2.0 Mass Storage Device", 2, 0xF2, 0xF3 }, +}; + +static int mounted=0; + +static int partition=0; +static int fullmode=0x100; +static char path[32]="\0"; + +s32 Nand_Mount(nandDevice *dev){ + s32 fd, ret; + u32 inlen = 0; + ioctlv *vector = NULL; + u32 *buffer = NULL; + int rev; + + /* Open FAT module */ + fd = IOS_Open("fat", 0); + if (fd < 0) + return fd; + + rev=IOS_GetRevision(); + /* Prepare vector */ + if(rev>=21){ + // NOTE: + // The official cIOSX rev21 by Waninkoko ignores the partition argument + // and the nand is always expected to be on the 1st partition. + // However this way earlier d2x betas having revision 21 take in + // consideration the partition argument. + inlen = 1; + + /* Allocate memory */ + buffer = (u32 *)memalign(32, sizeof(u32)*3); + + /* Set vector pointer */ + vector = (ioctlv *)buffer; + + buffer[0] = (u32)(buffer + 2); + buffer[1] = sizeof(u32); + buffer[2] = (u32)partition; + } + + /* Mount device */ + ret = IOS_Ioctlv(fd, dev->mountCmd, inlen, 0, vector); + + /* Free memory */ + if(buffer != NULL) + free(buffer); + + /* Close FAT module */ + IOS_Close(fd); + + return ret; +} + +s32 Nand_Unmount(nandDevice *dev){ + s32 fd, ret; + + // Open FAT module + fd = IOS_Open("fat", 0); + if (fd < 0) + return fd; + + // Unmount device + ret = IOS_Ioctlv(fd, dev->umountCmd, 0, 0, NULL); + + // Close FAT module + IOS_Close(fd); + + return ret; +} + +s32 Nand_Enable(nandDevice *dev){ + s32 fd, ret; + u32 *buffer = NULL; + int rev; + + // Open /dev/fs + fd = IOS_Open("/dev/fs", 0); + if (fd < 0) + return fd; + + rev=IOS_GetRevision(); + // Set input buffer + if(rev>=21 && rev<30000 && dev->mode!=0){ + //FULL NAND emulation since rev18 + //needed for reading images on triiforce mrc folder using ISFS commands + inbuf[0] = dev->mode | fullmode; + }else{ + inbuf[0] = dev->mode; //old method + } + + // Enable NAND emulator + if(rev>=21 && rev<30000){ + // NOTE: + // The official cIOSX rev21 by Waninkoko provides an undocumented feature + // to set nand path when mounting the device. + // This feature has been discovered during d2x development. + int pathlen = strlen(path)+1; + + /* Allocate memory */ + buffer = (u32 *)memalign(32, (sizeof(u32)*5)+pathlen); + + buffer[0] = (u32)(buffer + 4); + buffer[1] = sizeof(u32); // actually not used by cios + buffer[2] = (u32)(buffer + 5); + buffer[3] = pathlen; // actually not used by cios + buffer[4] = inbuf[0]; + strcpy((char*)(buffer+5), path); + + ret = IOS_Ioctlv(fd, 100, 2, 0, (ioctlv *)buffer); + }else{ + ret = IOS_Ioctl(fd, 100, inbuf, sizeof(inbuf), NULL, 0); + } + + /* Free memory */ + if(buffer != NULL) + free(buffer); + + // Close /dev/fs + IOS_Close(fd); + + return ret; +} + +s32 Nand_Disable(void){ + s32 fd, ret; + + // Open /dev/fs + fd = IOS_Open("/dev/fs", 0); + if (fd < 0) + return fd; + + // Set input buffer + inbuf[0] = 0; + + // Disable NAND emulator + ret = IOS_Ioctl(fd, 100, inbuf, sizeof(inbuf), NULL, 0); + + // Close /dev/fs + IOS_Close(fd); + + return ret; +} + + +s32 Enable_Emu(int selection){ + if(mounted != 0) + return -1; + s32 ret; + nandDevice *ndev = NULL; + ndev = &ndevList[selection]; + + ret = Nand_Mount(ndev); + if (ret < 0) + { + printf(" ERROR Mount! (ret = %d)\n", ret); + return ret; + + } + + ret = Nand_Enable(ndev); + if (ret < 0) + { + printf(" ERROR Enable! (ret = %d)\n", ret); + return ret; + } + mounted = selection; + return 0; +} + +s32 Disable_Emu(){ + if(mounted==0) + return 0; + + nandDevice *ndev = NULL; + ndev = &ndevList[mounted]; + + Nand_Disable(); + Nand_Unmount(ndev); + + mounted = 0; + + return 0; +} + +void Set_Partition(int p){ + partition=p; +} + +void Set_Path(const char* p){ + int i=0; + + while(p[i]!='\0' && i<31){ + path[i]=p[i]; + i++; + } + if(path[i-1]=='/') + path[i-1]='\0'; + path[i]='\0'; +} + +void Set_FullMode(int fm){ + fullmode=fm? 0x100: 0; +} + +const char* Get_Path(void){ + return path; +} \ No newline at end of file diff --git a/plugins/mighty/source/nand.h b/plugins/mighty/source/nand.h new file mode 100644 index 0000000..5e396f0 --- /dev/null +++ b/plugins/mighty/source/nand.h @@ -0,0 +1,35 @@ +#ifndef _NAND_H_ +#define _NAND_H_ + +/* 'NAND Device' structure */ +typedef struct { + /* Device name */ + char *name; + + /* Mode value */ + u32 mode; + + /* Un/mount command */ + u32 mountCmd; + u32 umountCmd; +} nandDevice; + + +#define REAL_NAND 0 +#define EMU_SD 1 +#define EMU_USB 2 + +/* Prototypes */ +s32 Nand_Mount(nandDevice *); +s32 Nand_Unmount(nandDevice *); +s32 Nand_Enable(nandDevice *); +s32 Nand_Disable(void); +s32 Enable_Emu(int selection); +s32 Disable_Emu(); + +void Set_Partition(int); +void Set_Path(const char*); +void Set_FullMode(int); +const char* Get_Path(void); + +#endif diff --git a/plugins/mighty/source/patch.c b/plugins/mighty/source/patch.c new file mode 100644 index 0000000..4d72a75 --- /dev/null +++ b/plugins/mighty/source/patch.c @@ -0,0 +1,421 @@ +/******************************************************************************* + * patch.c + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#include +#include "patch.h" + +GXRModeObj TVPal528Prog = +{ + 6, // viDisplayMode + 640, // fbWidth + 528, // efbHeight + 528, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } + +}; + +GXRModeObj TVPal528ProgSoft = +{ + 6, // viDisplayMode + 640, // fbWidth + 528, // efbHeight + 528, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } + +}; + +GXRModeObj TVPal528ProgUnknown = +{ + 6, // viDisplayMode + 640, // fbWidth + 264, // efbHeight + 524, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 524, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } + +}; + +/*GXRModeObj TVMpal480Prog = +{ + 10, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +};*/ + + +bool compare_videomodes(GXRModeObj* mode1, GXRModeObj* mode2) +{ + if (mode1->viTVMode != mode2->viTVMode || mode1->fbWidth != mode2->fbWidth || mode1->efbHeight != mode2->efbHeight || mode1->xfbHeight != mode2->xfbHeight || + mode1->viXOrigin != mode2->viXOrigin || mode1->viYOrigin != mode2->viYOrigin || mode1->viWidth != mode2->viWidth || mode1->viHeight != mode2->viHeight || + mode1->xfbMode != mode2->xfbMode || mode1->field_rendering != mode2->field_rendering || mode1->aa != mode2->aa || mode1->sample_pattern[0][0] != mode2->sample_pattern[0][0] || + mode1->sample_pattern[1][0] != mode2->sample_pattern[1][0] || mode1->sample_pattern[2][0] != mode2->sample_pattern[2][0] || + mode1->sample_pattern[3][0] != mode2->sample_pattern[3][0] || mode1->sample_pattern[4][0] != mode2->sample_pattern[4][0] || + mode1->sample_pattern[5][0] != mode2->sample_pattern[5][0] || mode1->sample_pattern[6][0] != mode2->sample_pattern[6][0] || + mode1->sample_pattern[7][0] != mode2->sample_pattern[7][0] || mode1->sample_pattern[8][0] != mode2->sample_pattern[8][0] || + mode1->sample_pattern[9][0] != mode2->sample_pattern[9][0] || mode1->sample_pattern[10][0] != mode2->sample_pattern[10][0] || + mode1->sample_pattern[11][0] != mode2->sample_pattern[11][0] || mode1->sample_pattern[0][1] != mode2->sample_pattern[0][1] || + mode1->sample_pattern[1][1] != mode2->sample_pattern[1][1] || mode1->sample_pattern[2][1] != mode2->sample_pattern[2][1] || + mode1->sample_pattern[3][1] != mode2->sample_pattern[3][1] || mode1->sample_pattern[4][1] != mode2->sample_pattern[4][1] || + mode1->sample_pattern[5][1] != mode2->sample_pattern[5][1] || mode1->sample_pattern[6][1] != mode2->sample_pattern[6][1] || + mode1->sample_pattern[7][1] != mode2->sample_pattern[7][1] || mode1->sample_pattern[8][1] != mode2->sample_pattern[8][1] || + mode1->sample_pattern[9][1] != mode2->sample_pattern[9][1] || mode1->sample_pattern[10][1] != mode2->sample_pattern[10][1] || + mode1->sample_pattern[11][1] != mode2->sample_pattern[11][1] || mode1->vfilter[0] != mode2->vfilter[0] || + mode1->vfilter[1] != mode2->vfilter[1] || mode1->vfilter[2] != mode2->vfilter[2] || mode1->vfilter[3] != mode2->vfilter[3] || mode1->vfilter[4] != mode2->vfilter[4] || + mode1->vfilter[5] != mode2->vfilter[5] || mode1->vfilter[6] != mode2->vfilter[6] ) + { + return false; + } else + { + return true; + } +} + +void patch_videomode(GXRModeObj* mode1, GXRModeObj* mode2) +{ + mode1->viTVMode = mode2->viTVMode; + mode1->fbWidth = mode2->fbWidth; + mode1->efbHeight = mode2->efbHeight; + mode1->xfbHeight = mode2->xfbHeight; + mode1->viXOrigin = mode2->viXOrigin; + mode1->viYOrigin = mode2->viYOrigin; + mode1->viWidth = mode2->viWidth; + mode1->viHeight = mode2->viHeight; + mode1->xfbMode = mode2->xfbMode; + mode1->field_rendering = mode2->field_rendering; + mode1->aa = mode2->aa; + mode1->sample_pattern[0][0] = mode2->sample_pattern[0][0]; + mode1->sample_pattern[1][0] = mode2->sample_pattern[1][0]; + mode1->sample_pattern[2][0] = mode2->sample_pattern[2][0]; + mode1->sample_pattern[3][0] = mode2->sample_pattern[3][0]; + mode1->sample_pattern[4][0] = mode2->sample_pattern[4][0]; + mode1->sample_pattern[5][0] = mode2->sample_pattern[5][0]; + mode1->sample_pattern[6][0] = mode2->sample_pattern[6][0]; + mode1->sample_pattern[7][0] = mode2->sample_pattern[7][0]; + mode1->sample_pattern[8][0] = mode2->sample_pattern[8][0]; + mode1->sample_pattern[9][0] = mode2->sample_pattern[9][0]; + mode1->sample_pattern[10][0] = mode2->sample_pattern[10][0]; + mode1->sample_pattern[11][0] = mode2->sample_pattern[11][0]; + mode1->sample_pattern[0][1] = mode2->sample_pattern[0][1]; + mode1->sample_pattern[1][1] = mode2->sample_pattern[1][1]; + mode1->sample_pattern[2][1] = mode2->sample_pattern[2][1]; + mode1->sample_pattern[3][1] = mode2->sample_pattern[3][1]; + mode1->sample_pattern[4][1] = mode2->sample_pattern[4][1]; + mode1->sample_pattern[5][1] = mode2->sample_pattern[5][1]; + mode1->sample_pattern[6][1] = mode2->sample_pattern[6][1]; + mode1->sample_pattern[7][1] = mode2->sample_pattern[7][1]; + mode1->sample_pattern[8][1] = mode2->sample_pattern[8][1]; + mode1->sample_pattern[9][1] = mode2->sample_pattern[9][1]; + mode1->sample_pattern[10][1] = mode2->sample_pattern[10][1]; + mode1->sample_pattern[11][1] = mode2->sample_pattern[11][1]; + mode1->vfilter[0] = mode2->vfilter[0]; + mode1->vfilter[1] = mode2->vfilter[1]; + mode1->vfilter[2] = mode2->vfilter[2]; + mode1->vfilter[3] = mode2->vfilter[3]; + mode1->vfilter[4] = mode2->vfilter[4]; + mode1->vfilter[5] = mode2->vfilter[5]; + mode1->vfilter[6] = mode2->vfilter[6]; +} + +int videomode_region(GXRModeObj* mode) +{ + if ( compare_videomodes(&TVNtsc480Int, mode) + || compare_videomodes(&TVNtsc480IntDf, mode) + || compare_videomodes(&TVNtsc480Prog, mode) ) + { + return 0; + } + if ( compare_videomodes(&TVPal528Int, mode) + || compare_videomodes(&TVPal528IntDf, mode) + || compare_videomodes(&TVPal528Prog, mode) + || compare_videomodes(&TVPal528ProgSoft, mode) + || compare_videomodes(&TVPal528ProgUnknown, mode) ) + { + return 1; + } + if ( compare_videomodes(&TVMpal480IntDf, mode) + || compare_videomodes(&TVNtsc480Prog, mode) ) + { + return 4; + } + if ( compare_videomodes(&TVEurgb60Hz480Int, mode) + || compare_videomodes(&TVEurgb60Hz480IntDf, mode) + || compare_videomodes(&TVEurgb60Hz480Prog, mode) ) + { + return 5; + } + return -1; +} + +int videomode_interlaced(GXRModeObj* mode) +{ + if ( compare_videomodes(&TVNtsc480Int, mode) + || compare_videomodes(&TVNtsc480IntDf, mode) + || compare_videomodes(&TVPal528Int, mode) + || compare_videomodes(&TVPal528IntDf, mode) + || compare_videomodes(&TVMpal480IntDf, mode) + || compare_videomodes(&TVEurgb60Hz480Int, mode) + || compare_videomodes(&TVEurgb60Hz480IntDf, mode) ) + { + return 1; + } + if ( compare_videomodes(&TVNtsc480Prog, mode) + || compare_videomodes(&TVPal528Prog, mode) + || compare_videomodes(&TVPal528ProgSoft, mode) + || compare_videomodes(&TVPal528ProgUnknown, mode) + || compare_videomodes(&TVNtsc480Prog, mode) + || compare_videomodes(&TVEurgb60Hz480Prog, mode) ) + { + return 0; + } + return -1; +} + +int videomode_480(GXRModeObj* mode) +{ + switch (videomode_region(mode)) + { + case 0: + case 4: + case 5: + return 1; + + case 1: + return 0; + + default: + return -1; + } +} + +static GXRModeObj* videomodes[50]; +static u32 videomodecount; + +void search_video_modes(u8 *file, u32 size) +{ + videomodecount = 0; + u8 *Addr = file; + + while ((u32)Addr < (u32)file+size-sizeof(GXRModeObj)) + { + if (videomode_region((GXRModeObj*)Addr) != -1) + { + videomodes[videomodecount] = (GXRModeObj*)Addr; + videomodecount++; + Addr += sizeof(GXRModeObj); + } else + { + Addr += 4; + } + } +} + + +void patch_video_modes_to(GXRModeObj* vmode, u32 videopatchselect) +{ + bool patch; + if (videopatchselect > 0) + { + unsigned int i; + for (i=0;i < videomodecount;i++) + { + patch = false; + if (videopatchselect == 3) + { + patch = true; + } else + { + if (videomode_interlaced(videomodes[i]) == videomode_interlaced(vmode)) + { + if (videopatchselect == 2) + { + patch = true; + } else + { + if (videomode_480(videomodes[i]) == videomode_480(vmode)) + { + patch = true; + } + } + } + } + if (patch == true) + { + patch_videomode(videomodes[i], vmode); + } + } + } +} + + +s32 search_offset(u8 *file, u32 size, u8 *value, u32 valuesize, u32 offset, u32 *outoffset) +{ + int i = 0; + for (i = offset; i < size; i+=4) + { + if (memcmp(file + i, value, valuesize) == 0) + { + *outoffset = i; + return 0; + } + } + return -1; +} + +s32 parser(u8 *file, u32 size, u8 *value, u32 valuesize, u8 *patch, u32 patchsize, u32 shift) +{ + u32 cnt = 0; + u32 maxsize = size - valuesize; + if (patchsize + shift > valuesize) + { + maxsize = size - patchsize - shift; + } + + u32 offset = 0; + while (search_offset(file, maxsize-offset, value, valuesize, offset, &offset) >= 0) + { + memcpy(file + offset + shift, patch, patchsize); + offset = offset + shift + patchsize; + cnt++; + } + + if (cnt > 0) + { + return cnt; + } else + { + return -1; + } +} + +s32 patch_language(u8 *file, u32 size, u32 languageoption) +{ + u8 languagesearchpattern1[12] = { 0x7C, 0x60, 0x07, 0x75, 0x40, 0x82, 0x00, 0x10, 0x38, 0x00, 0x00, 0x00 }; + u8 languagesearchpattern2[4] = { 0x88, 0x61, 0x00, 0x08 }; + u8 languagepatch[4] = { 0x38, 0x60, 0x00, languageoption }; + + u32 offset; + if (search_offset(file, size - 16, languagesearchpattern1, 12, 0, &offset) >= 0) + { + if (search_offset(file, size -4 -offset, languagesearchpattern2, 4, offset, &offset) >= 0) + { + memcpy(file + offset, languagepatch, 4); + return 0; + } + } + return -1; +} + + diff --git a/plugins/mighty/source/patch.h b/plugins/mighty/source/patch.h new file mode 100644 index 0000000..40e32ba --- /dev/null +++ b/plugins/mighty/source/patch.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * tools.h + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#include + +extern GXRModeObj TVNtsc480Int; // declared somewhere inside libogc, but missing in the correct .h file +extern GXRModeObj TVMpal480Prog; + +int videomode_interlaced(GXRModeObj* mode); +void search_video_modes(u8 *file, u32 size); +void patch_video_modes_to(GXRModeObj* vmode, u32 videopatchselect); +s32 parser(u8 *file, u32 size, u8 *value, u32 valuesize, u8 *patch, u32 patchsize, u32 offset); +s32 patch_language(u8 *file, u32 size, u32 languageoption); + diff --git a/plugins/mighty/source/sonido.c b/plugins/mighty/source/sonido.c new file mode 100644 index 0000000..581a627 --- /dev/null +++ b/plugins/mighty/source/sonido.c @@ -0,0 +1,671 @@ + +// Banner sounds by oggzee +// Fixes by Clipper + +#include +#include +#include +#include +#include + +#include "sonido.h" + + +/* +void hex_dump2(void *p, int size) +{ + int i = 0, j, x = 12; + char *c = p; + //printf("\n"); + while (i 128) cc = '.'; + //printf("%c", cc); + } + //printf("|\n"); + i += x; + } +} + +void dbg_hex_dump(void *p, int size) +{ + hex_dump2(p, size); +}*/ + +typedef struct { + //u8 zeroes[128]; // padding + u8 zeroes[0x40]; // padding + u8 imet[4]; // "IMET" + u8 unk[8]; // 0x0000060000000003 fixed, unknown purpose + u32 sizes[3]; // icon.bin, banner.bin, sound.bin + u32 flag1; // unknown + u8 names[7][84]; // JP, EN, DE, FR, ES, IT, NL + u8 zeroes_2[0x348]; // padding + u8 crypto[16]; // MD5 of 0x40 to 0x640 in header. +} IMET; + +struct U8_archive_header +{ + u8 tag[4]; // 0x55AA382D "U.8-" + u32 rootnode_offset; // offset to root_node, always 0x20. + u32 header_size; // size of header from root_node to end of string table. + u32 data_offset; // offset to data -- this is rootnode_offset + header_size, aligned to 0x40. + u8 zeroes[16]; +}; + +struct U8_node +{ + u8 type; + u8 name_offset[3]; //really a "u24" + u32 data_offset; + u32 size; +}; + +typedef struct { + u32 imd5; // 'IMD5' + u32 filesize; //size of rest of file + u8 zeroes[8]; //padding + u8 crypto[16]; //MD5 of rest of file +} IMD5; + +// Header +typedef struct { + char bns[4]; // 'BNS ' + u32 version; // 0xFEFF0100 endianness and format version check + u32 filesize; // size of entire BNS + u16 headersize; // size of BNS header (including chunkinfo) + u16 chunkcount; // number of chunks + struct { + u32 offset; // offset from start of BNS of chunk header + u32 size; // size of chunk including header + } chunkinfo[0]; //[chunkcount]; // info for each chunk +} BNS; + +// Chunk header +typedef struct { + char type[4]; // 'INFO' or 'DATA' + u32 size; // size including header +} BNS_chunk; + +// INFO chunk +typedef struct { + u8 unk; // 0, possibly format control + u8 loop; // loop flag, 0 = no loop, 1 = loop + u8 channels; // channel count + u8 unk2; // 0, padding? + u16 samplerate; // sample rate (Hz) + u16 pad; // 0 + u32 loopstart; // loop start sample + u32 samples; // total sample count + u32 offset; // offset (in INFO) to channel info offset list + u32 unk4; // 0, padding? +} INFO_chunk; + +// channel info offset list +typedef struct { +// u32 offset[channels]; // offset (in INFO) to channel info for each channel + u32 offset[0]; // offset (in INFO) to channel info for each channel +} channel_info_offset_list; + +// channel info (for DSP ADPCM) +typedef struct { + u32 data_offset; // offset (in DATA) of audio data for channel + u32 info_offset; // offset (in INFO) of DSP info (0x30 byte block) + u32 unk; // 0 +} channel_info; + + +void adpcm_decode(void *dsp_buf, void *adpcm_info, void *adpcm_data, + int samples, int channels, int cur_chan) +{ + // read: + short *CoEfficients = adpcm_info; //[16]; + u8 *cdata = adpcm_data; + // write: + //int dsp_size = size / 8 * 14; + int rsize = samples * 8 / 14; + short *dsp = dsp_buf; + // state + int rpos = 0, wpos = cur_chan; + short adpcmhistory1 = 0; + short adpcmhistory2 = 0; + + while (rpos < rsize) { + int sample; + short nibbles[14]; + int index1; + int i,j; + u8 header; + short delta; + u8 thisbyte; + short hist = adpcmhistory1; + short hist2 = adpcmhistory2; + // If we have loop data then we write to our looping file. + //if (mscdata.Position == looppos) + // bwpdata = new BinaryWriter(msploop); + + //header = brcdata.ReadByte(); + header = cdata[rpos++]; + i = header & 0xFF; + delta = (short) (1 << (i & 0xff & 0xf)); + index1 = (i & 0xff) >> 4; + + for(i = 0; i < 14; i += 2) { + //thisbyte = brcdata.ReadByte(); + thisbyte = cdata[rpos++]; + j = (thisbyte & 0xff) >> 4; + nibbles[i] = (short) j; + j = thisbyte & 0xff & 0xf; + nibbles[i+1] = (short) j; + } + + for(i = 0; i < 14; i++) { + if(nibbles[i] >= 8) + nibbles[i] = (short)(nibbles[i] - 16); + } + + for(i = 0; i<14 ; i++) { + sample = (delta * nibbles[i])<<11; + sample += CoEfficients[index1 * 2] * hist; + sample += CoEfficients[index1 * 2 + 1] * hist2; + sample = sample + 1024; + sample = sample >> 11; + if(sample > 32767) + sample = 32767; + + if(sample < -32768) + sample = -32768; + //bwpdata.Write((short)sample); + dsp[wpos] = (short)sample; + wpos += channels; + if (wpos >= samples * channels) { + rpos = rsize; + break; + } + hist2 = hist; + hist = (short)sample; + } + adpcmhistory1 = hist; + adpcmhistory2 = hist2; + + } +} + +u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size) +{ + u8 *data_end; + u8 *decompressed_data; + size_t unpacked_size; + u8 *in_ptr; + u8 *out_ptr; + u8 *out_end; + + in_ptr = data; + data_end = data + data_size; + // Assume this for now and grow when needed + unpacked_size = data_size; + decompressed_data = malloc(unpacked_size); + out_end = decompressed_data + unpacked_size; + out_ptr = decompressed_data; + + while (in_ptr < data_end) { + int bit; + u8 bitmask = *in_ptr; + + in_ptr++; + for (bit = 0x80; bit != 0; bit >>= 1) { + if (bitmask & bit) { + // Next section is compressed + u8 rep_length; + u16 rep_offset; + + rep_length = (*in_ptr >> 4) + 3; + rep_offset = *in_ptr & 0x0f; + in_ptr++; + rep_offset = *in_ptr | (rep_offset << 8); + in_ptr++; + if (out_ptr-decompressed_data < rep_offset) { + //printf("Inconsistency in LZ77 encoding %x\n", in_ptr-data); + } + + for ( ; rep_length > 0; rep_length--) { + *out_ptr = out_ptr[-rep_offset-1]; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + } + } else { + // Just copy byte + *out_ptr = *in_ptr; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + in_ptr++; + } + } + } + + *decompressed_size = (out_ptr - decompressed_data); + return decompressed_data; +} + +void parse_bns(void *data_bns, SoundInfo *snd) +{ + BNS *bns = data_bns; + BNS_chunk *b_chunk = NULL; + INFO_chunk *i_chunk = NULL; + void *d_chunk = NULL; + channel_info_offset_list *ci_off; + channel_info *ci; + void *dsp_data = NULL; + int dsp_size = 0; + int i; + + //printf("BNS: %.4s 0x%x %x %d\n", bns->bns, + // bns->version, bns->filesize, bns->chunkcount); + if (memcmp(bns, "BNS ", 4) != 0) return; + + for (i=0; ichunkcount; i++) { + b_chunk = (void*)bns + bns->chunkinfo[i].offset; + //printf("%d %4x %4x %.4s %x\n", i, + // bns->chunkinfo[i].offset, + // bns->chunkinfo[i].size, + // b_chunk->type, + // b_chunk->size); + if (memcmp(b_chunk->type, "INFO", 4)==0) { + if (!i_chunk) i_chunk = (void*)(b_chunk+1); + //printf(" l:%d c:%d s:%d %x %x %x\n", + // i_chunk->loop, + // i_chunk->channels, + // i_chunk->samplerate, + // i_chunk->loopstart, + // i_chunk->samples, + // i_chunk->offset); + } + if (memcmp(&b_chunk->type, "DATA", 4)==0) { + if (!d_chunk) d_chunk = (void*)(b_chunk+1); + } + } + + if (!i_chunk || !d_chunk) return; + if (i_chunk->channels < 1 || i_chunk->channels > 2) return; + + dsp_size = i_chunk->samples * i_chunk->channels * sizeof(short); + dsp_data = memalign(32, dsp_size + 14*2); + + + ci_off = (void*)i_chunk + i_chunk->offset; + for (i=0; ichannels; i++) { + int off = ci_off->offset[i]; + ci = (void*)i_chunk + off; + //printf(" [%d] %x data: %x info: %x\n", i, off, + // ci->data_offset, ci->info_offset); + void *adpcm_data = (void*)d_chunk + ci->data_offset; + void *adpcm_info = (void*)i_chunk + ci->info_offset; + adpcm_decode(dsp_data, adpcm_info, adpcm_data, + i_chunk->samples, i_chunk->channels, i); + } + snd->dsp_data = dsp_data; + snd->size = dsp_size; + snd->channels = i_chunk->channels; + snd->rate = i_chunk->samplerate; + snd->loop = i_chunk->loop; + + //printf("LOOPSTART: %d ",i_chunk->loopstart); +} + + +/* +The canonical WAVE format starts with the RIFF header: + +Offset Size Name Description + +0 4 ChunkID Contains the letters "RIFF" in ASCII form + (0x52494646 big-endian form). +4 4 ChunkSize 36 + SubChunk2Size, or more precisely: + 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) + This is the size of the rest of the chunk + following this number. This is the size of the + entire file in bytes minus 8 bytes for the + two fields not included in this count: + ChunkID and ChunkSize. +8 4 Format Contains the letters "WAVE" + (0x57415645 big-endian form). + +The "WAVE" format consists of two subchunks: "fmt " and "data": +The "fmt " subchunk describes the sound data's format: + +12 4 Subchunk1ID Contains the letters "fmt " + (0x666d7420 big-endian form). +16 4 Subchunk1Size 16 for PCM. This is the size of the + rest of the Subchunk which follows this number. +20 2 AudioFormat PCM = 1 (i.e. Linear quantization) + Values other than 1 indicate some + form of compression. +22 2 NumChannels Mono = 1, Stereo = 2, etc. +24 4 SampleRate 8000, 44100, etc. +28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8 +32 2 BlockAlign == NumChannels * BitsPerSample/8 + The number of bytes for one sample including + all channels. I wonder what happens when + this number isn't an integer? +34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc. + 2 ExtraParamSize if PCM, then doesn't exist + X ExtraParams space for extra parameters + +The "data" subchunk contains the size of the data and the actual sound: + +36 4 Subchunk2ID Contains the letters "data" + (0x64617461 big-endian form). +40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 + This is the number of bytes in the data. + You can also think of this as the size + of the read of the subchunk following this + number. +44 * Data The actual sound data. +*/ + +typedef struct RIFF +{ + char ChunkID[4]; // Contains the letters "RIFF" in ASCII form + int ChunkSize; // 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) + char Format[4]; // Contains the letters "WAVE" + //The "WAVE" format consists of two subchunks: "fmt " and "data": + + //The "fmt " subchunk describes the sound data's format: + char Subchunk1ID[4];// Contains the letters "fmt " + int Subchunk1Size; // 16 for PCM. This is the size of the rest of the Subchunk + short AudioFormat; // PCM = 1 (i.e. Linear quantization) + short NumChannels; // Mono = 1, Stereo = 2, etc. + int SampleRate; // 8000, 44100, etc. + int ByteRate; // == SampleRate * NumChannels * BitsPerSample/8 + short BlockAlign; // == NumChannels * BitsPerSample/8 + short BitsPerSample;// 8 bits = 8, 16 bits = 16, etc. + // 2 ExtraParamSize // if PCM, then doesn't exist + // X ExtraParams // space for extra parameters +} RIFF; + +typedef struct RIFF_chnk +{ + char id[4]; + int size; +} RIFF_chnk; + +void parse_riff(void *data, SoundInfo *snd) +{ + RIFF *riff = data; + int riff_size = _le32(&riff->ChunkSize); + int sub1_size = _le32(&riff->Subchunk1Size); + //dbg_hex_dump(data, 128); + //printf("'%.4s %x %.4s %.4s %x\n", + // riff->ChunkID, _le32(&riff->ChunkSize), riff->Format, + // riff->Subchunk1ID, sub1_size); + if (memcmp(riff->ChunkID, "RIFF", 4)) return; + if (memcmp(riff->Format, "WAVE", 4)) return; + if (memcmp(riff->Subchunk1ID, "fmt ", 4)) return; + + short AudioFormat = _le16(&riff->AudioFormat); + short NumChannels = _le16(&riff->NumChannels); + short BitsPerSample = _le16(&riff->BitsPerSample); + int SampleRate = _le32(&riff->SampleRate); + if (AudioFormat != 1 || BitsPerSample != 16) return; + if (NumChannels < 1 || NumChannels > 2) return; + + //printf("f:%d c:%d r:%d b:%d\n", AudioFormat, NumChannels, + // SampleRate, BitsPerSample); + RIFF_chnk *chnk = ((void*)riff) + 12 + 8 + sub1_size; + RIFF_chnk *riff_data = NULL; + while (((void*)chnk > data) && ((void*)chnk < data + riff_size)) { + int size = _le32(&chnk->size); + //printf("[%.4s] %x\n", chnk->id, size); + //dbg_pause(); + if (memcmp(chnk->id, "data", 4)==0) { + riff_data = chnk; + break; + } + if (size <= 0) break; + chnk = ((void*)chnk) + sizeof(RIFF_chnk) + size; + } + if (riff_data == NULL) return; + + int Size = _le32(&riff_data->size); + short *ss = (void*)(riff_data + 1); + short *dsp_data; + int i; + dsp_data = memalign(32, Size); + if (!dsp_data) return; + // byte order + for (i=0; idsp_data = dsp_data; + snd->channels = NumChannels; + snd->rate = SampleRate; + snd->size = Size; + snd->loop = 0; +} + +/* +Byte order: Big-endian + +Offset Length Contents + 0 4 bytes "FORM" + 4 4 bytes + 8 4 bytes "AIFF" +[ // Chunks + 4 bytes + 4 bytes + (x)bytes +]* + +COMM chunk: Must be defined + 0 4 bytes "COMM" + 4 4 bytes // (=18) + 8 2 bytes + 10 4 bytes + 14 2 bytes // 1..32 + 16 10 bytes + +SSND chunk: Must be defined + 0 4 bytes "SSND" + 4 4 bytes + 8 4 bytes + 12 4 bytes // (=0) + 16 (n)bytes Comment + 16+(n) (s)bytes // (s) := (x) - (n) - 8 +*/ + +typedef struct CHNK +{ + char id[4]; + int size; +} CHNK; + +typedef struct AIFF +{ + char id[4]; // FORM + int size; + char aiff[4]; // AIFF +} AIFF; + +/* +typedef struct COMM +{ + char id[4]; // 4 bytes "COMM" + int size; // 4 bytes // (=18) + u16 channels; // 2 bytes + int frames; // 4 bytes + u16 bits; // 2 bytes // 1..32 + char rate[10]; //10 bytes +} COMM; +*/ +typedef struct COMM +{ + char id[4]; // 4 bytes "COMM" + int size; // 4 bytes // (=18) + u16 channels; // 2 bytes + u8 frames[4]; // 4 bytes + u16 bits; // 2 bytes // 1..32 + u8 rate[10]; + //u16 pad1; + //u16 rate; + //u8 pad[6]; //10 bytes +} COMM; + +typedef struct SSND +{ + char id[4]; // 4 bytes "SSND" + int size; // 4 bytes + int offset; // 4 bytes + int bsize; // 4 bytes // (=0) + //(n)bytes Comment + //(s)bytes // (s) := (x) - (n) - 8 +} SSND; + +double ext_to_double(unsigned char a_extended[10]) +{ + int exponent,i; + char found; + union { + double dval; + unsigned char cval[8]; + } value; + unsigned char extended[10]; + memcpy(extended, a_extended, 10); + //for (i=0;i<10;i++) printf("%02x ", extended[i]); puts(""); + /* I'll assume little endian for now. Easy to fix to big endian anyway */ + /* also assuming positive for simplicity */ + found=0; + for (i = 2; i < 10; i++) found |= extended[i]; + if (!found) return 0; + exponent = (((extended[0]&0x7F) << 8) | extended[1]) - 15359; + found=0; + while(!found) { + found = (extended[2] & 0x80); + for (i = 2; i < 9; i++) extended[i] = (extended[i] << 1) | (extended[i+1] >> 7); + extended[9] = extended[9] << 1; + exponent--; + } + /* Little Endian here: + value.cval[7] = extended[0]&0x80 | (exponent >> 4); + value.cval[6] = (exponent << 4) | (extended[2] >> 4); + for (i=0;i<6; i++) { + value.cval[5-i] = (extended[i+2] << 4) | (extended[i+3] >> 4); + } + */ + value.cval[0] = (extended[0]&0x80) | (exponent >> 4); + value.cval[1] = (exponent << 4) | (extended[2] >> 4); + for (i=0;i<6; i++) { + value.cval[i+2] = (extended[i+2] << 4) | (extended[i+3] >> 4); + } + return value.dval; +} + +void parse_aiff(void *data, SoundInfo *snd) +{ + AIFF *aiff = data; + CHNK *chnk = (CHNK*)(aiff+1); + COMM *comm = NULL; + SSND *ssnd = NULL; + int rate = 0; + //printf("%.4s %x %.4s\n", aiff->id, aiff->size, aiff->aiff); + while ((void*)chnk < data + aiff->size) { + //printf("[%.4s] %d %x\n", chnk->id, chnk->size, chnk->size); + if (memcmp(chnk->id, "COMM", 4)==0 && !comm) { + comm = (COMM*)chnk; + rate = (int)ext_to_double(comm->rate); + //printf("c:%d f:%x b:%d r:%d\n", comm->channels, + // _be32(comm->frames), comm->bits, rate); + } + if (memcmp(chnk->id, "SSND", 4)==0 && !ssnd) { + ssnd = (SSND*)chnk; + //printf("%d %d\n", ssnd->offset, ssnd->bsize); + } + // next chunk: + chnk = (void*)(chnk+1) + chnk->size; + } + if (!comm || !ssnd) return; + int dsp_size = ssnd->size - ssnd->offset - 8; + void *dsp_data = memalign(32, dsp_size); + if (!dsp_data) return; + memcpy(dsp_data, (void*)(ssnd+1) + ssnd->offset, dsp_size); + snd->dsp_data = dsp_data; + snd->channels = comm->channels; + snd->rate = rate; + snd->size = dsp_size; + snd->loop = 0; +} + +void parse_banner_snd(void *banner, SoundInfo *snd){ + //void *data_hdr = banner + sizeof(IMET); + void *data_hdr = banner + 0x640; + struct U8_archive_header *u8_hdr; + struct U8_node *node; + int i, num, off; + char *name_start, *name; + u8 u8_tag[4] = {0x55, 0xAA, 0x38, 0x2D}; // "U.8-" + + //dbg_hex_dump(banner, 128); + u8_hdr = data_hdr; + //printf("imet: %.4s\n", ((IMET*)banner)->imet); + //printf("U8: %.4s 0x%x\n", u8_hdr->tag, u8_hdr->rootnode_offset); + //dbg_pause(); + if (memcmp(u8_hdr->tag, u8_tag, 4)) return; + + node = data_hdr + u8_hdr->rootnode_offset; + num = node->size; + name_start = (char*)&node[num]; + if (num>5) num = 5; + for (i=1; ifilesize; + void *lz_data = NULL; + retry: + //printf("[%.4s]\n", (char*)sound_data); + //dbg_pause(); + if (memcmp(sound_data, "BNS ", 4)==0) { + parse_bns(sound_data, snd); + } else if (memcmp(sound_data, "RIFF", 4)==0) { + parse_riff(sound_data, snd); + } else if (memcmp(sound_data, "FORM", 4)==0) { + parse_aiff(sound_data, snd); + } else if (memcmp(sound_data, "LZ77", 4)==0 && lz_data == NULL) { + //unsigned lz_hdr = ((unsigned*)sound_data)[1]; + lz_data = decompress_lz77(sound_data+8, size-8, &size); + //printf("de-LZ: %p %x %x %x %x %.4s\n", lz_data, + // node[i].size, size, lz_hdr, lz_hdr>>8, (char*)lz_data); + sound_data = lz_data; + goto retry; + } + //SAFE_FREE(lz_data); + if(lz_data){ + free(lz_data); + lz_data=NULL;} + + } + } + //printf("snd: %p %x\n", snd->dsp_data, snd->size); +} + + diff --git a/plugins/mighty/source/sonido.h b/plugins/mighty/source/sonido.h new file mode 100644 index 0000000..c573ac4 --- /dev/null +++ b/plugins/mighty/source/sonido.h @@ -0,0 +1,32 @@ +#ifndef _SONIDO_H_ +#define _SONIDO_H_ + +typedef struct SoundInfo +{ + void *dsp_data; + int size; + int channels; + int rate; + int loop; +} SoundInfo; + +void hex_dump2(void *p, int size); + +static inline u32 _be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} +static inline u32 _le32(const void *d) +{ + const u8 *p = d; + return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; +} +static inline u32 _le16(const void *d) +{ + const u8 *p = d; + return (p[1] << 8) | p[0]; +} + +void parse_banner_snd(void *data_hdr, SoundInfo *snd); + +#endif \ No newline at end of file diff --git a/plugins/mighty/source/stub.s b/plugins/mighty/source/stub.s new file mode 100644 index 0000000..ff72755 --- /dev/null +++ b/plugins/mighty/source/stub.s @@ -0,0 +1,18 @@ +#define STUB 0x3400 + + .text + .section .text + .globl _unstub_start + +_unstub_start: + isync + // set MSR[DR:IR] = 00, jump to STUB + lis 3,STUB@h + ori 3,3,STUB@l + mtsrr0 3 + + mfmsr 3 + li 4,0x30 + andc 3,3,4 + mtsrr1 3 + rfi diff --git a/plugins/mighty/source/tools.c b/plugins/mighty/source/tools.c new file mode 100644 index 0000000..9e61879 --- /dev/null +++ b/plugins/mighty/source/tools.c @@ -0,0 +1,323 @@ +/******************************************************************************* + * tools.c + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "tools.h" + +void *allocate_memory(u32 size){ + return memalign(32, (size+31)&(~31) ); +} + +/* +u32 Pad_WaitButtons(void){ + u32 buttons=0; + + for(;;){ + //Obtener botones pulsados de Wiimotes + buttons=0; + WPAD_ScanPads(); + //for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + // buttons |= WPAD_ButtonsDown(cnt); + buttons=WPAD_ButtonsDown(0); + if(buttons){ + return buttons; + } + VIDEO_WaitVSync(); + + //Obtener botones pulsados de mandos GC + buttons=0; + PAD_ScanPads(); + buttons=PAD_ButtonsDown(0); + if(buttons){ + if(buttons & PAD_BUTTON_UP) + return WPAD_BUTTON_UP; + else if(buttons & PAD_BUTTON_DOWN) + return WPAD_BUTTON_DOWN; + else if(buttons & PAD_BUTTON_LEFT) + return WPAD_BUTTON_LEFT; + else if(buttons & PAD_BUTTON_RIGHT) + return WPAD_BUTTON_RIGHT; + else if(buttons & PAD_BUTTON_A) + return WPAD_BUTTON_A; + else if(buttons & PAD_BUTTON_B) + return WPAD_BUTTON_B; + else if(buttons & PAD_BUTTON_Y) + return WPAD_BUTTON_1; + else if(buttons & PAD_BUTTON_X) + return WPAD_BUTTON_2; + else if(buttons & PAD_TRIGGER_R) + return WPAD_BUTTON_PLUS; + else if(buttons & PAD_TRIGGER_L) + return WPAD_BUTTON_MINUS; + else if(buttons & PAD_BUTTON_START) + return WPAD_BUTTON_HOME; + } + VIDEO_WaitVSync(); + + } +}*/ + +s32 __FileCmp(const void *a, const void *b){ + dirent_t *hdr1 = (dirent_t *)a; + dirent_t *hdr2 = (dirent_t *)b; + + if (hdr1->type == hdr2->type){ + return strcmp(hdr1->name, hdr2->name); + }else{ + return 0; + } +} + +s32 getdir(char *path, dirent_t **ent, u32 *cnt){ + s32 res; + u32 num = 0; + + int i, j, k; + + res = ISFS_ReadDir(path, NULL, &num); + if(res != ISFS_OK){ + printf("Error: could not get dir entry count! (result: %d)\n", res); + return -1; + } + + char ebuf[ISFS_MAXPATH + 1]; + + char *nbuf = (char *)allocate_memory((ISFS_MAXPATH + 1) * num); + if(nbuf == NULL){ + printf("ERROR: could not allocate buffer for name list!\n"); + return -2; + } + + res = ISFS_ReadDir(path, nbuf, &num); + DCFlushRange(nbuf,13*num); //quick fix for cache problems? + if(res != ISFS_OK){ + printf("ERROR: could not get name list! (result: %d)\n", res); + free(nbuf); + return -3; + } + + *cnt = num; + + *ent = allocate_memory(sizeof(dirent_t) * num); + if(*ent==NULL){ + printf("Error: could not allocate buffer\n"); + free(nbuf); + return -4; + } + + for(i = 0, k = 0; i < num; i++){ + for(j = 0; nbuf[k] != 0; j++, k++) + ebuf[j] = nbuf[k]; + ebuf[j] = 0; + k++; + + strcpy((*ent)[i].name, ebuf); + } + + qsort(*ent, *cnt, sizeof(dirent_t), __FileCmp); + + free(nbuf); + return 0; +} + +s32 read_file(char *filepath, u8 **buffer, u32 *filesize){ + s32 Fd; + int ret; + + if(buffer == NULL){ + printf("NULL Pointer\n"); + return -1; + } + + Fd = ISFS_Open(filepath, ISFS_OPEN_READ); + if (Fd < 0){ + //printf("\n * ISFS_Open %s failed %d", filepath, Fd); + //Pad_WaitButtons(); + return Fd; + } + + fstats *status; + status = allocate_memory(sizeof(fstats)); + if (status == NULL){ + printf("Out of memory for status\n"); + return -1; + } + + ret = ISFS_GetFileStats(Fd, status); + if (ret < 0){ + printf("ISFS_GetFileStats failed %d\n", ret); + ISFS_Close(Fd); + free(status); + return -1; + } + + *buffer = allocate_memory(status->file_length); + if (*buffer == NULL){ + printf("Out of memory for buffer\n"); + ISFS_Close(Fd); + free(status); + return -1; + } + + ret = ISFS_Read(Fd, *buffer, status->file_length); + if (ret < 0){ + printf("ISFS_Read failed %d\n", ret); + ISFS_Close(Fd); + free(status); + free(*buffer); + return ret; + } + ISFS_Close(Fd); + + *filesize = status->file_length; + free(status); + + return 0; +} + +s32 Identify_GenerateTik(signed_blob **outbuf, u32 *outlen){ + signed_blob *buffer = NULL; + + sig_rsa2048 *signature = NULL; + tik *tik_data = NULL; + + u32 len; + + /* Set ticket length */ + len = STD_SIGNED_TIK_SIZE; + + /* Allocate memory */ + buffer = (signed_blob *)memalign(32, len); + if (!buffer) + return -1; + + /* Clear buffer */ + memset(buffer, 0, len); + + /* Generate signature */ + signature=(sig_rsa2048 *)buffer; + signature->type = ES_SIG_RSA2048; + + /* Generate ticket */ + tik_data = (tik *)SIGNATURE_PAYLOAD(buffer); + + strcpy(tik_data->issuer, "Root-CA00000001-XS00000003"); + memset(tik_data->cidx_mask, 0xFF, 32); + + /* Set values */ + *outbuf = buffer; + *outlen = len; + + return 0; +} + + +s32 identify(u64 titleid, u32 *ios){ + char filepath[ISFS_MAXPATH] ATTRIBUTE_ALIGN(0x20); + u8 *tmdBuffer = NULL; + u32 tmdSize; + signed_blob *tikBuffer = NULL; + u32 tikSize; + u8 *certBuffer = NULL; + u32 certSize; + + int ret; + + printf("Reading TMD..."); + fflush(stdout); + + sprintf(filepath, "/title/%08x/%08x/content/title.tmd", TITLE_UPPER(titleid), TITLE_LOWER(titleid)); + ret = read_file(filepath, &tmdBuffer, &tmdSize); + if (ret < 0) + { + printf("Reading TMD failed\n"); + return ret; + } + printf("done\n"); + + *ios = (u32)(tmdBuffer[0x18b]); + + printf("Generating fake ticket..."); + fflush(stdout); + /* + sprintf(filepath, "/ticket/%08x/%08x.tik", TITLE_UPPER(titleid), TITLE_LOWER(titleid)); + ret = read_file(filepath, &tikBuffer, &tikSize); + if (ret < 0) + { + printf("Reading ticket failed\n"); + free(tmdBuffer); + return ret; + }*/ + Identify_GenerateTik(&tikBuffer,&tikSize); + printf("done\n"); + + printf("Reading certs..."); + fflush(stdout); + + sprintf(filepath, "/sys/cert.sys"); + ret = read_file(filepath, &certBuffer, &certSize); + if (ret < 0) + { + printf("Reading certs failed\n"); + free(tmdBuffer); + free(tikBuffer); + return ret; + } + printf("done\n"); + + printf("ES_Identify..."); + fflush(stdout); + + ret = ES_Identify((signed_blob*)certBuffer, certSize, (signed_blob*)tmdBuffer, tmdSize, tikBuffer, tikSize, NULL); + if (ret < 0) + { + switch(ret) + { + case ES_EINVAL: + printf("Error! ES_Identify (ret = %d;) Data invalid!\n", ret); + break; + case ES_EALIGN: + printf("Error! ES_Identify (ret = %d;) Data not aligned!\n", ret); + break; + case ES_ENOTINIT: + printf("Error! ES_Identify (ret = %d;) ES not initialized!\n", ret); + break; + case ES_ENOMEM: + printf("Error! ES_Identify (ret = %d;) No memory!\n", ret); + break; + default: + printf("Error! ES_Identify (ret = %d)\n", ret); + break; + } + free(tmdBuffer); + free(tikBuffer); + free(certBuffer); + return ret; + } + printf("done\n"); + + free(tmdBuffer); + free(tikBuffer); + free(certBuffer); + return 0; +} + diff --git a/plugins/mighty/source/tools.h b/plugins/mighty/source/tools.h new file mode 100644 index 0000000..cf4c86d --- /dev/null +++ b/plugins/mighty/source/tools.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * tools.h + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ +typedef struct _dirent{ + char name[ISFS_MAXPATH + 1]; + int type; +} dirent_t; + +#include +#include +#include +#include +#include +#include + +#define TITLE_UPPER(x) ((u32)((x) >> 32)) +#define TITLE_LOWER(x) ((u32)(x)) +#define TITLE_ID(x,y) (((u64)(x) << 32) | (y)) + +void *allocate_memory(u32 size); +s32 getdir(char *, dirent_t **, u32 *); +s32 read_file(char *filepath, u8 **buffer, u32 *filesize); +s32 identify(u64 titleid, u32 *ios); +//u32 Pad_WaitButtons(void); + + diff --git a/plugins/mighty/source/usbstorage.c b/plugins/mighty/source/usbstorage.c new file mode 100644 index 0000000..033c9f0 --- /dev/null +++ b/plugins/mighty/source/usbstorage.c @@ -0,0 +1,291 @@ +/*------------------------------------------------------------- + +usbstorage_starlet.c -- USB mass storage support, inside starlet +Copyright (C) 2009 Kwiirk + +If this driver is linked before libogc, this will replace the original +usbstorage driver by svpe from libogc +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. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include + +/* IOCTL commands */ +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) +#define USB_IOCTL_UMS_UNMOUNT (UMS_BASE+0x10) +#define USB_IOCTL_UMS_WATCHDOG (UMS_BASE+0x80) + +#define UMS_HEAPSIZE 0x1000 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/usb2"; +static char fs2[] ATTRIBUTE_ALIGN(32) = "/dev/usb/ehc"; + +static s32 hid = -1, fd = -1; +static u32 sector_size; + +extern void* SYS_AllocArena2MemLo(u32 size,u32 align); +static void *mem2_ptr=NULL; + +inline s32 __USBStorage_isMEM2Buffer(const void *buffer) { + u32 high_addr = ((u32)buffer) >> 24; + + return (high_addr == 0x90) || (high_addr == 0xD0); +} + + +s32 USBStorage_GetCapacity(u32 *_sector_size) { + if (fd > 0) { + s32 ret; + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_GET_CAPACITY, ":i", §or_size); + + if (ret && _sector_size) + *_sector_size = sector_size; + + return ret; + } + + return IPC_ENOENT; +} + +s32 USBStorage_Init(void) { + s32 ret; + + /* Already open */ + if (fd > 0) + return 0; + + /* Create heap */ + if (hid < 0) { + hid = iosCreateHeap(UMS_HEAPSIZE); + if (hid < 0) + return IPC_ENOMEM; + } + + /* Open USB device */ + fd = IOS_Open(fs, 0); + if (fd < 0) + fd = IOS_Open(fs2, 0); + if (fd < 0) + return fd; + + /* Initialize USB storage */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_INIT, ":"); + if (ret<0) goto err; + + /* Get device capacity */ + ret = USBStorage_GetCapacity(NULL); + if (!ret) + goto err; + + return 0; + +err: + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } + + return -1; +} + +/** Hermes **/ +s32 USBStorage_Watchdog(u32 on_off) { + if (fd >= 0) { + s32 ret; + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WATCHDOG, "i:", on_off); + + return ret; + } + + return IPC_ENOENT; +} + +s32 USBStorage_Umount(void) { + if (fd >= 0) { + s32 ret; + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_UNMOUNT, ":"); + return ret; + } + + return IPC_ENOENT; +} + +void USBStorage_Deinit(void) { + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } +} + +s32 USBStorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) { + + void *buf = (void *)buffer; + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + if(!mem2_ptr) mem2_ptr=SYS_AllocArena2MemLo(2048*256,32); + /* MEM1 buffer */ + if (!__USBStorage_isMEM2Buffer(buffer)) { + /* Allocate memory */ + buf = mem2_ptr; //iosAlloc(hid, len); + if (!buf) + return IPC_ENOMEM; + } + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", sector, numSectors, buf, len); + + /* Copy data */ + if (buf != buffer) { + memcpy(buffer, buf, len); + //iosFree(hid, buf); + } + + return ret; +} + +s32 USBStorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) { + + void *buf = (void *)buffer; + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + if(!mem2_ptr) mem2_ptr = SYS_AllocArena2MemLo(2048*256,32); + + + /* MEM1 buffer */ + if (!__USBStorage_isMEM2Buffer(buffer)) { + /* Allocate memory */ + buf = mem2_ptr; //buf = iosAlloc(hid, len); + if (!buf) + return IPC_ENOMEM; + + /* Copy data */ + memcpy(buf, buffer, len); + } + + /* Write data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WRITE_SECTORS, "ii:d", sector, numSectors, buf, len); + + /* Free memory */ + if (buf != buffer) + iosFree(hid, buf); + + return ret; +} + + +#define DEVICE_TYPE_WII_UMS (('W'<<24)|('U'<<16)|('M'<<8)|'S') + + +bool umsio_Startup() { + return USBStorage_Init() == 0; +} + +bool umsio_IsInserted() { + return true; // allways true +} +bool umsio_ReadSectors(sec_t sector, sec_t numSectors, u8 *buffer) { + u32 cnt = 0; + s32 ret; + /* Do reads */ + while (cnt < numSectors) { + u32 sectors = (numSectors - cnt); + + /* Read sectors is too big */ + if (sectors > 32) + sectors = 32; + + /* USB read */ + ret = USBStorage_ReadSectors(sector + cnt, sectors, &buffer[cnt*512]); + if (ret < 0) + return false; + + /* Increment counter */ + cnt += sectors; + } + + return true; +} + +bool umsio_WriteSectors(sec_t sector, sec_t numSectors, const u8* buffer) { + u32 cnt = 0; + s32 ret; + + /* Do writes */ + while (cnt < numSectors) { + u32 sectors = (numSectors - cnt); + + /* Write sectors is too big */ + if (sectors > 32) + sectors = 32; + + /* USB write */ + ret = USBStorage_WriteSectors(sector + cnt, sectors, &buffer[cnt * 512]); + if (ret < 0) + return false; + + /* Increment counter */ + cnt += sectors; + } + + return true; +} +bool umsio_ClearStatus(void) { + return true; +} + +bool umsio_Shutdown() { + USBStorage_Deinit(); + return true; +} +const DISC_INTERFACE __io_wiiums = { + DEVICE_TYPE_WII_UMS, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP)&umsio_Startup, + (FN_MEDIUM_ISINSERTED)&umsio_IsInserted, + (FN_MEDIUM_READSECTORS)&umsio_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&umsio_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&umsio_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&umsio_Shutdown +}; diff --git a/plugins/mighty/source/usbstorage.h b/plugins/mighty/source/usbstorage.h new file mode 100644 index 0000000..835cc1b --- /dev/null +++ b/plugins/mighty/source/usbstorage.h @@ -0,0 +1,19 @@ +#ifndef _USBSTORAGE_H_ +#define _USBSTORAGE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + /* Prototypes */ + s32 USBStorage_GetCapacity(u32 *); + s32 USBStorage_Init(void); + void USBStorage_Deinit(void); + s32 USBStorage_Watchdog(u32 on_off); + s32 USBStorage_ReadSectors(u32, u32, void *); + s32 USBStorage_WriteSectors(u32, u32, const void *); + extern const DISC_INTERFACE __io_wiiums; +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/mighty/source/video.c b/plugins/mighty/source/video.c new file mode 100644 index 0000000..adf6010 --- /dev/null +++ b/plugins/mighty/source/video.c @@ -0,0 +1,92 @@ +#include +#include +#include //for malloc + +#include "video.h" +#include "tools.h" + +// Video variables +u32* framebuffer = NULL; +GXRModeObj *vmode = NULL; + +void Con_Init(u32 x, u32 y, u32 w, u32 h){ + // Create console in the framebuffer + CON_InitEx(vmode, x, y, w, h); +} + +void Con_Clear(void){ + // Clear console + printf("\x1b[2J"); + fflush(stdout); +} + +void Con_ClearLine(void){ + s32 cols, rows; + u32 cnt; + + printf("\r"); + fflush(stdout); + + // Get console metrics + CON_GetMetrics(&cols, &rows); + + // Erase line + for(cnt = 1; cnt < cols; cnt++){ + printf(" "); + fflush(stdout); + } + + printf("\r"); + fflush(stdout); +} + +void Video_Init(void){ + VIDEO_Init(); + vmode = VIDEO_GetPreferredMode(0); + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); + VIDEO_Configure(vmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(vmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + + VIDEO_ClearFrameBuffer(vmode, framebuffer, COLOR_BLACK); +} + +#define MASK_W 32 +#define MASK_H 32 +static const u8 mask[MASK_H][MASK_W]={ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 208, 208, 208, 208, 208, 208, 208}, + { 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 208, 208, 208, 208, 208, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 0, 0, 0, 0, 128, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 0, 0, 128, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} +}; \ No newline at end of file diff --git a/plugins/mighty/source/video.h b/plugins/mighty/source/video.h new file mode 100644 index 0000000..b8134cf --- /dev/null +++ b/plugins/mighty/source/video.h @@ -0,0 +1,18 @@ +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#define CONSOLE_X 50 +#define CONSOLE_Y 50 +#define CONSOLE_WIDTH 384 +#define CONSOLE_HEIGHT 224 + +/* Prototypes */ +void Con_Init(u32, u32, u32, u32); +void Con_Clear(void); +void Con_ClearLine(void); + +void Video_Init(void); + +//void MRC_Capture(void); + +#endif diff --git a/source/GRRLIB.c b/source/GRRLIB.c new file mode 100644 index 0000000..215dc0b --- /dev/null +++ b/source/GRRLIB.c @@ -0,0 +1,1600 @@ +/*=========================================== + GRRLIB 4.0.0 + Code : NoNameNo + Additional Code : Crayon + GX hints : RedShade + +Download and Help Forum : http://grrlib.santo.fr +===========================================*/ + +// Modified by oggzee and usptactical + +#include +#include +#include +#include +#include +#include +#include "pngu/pngu.h" +#include +#include "jpeglib.h" +#include "my_GRRLIB.h" +#include +#include "console.h" +#include "mem.h" + +#define DEFAULT_FIFO_SIZE (256 * 1024) +void *gp_fifo = NULL; + +/** + * Convert a raw bmp (RGB, no alpha) to 4x4RGBA. + * @author DrTwox + * @param src + * @param dst + * @param width + * @param height +*/ +static void RawTo4x4RGBA(const unsigned char *src, void *dst, const unsigned int width, const unsigned int height) { + unsigned int block; + unsigned int i; + unsigned int c; + unsigned int ar; + unsigned int gb; + unsigned char *p = (unsigned char*)dst; + + for (block = 0; block < height; block += 4) { + for (i = 0; i < width; i += 4) { + /* Alpha and Red */ + for (c = 0; c < 4; ++c) { + for (ar = 0; ar < 4; ++ar) { + /* Alpha pixels */ + *p++ = 255; + /* Red pixels */ + *p++ = src[((i + ar) + ((block + c) * width)) * 3]; + } + } + + /* Green and Blue */ + for (c = 0; c < 4; ++c) { + for (gb = 0; gb < 4; ++gb) { + /* Green pixels */ + *p++ = src[(((i + gb) + ((block + c) * width)) * 3) + 1]; + /* Blue pixels */ + *p++ = src[(((i + gb) + ((block + c) * width)) * 3) + 2]; + } + } + } /* i */ + } /* block */ +} + +//************************************************* +//* jpeglib code cleanup and error handling added by usptactical + +//error manager struct for jpeglib +struct my_error_mgr { + struct jpeg_error_mgr pub; + //return to the caller on error + jmp_buf setjmp_buffer; +}; +typedef struct my_error_mgr * my_error_ptr; + +/** + * Overrides the standard jpeglib error handler + * @param cinfo Pointer to my_error_mgr struct + * @return void + */ +METHODDEF(void) my_error_exit (j_common_ptr cinfo) +{ + //cinfo->err really points to a my_error_mgr struct + my_error_ptr myerr = (my_error_ptr) cinfo->err; + + //display the internal jpeglib error message + //(*cinfo->err->output_message) (cinfo); + + jpeg_abort((j_common_ptr) cinfo); + + //Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); +} + +/** + * Output message handler for jpeglib errors + * @param cinfo Pointer to my_error_mgr struct + * @return void + */ +METHODDEF(void) output_message(j_common_ptr cinfo) +{ + // display an error message. + printf("\nError with JPEG!\n"); +} + + +/** + * Load a jpg from a buffer. + * Take Care to have a JPG finnishing by 0xFF 0xD9 !!!! + * @param my_jpg the JPEG buffer to load. + * @return allocated image buffer + */ +void* Load_JPG_RGB(const unsigned char my_jpg[], int *w, int *h) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + JSAMPROW row_pointer[1]; + unsigned char *tempBuffer = 0; + unsigned int i; + int n; + + //init the texture object + *w = 0; + *h = 0; + + //get the jpg size + n = 0; + if((my_jpg[0]==0xff) && (my_jpg[1]==0xd8) && (my_jpg[2]==0xff)) { + while(1) { + if((my_jpg[n]==0xff) && (my_jpg[n+1]==0xd9)) + break; + n++; + } + n+=2; + } + + //Set up the error handler first in case the initialization step fails + cinfo.err = jpeg_std_error(&jerr.pub); + cinfo.err->error_exit = my_error_exit; + cinfo.err->output_message = output_message; + //Establish the setjmp return context for my_error_exit to use + if (setjmp(jerr.setjmp_buffer)) { + //If we get here, the JPEG code has signaled an error. + //We need to clean up the JPEG object, and return. + //TODO: mem is not being cleaned up properly - I assume jpeglib's mem alloc is the culprit + jpeg_destroy_decompress(&cinfo); + SAFE_FREE(row_pointer[0]); + SAFE_FREE(tempBuffer); + *w = -666; + return NULL; + } + + //initialize the decompression object + jpeg_create_decompress(&cinfo); + cinfo.progress = NULL; + +// jpeg_memory_src(&cinfo, my_jpg, n); + jpeg_mem_src(&cinfo, (unsigned char *)my_jpg, n); + + jpeg_read_header(&cinfo, TRUE); + if (cinfo.jpeg_color_space == JCS_GRAYSCALE) + cinfo.out_color_space = JCS_RGB; //JCS_CMYK; //JCS_RGB; + //these speed up decompression... + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + //initialize internal state, allocate working memory, and prepare for returning data + jpeg_start_decompress(&cinfo); + + tempBuffer = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); + row_pointer[0] = malloc(cinfo.output_width * cinfo.output_components); + + size_t location = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, row_pointer, 1); + for (i = 0; i < cinfo.image_width * cinfo.output_components; i++) { + /* Put the decoded scanline into the tempBuffer */ + tempBuffer[ location++ ] = row_pointer[0][i]; + } + } + + //Complete the decompression cycle. This causes working memory + // associated with the JPEG object to be released. + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + SAFE_FREE(row_pointer[0]); + + //set texture dimensions + *w = cinfo.output_width; + *h = cinfo.output_height; + return tempBuffer; +} + +/** + * Load a texture from a buffer. + * Take Care to have a JPG finnishing by 0xFF 0xD9 !!!! + * @author DrTwox + * @param my_jpg the JPEG buffer to load. + * @return A GRRLIB_texImg structure filled with PNG informations. + */ +GRRLIB_texImg my_GRRLIB_LoadTextureJPG(const unsigned char my_jpg[]) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + JSAMPROW row_pointer[1]; + unsigned char *tempBuffer = 0; + GRRLIB_texImg my_texture; + unsigned int i; + int n; + + //init the texture object + my_texture.w = 0; + my_texture.h = 0; + my_texture.data = NULL; + + //get the jpg size + n = 0; + if((my_jpg[0]==0xff) && (my_jpg[1]==0xd8) && (my_jpg[2]==0xff)) { + while(1) { + if((my_jpg[n]==0xff) && (my_jpg[n+1]==0xd9)) + break; + n++; + } + n+=2; + } + + //Set up the error handler first in case the initialization step fails + cinfo.err = jpeg_std_error(&jerr.pub); + cinfo.err->error_exit = my_error_exit; + cinfo.err->output_message = output_message; + //Establish the setjmp return context for my_error_exit to use + if (setjmp(jerr.setjmp_buffer)) { + //If we get here, the JPEG code has signaled an error. + //We need to clean up the JPEG object, and return. + //TODO: mem is not being cleaned up properly - I assume jpeglib's mem alloc is the culprit + jpeg_destroy_decompress(&cinfo); + SAFE_FREE(row_pointer[0]); + SAFE_FREE(tempBuffer); + SAFE_FREE(my_texture.data); + my_texture.w = -666; + return my_texture; + } + + //initialize the decompression object + jpeg_create_decompress(&cinfo); + cinfo.progress = NULL; + +// jpeg_memory_src(&cinfo, my_jpg, n); + jpeg_mem_src(&cinfo, (unsigned char *)my_jpg, n); + + jpeg_read_header(&cinfo, TRUE); + if (cinfo.jpeg_color_space == JCS_GRAYSCALE) + cinfo.out_color_space = JCS_RGB; //JCS_CMYK; //JCS_RGB; + //these speed up decompression... + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + //initialize internal state, allocate working memory, and prepare for returning data + jpeg_start_decompress(&cinfo); + + +//TODO...this seems to work ok + //tempBuffer = mem_alloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); + //row_pointer[0] = mem_alloc(cinfo.output_width * cinfo.output_components); + + tempBuffer = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); + row_pointer[0] = malloc(cinfo.output_width * cinfo.output_components); + + size_t location = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, row_pointer, 1); + for (i = 0; i < cinfo.image_width * cinfo.output_components; i++) { + /* Put the decoded scanline into the tempBuffer */ + tempBuffer[ location++ ] = row_pointer[0][i]; + } + } + + //Complete the decompression cycle. This causes working memory + // associated with the JPEG object to be released. + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + SAFE_FREE(row_pointer[0]); + + /* Create a buffer to hold the final texture */ + //my_texture.data = memalign(32, cinfo.output_width * cinfo.output_height * 4); + my_texture.data = mem_alloc(cinfo.output_width * cinfo.output_height * 4); + RawTo4x4RGBA(tempBuffer, my_texture.data, cinfo.output_width, cinfo.output_height); + SAFE_FREE(tempBuffer); + //set texture dimensions + my_texture.w = cinfo.output_width; + my_texture.h = cinfo.output_height; + GRRLIB_FlushTex(&my_texture); + return my_texture; +} + +int _GRRLIB_Init(void *fb0, void *fb1) +{ + f32 yscale; + u32 xfbHeight; + Mtx44 perspective; + + if (fb0) { + xfb[0] = fb0; + } else { + xfb[0] = (u32 *)MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + } + + if (fb1) { + xfb[1] = fb1; + } else { + xfb[1] = (u32 *)MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + } + + if(xfb[0] == NULL || xfb[1] == NULL) + return -1; + + if (fb0 == NULL) + VIDEO_ClearFrameBuffer(rmode, xfb[0], COLOR_BLACK); + if (fb1 == NULL) + VIDEO_ClearFrameBuffer(rmode, xfb[1], COLOR_BLACK); + + fb = 0; + + if (fb0 == NULL) { + VIDEO_SetNextFramebuffer(xfb[fb]); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) + VIDEO_WaitVSync(); + } + + gp_fifo = (u8 *) memalign(32, DEFAULT_FIFO_SIZE); + if(gp_fifo == NULL) + return -1; + memset(gp_fifo, 0, DEFAULT_FIFO_SIZE); + GX_Init(gp_fifo, DEFAULT_FIFO_SIZE); + + // clears the bg to color and clears the z buffer + GXColor background = { 0, 0, 0, 0xff }; + GX_SetCopyClear (background, GX_MAX_Z24); + + // other gx setup + yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight); + xfbHeight = GX_SetDispCopyYScale(yscale); + GX_SetScissor(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetDispCopyDst(rmode->fbWidth, xfbHeight); + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE)); + + if (rmode->aa) + GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); + else + GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + + GX_SetDispCopyGamma(GX_GM_1_0); + + + // setup the vertex descriptor + // tells the flipper to expect direct data + GX_ClearVtxDesc(); + GX_InvVtxCache (); + GX_InvalidateTexAll(); + + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + + + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + GX_SetZMode (GX_FALSE, GX_LEQUAL, GX_TRUE); + + GX_SetNumChans(1); + GX_SetNumTexGens(1); + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + guMtxIdentity(GXmodelView2D); + guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -50.0F); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + guOrtho(perspective,0, 480, 0, 640, 0, 300.0F); + GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC); + + GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetAlphaUpdate(GX_TRUE); + + GX_SetCullMode(GX_CULL_NONE); + + GRRLIB_Settings.antialias = true; + + return 0; +} + +#if 0 + +void GRRLIB_Init() +{ + _GRRLIB_Init_Video(); + _GRRLIB_Init(NULL, NULL); +} + +#endif + +int GRRLIB_Init_VMode(GXRModeObj *a_rmode, void *fb0, void *fb1) +{ + if (a_rmode == NULL) return -1; + rmode = a_rmode; + return _GRRLIB_Init(fb0, fb1); +} + +#if 0 + +/** + * Call this function after drawing. + */ +void GRRLIB_Render() { + GX_DrawDone (); + + fb ^= 1; // flip framebuffer + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(xfb[fb], GX_TRUE); + VIDEO_SetNextFramebuffer(xfb[fb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); +} + +#endif + +void _GRRLIB_Render() { + GX_DrawDone (); + fb ^= 1; // flip framebuffer + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(xfb[fb], GX_TRUE); +} + +void _GRRLIB_VSync() { + VIDEO_SetNextFramebuffer(xfb[fb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); +} + +void** _GRRLIB_GetXFB(int *cur_fb) +{ + *cur_fb = fb; + return xfb; +} + +void _GRRLIB_SetFB(int cur_fb) +{ + fb = cur_fb; +} + +#if 0 + +/** + * Call this before exiting your application. + */ +void GRRLIB_Exit() { + GX_Flush(); + GX_AbortFrame(); + + if(xfb[0] != NULL) { + free(MEM_K1_TO_K0(xfb[0])); + xfb[0] = NULL; + } + if(xfb[1] != NULL) { + free(MEM_K1_TO_K0(xfb[1])); + xfb[1] = NULL; + } + if(gp_fifo != NULL) { + free(gp_fifo); + gp_fifo = NULL; + } +} + +#endif + +void _GRRLIB_Exit() { + GX_Flush(); + GX_AbortFrame(); + + if(gp_fifo != NULL) { + free(gp_fifo); + gp_fifo = NULL; + } +} + + +void GRRLIB_DrawTile_begin0(GRRLIB_texImg *tex) +{ + GXTexObj texObj; + + GX_InitTexObj(&texObj, tex->data, tex->tilew*tex->nbtilew, tex->tileh*tex->nbtileh, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + const int antialias = 1; + if (antialias == 0) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + GX_LoadTexObj(&texObj, GX_TEXMAP0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); +} + +void GRRLIB_DrawTile_begin(GRRLIB_texImg *tex, f32 x, f32 y, f32 scale) +{ + GRRLIB_DrawTile_begin0(tex); + + Mtx m, m1, m2, mv; + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scale, scale, 1.0f); + guVector axis = (guVector) {0, 0, 1 }; + guMtxRotAxisDeg (m2, &axis, 0); // 0 degrees rotate + guMtxConcat(m2, m1, m); + guMtxTransApply(m, m, x, y, 0); + guMtxConcat (GXmodelView2D, m, mv); + GX_LoadPosMtxImm (m, GX_PNMTX0); +} + + +inline void GRRLIB_DrawTile_draw(f32 xpos, f32 ypos, GRRLIB_texImg tex, float degrees, float scaleX, f32 scaleY, u32 color, int frame) +{ + f32 width, height; + Mtx m, m1, m2, mv; + + // Frame Correction by spiffen + f32 FRAME_CORR = 0.001f; + f32 s1 = (((frame%tex.nbtilew))/(f32)tex.nbtilew)+(FRAME_CORR/tex.w); + f32 s2 = (((frame%tex.nbtilew)+1)/(f32)tex.nbtilew)-(FRAME_CORR/tex.w); + f32 t1 = (((int)(frame/tex.nbtilew))/(f32)tex.nbtileh)+(FRAME_CORR/tex.h); + f32 t2 = (((int)(frame/tex.nbtilew)+1)/(f32)tex.nbtileh)-(FRAME_CORR/tex.h); + + width = tex.tilew * 0.5f; + height = tex.tileh * 0.5f; + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0f); + guVector axis = (guVector) {0, 0, 1 }; + guMtxRotAxisDeg (m2, &axis, degrees); + guMtxConcat(m2, m1, m); + guMtxTransApply(m, m, xpos+width, ypos+height, 0); + guMtxConcat (GXmodelView2D, m, mv); + GX_LoadPosMtxImm (mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(width, -height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(width, height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(-width, height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0); +} + +inline void GRRLIB_DrawTile_draw1(f32 xpos, f32 ypos, GRRLIB_texImg *tex, u32 color, int frame) +{ + //f32 width, height; + + // Frame Correction by spiffen + const f32 FRAME_CORR = 0.001f; + f32 s1, s2, t1, t2; + s1 = (frame % tex->nbtilew) * tex->ofnormaltexx; + s2 = s1 + tex->ofnormaltexx; + t1 = (int)(frame/tex->nbtilew) * tex->ofnormaltexy; + t2 = t1 + tex->ofnormaltexy; + s1 += FRAME_CORR; + s2 -= FRAME_CORR; + t1 += FRAME_CORR; + t2 -= 0.0015f; + + + //width = tex->tilew * 0.5f; + //height = tex->tileh * 0.5f; + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(xpos, ypos, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(xpos+tex->tilew, ypos, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(xpos+tex->tilew, ypos+tex->tileh, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(xpos, ypos+tex->tileh, 0); + GX_Color1u32(color); + GX_TexCoord2f32(s1, t2); + GX_End(); +} + +inline void GRRLIB_DrawTile_end0(GRRLIB_texImg *tex) +{ + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); +} + +inline void GRRLIB_DrawTile_end(GRRLIB_texImg *tex) +{ + GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0); + GRRLIB_DrawTile_end0(tex); +} + +/** + * Draw a slice. + * @param xpos specifies the x-coordinate of the upper-left corner. + * @param ypos specifies the y-coordinate of the upper-left corner. + * @param tex texture to draw. + * @param degrees angle of rotation. + * @param scaleX + * @param scaleY + * @param color1 top + * @param color2 bottom + * @param x,y,w,h slice coords inside texture + */ +void GRRLIB_DrawSlice2(f32 xpos, f32 ypos, GRRLIB_texImg tex, float degrees, + float scaleX, f32 scaleY, u32 color1, u32 color2, + float x, float y, float w, float h) +{ + GXTexObj texObj; + f32 width, height; + Mtx m, m1, m2, mv; + + // Frame Correction by spiffen + /* + f32 FRAME_CORR = 0.001f; + f32 s1 = (((frame%tex.nbtilew))/(f32)tex.nbtilew)+(FRAME_CORR/tex.w); + f32 s2 = (((frame%tex.nbtilew)+1)/(f32)tex.nbtilew)-(FRAME_CORR/tex.w); + f32 t1 = (((int)(frame/tex.nbtilew))/(f32)tex.nbtileh)+(FRAME_CORR/tex.h); + f32 t2 = (((int)(frame/tex.nbtilew)+1)/(f32)tex.nbtileh)-(FRAME_CORR/tex.h); + */ + f32 FRAME_CORR = 0.001f; + f32 s1 = (x / (f32)tex.w) + (FRAME_CORR/tex.w); + f32 s2 = ((x+w) / (f32)tex.w) - (FRAME_CORR/tex.w); + f32 t1 = (y / (f32)tex.h) + (FRAME_CORR/tex.h); + f32 t2 = ((y+h) / (f32)tex.h) - (FRAME_CORR/tex.h); + + //GX_InitTexObj(&texObj, tex.data, tex.tilew*tex.nbtilew, tex.tileh*tex.nbtileh, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_InitTexObj(&texObj, tex.data, tex.w, tex.h, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_LoadTexObj(&texObj, GX_TEXMAP0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); + + //width = tex.tilew * 0.5f; + //height = tex.tileh * 0.5f; + width = w * 0.5f; + height = h * 0.5f; + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0f); + guVector axis = (guVector) {0, 0, 1 }; + guMtxRotAxisDeg (m2, &axis, degrees); + guMtxConcat(m2, m1, m); + guMtxTransApply(m, m, xpos+width, ypos+height, 0); + guMtxConcat (GXmodelView2D, m, mv); + GX_LoadPosMtxImm (mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32(color1); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(width, -height, 0); + GX_Color1u32(color1); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(width, height, 0); + GX_Color1u32(color2); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(-width, height, 0); + GX_Color1u32(color2); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); +} + +void GRRLIB_DrawSlice(f32 xpos, f32 ypos, GRRLIB_texImg tex, float degrees, + float scaleX, f32 scaleY, u32 color, + float x, float y, float w, float h) +{ + GRRLIB_DrawSlice2(xpos, ypos, tex, degrees, + scaleX, scaleY, color, color, + x, y, w, h); +} + +#define UF_CACHE_SIZE 512 +int uf_cache_c[UF_CACHE_SIZE]; +struct GRRLIB_texImg uf_cache_tx[UF_CACHE_SIZE]; +void *uf_cache_data = NULL; +int uf_cache_next = 0; + +void unifont_cache_init() +{ + uf_cache_data = mem2_alloc(16*16*4*UF_CACHE_SIZE); + // seems texture data has to be in mem2 + // if it's in mem1 it doesn't display properly + // strange... +} + +void GRRLIB_InitTexture(GRRLIB_texImg *tx, int w, int h, void *data) +{ + memset(tx, 0, sizeof(GRRLIB_texImg)); + tx->w = w; + tx->h = h; + tx->data = data; + memset(data, 0, w*h*4); +} + +int unifont_to_tx(struct GRRLIB_texImg *tx, int c) +{ + int x, y, xx, i; + u8 *glyph; + int off, len; + u32 color; + if (!unifont) return 0; + if (!unifont->index[c]) return 0; + if (tx->data == NULL) return 0; + off = unifont->index[c] >> 8; + len = unifont->index[c] & 0x0F; + if (len > 2) len = 2; + GRRLIB_InitTileSet(tx, 8*len, 16, 0); + glyph = unifont_glyph + off * 16; + xx = 0; + for (i=0; i>x)) { + color = 0xFFFFFFFF; + } else { + color = 0x00000000; + } + GRRLIB_SetPixelTotexImg(xx+x, y, tx, color); + } + glyph++; + } + xx += 8; + } + GRRLIB_FlushTex(tx); + return len; +} + + +struct GRRLIB_texImg* get_unifont_cache(int c) +{ + int i; + struct GRRLIB_texImg *tx; + if (!uf_cache_data) return NULL; + for (i=0; iindex[c]) return 0; + if (!uf_cache_data) return 0; + tx = get_unifont_cache(c); + len = unifont->index[c] & 0x0F; + if (len > 2) len = 2; + float s = h / 16.0; + //GRRLIB_DrawTile(xpos+2, ypos, tx, 0, s, s, color, 0); + GRRLIB_DrawTile_begin0(tx); + GRRLIB_DrawTile_draw1(xpos+2, ypos, tx, color, 0); + GRRLIB_DrawTile_end0(tx); + // heh, why xpos+2? + // the 512 font chars are right aligned while unifont is left aligned + // and if the two are together they will touch + //GX_DrawDone(); + // dbg: + //extern GRRLIB_texImg tx_font; + //GRRLIB_Printf(50, xpos, tx_font, color, 1, "%d", c); + return s * (float)len * 8.0; +} + +void __GRRLIB_Print1w(f32 xpos, f32 ypos, struct GRRLIB_texImg *tex, + u32 color, const wchar_t *wtext) +{ + unsigned nc = tex->nbtilew * tex->nbtileh; + wchar_t c; + int i; + unsigned cc; + f32 w = tex->tilew; + + for (i = 0; wtext[i]; i++) { + cc = c = wtext[i]; + // break on newline + if (cc == '\r' || cc == '\n') break; + if (cc >= nc) { + cc = map_ufont(c); + if (cc == 0 && (unsigned)c <= 0xFFFF) { + if (unifont && unifont->index[(unsigned)c]) { + // unifont contains almost all unicode chars + GRRLIB_DrawTile_end0(tex); + xpos += draw_unifont(xpos, ypos, tex->tilew, tex->tileh, color, c); + GRRLIB_DrawTile_begin0(tex); + continue; + } + } + c = cc; + } + c -= tex->tilestart; + GRRLIB_DrawTile_draw1(xpos, ypos, tex, color, c); + xpos += w; + } +} + +void GRRLIB_Print4(f32 xpos, f32 ypos, struct GRRLIB_texImg *tex, u32 color, u32 outline, u32 shadow, f32 scale, const char *text, int maxlen) +{ + if (maxlen < 0 ) maxlen = strlen(text); + wchar_t wtext[maxlen+1]; + int wlen; + wlen = mbstowcs(wtext, text, maxlen); + wtext[wlen] = 0; + + GRRLIB_DrawTile_begin(tex, xpos, ypos, scale); + xpos = ypos = 0; // position is set above + if (shadow) { + float x, y; + if (outline) { x = y = 1; } else { x = y = 0; } + __GRRLIB_Print1w(x+1, y+0, tex, shadow, wtext); + __GRRLIB_Print1w(x+0, y+1, tex, shadow, wtext); + __GRRLIB_Print1w(x+1, y+1, tex, shadow, wtext); + __GRRLIB_Print1w(x+2, y+2, tex, shadow, wtext); + } + if (outline) { + /* + // x spread + __GRRLIB_Print1w(xpos-1, ypos-1, tex, outline, wtext); + __GRRLIB_Print1w(xpos+1, ypos-1, tex, outline, wtext); + __GRRLIB_Print1w(xpos-1, ypos+1, tex, outline, wtext); + __GRRLIB_Print1w(xpos+1, ypos+1, tex, outline, wtext); + */ + // + spread + __GRRLIB_Print1w(xpos-1, ypos-0, tex, outline, wtext); + __GRRLIB_Print1w(xpos+1, ypos-0, tex, outline, wtext); + __GRRLIB_Print1w(xpos-0, ypos-1, tex, outline, wtext); + __GRRLIB_Print1w(xpos-0, ypos+1, tex, outline, wtext); + } + __GRRLIB_Print1w(xpos, ypos, tex, color, wtext); + GRRLIB_DrawTile_end(tex); +} + +void GRRLIB_Print3(f32 xpos, f32 ypos, struct GRRLIB_texImg *tex, u32 color, u32 outline, u32 shadow, f32 scale, const char *text) +{ + GRRLIB_Print4(xpos, ypos, tex, color, outline, shadow, scale, text, -1); +} + +void GRRLIB_Print2(f32 xpos, f32 ypos, struct GRRLIB_texImg *tex, u32 color, u32 outline, u32 shadow, const char *text) +{ + GRRLIB_Print3(xpos, ypos, tex, color, outline, shadow, 1.0, text); +} + +void GRRLIB_Print(f32 xpos, f32 ypos, struct GRRLIB_texImg *tex, u32 color, const char *text) +{ + GRRLIB_Print2(xpos, ypos, tex, color, 0, 0, text); +} + +//============================================================== +// Stencil code... +const int _stencilWidth = 128; +const int _stencilHeight = 128; + +void GRRLIB_DrawImg_format(f32 xpos, f32 ypos, GRRLIB_texImg tex, u8 texFormat, float degrees, float scaleX, f32 scaleY, u32 color ) +{ + GXTexObj texObj; + u16 width, height; + Mtx m, m1, m2, mv; + + GX_InitTexObj(&texObj, tex.data, tex.w, tex.h, texFormat, GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_LoadTexObj(&texObj, GX_TEXMAP0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); + + width = tex.w * 0.5; + height = tex.h * 0.5; + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0); + guVector axis = (guVector) {0, 0, 1 }; + guMtxRotAxisDeg (m2, &axis, degrees); + guMtxConcat(m2, m1, m); + + guMtxTransApply(m, m, xpos+width, ypos+height, 0); + guMtxConcat (GXmodelView2D, m, mv); + GX_LoadPosMtxImm (mv, GX_PNMTX0); + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(0, 0); + + GX_Position3f32(width, -height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(1, 0); + + GX_Position3f32(width, height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(1, 1); + + GX_Position3f32(-width, height, 0); + GX_Color1u32(color); + GX_TexCoord2f32(0, 1); + GX_End(); + GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); +} + +static inline u32 coordsI8(u32 x, u32 y, u32 w) +{ + return (((y >> 2) * (w >> 3) + (x >> 3)) << 5) + ((y & 3) << 3) + (x & 7); +} + +int GRRLIB_stencilVal(int x, int y, GRRLIB_texImg tx) +{ + u8 *truc = (u8*)tx.data; + u32 offset; + int col; + + if ((u32)x >= rmode->fbWidth || x < 0 || (u32)y >= rmode->efbHeight || y < 0) + return 255; + x = x * _stencilWidth / 640; + y = y * _stencilHeight / 480; + offset = coordsI8(x, y, (u32)_stencilWidth); + if (offset >= (u32)(_stencilWidth * _stencilHeight)) + return 255; + col = (int)*(truc+offset); + return (col>255?255:col); +} + +void GRRLIB_prepareStencil(void) +{ + GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); + GX_SetViewport(0.f, 0.f, (float)_stencilWidth, (float)_stencilHeight, 0.f, 1.f); + GX_SetScissor(0, 0, _stencilWidth, _stencilHeight); + GX_InvVtxCache(); + GX_InvalidateTexAll(); +} + +void GRRLIB_renderStencil_buf(GRRLIB_texImg *tx) +{ + if (tx == NULL) return; + if (tx->data == NULL) return; + if (tx->w != _stencilWidth) return; + if (tx->h != _stencilHeight) return; + GX_DrawDone(); + GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); + GX_SetTexCopySrc(0, 0, _stencilWidth, _stencilHeight); + GX_SetTexCopyDst(_stencilWidth, _stencilHeight, GX_CTF_R8, GX_FALSE); + GX_CopyTex(tx->data, GX_TRUE); + GX_PixModeSync(); + DCFlushRange(tx->data, _stencilWidth * _stencilHeight); + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); +} +//============================================================== + + +/** + * Make a snapshot of the screen to a pre-allocated texture. Used for AA. + * @return A pointer to a texture representing the screen or NULL if an error occurs. + */ +void GRRLIB_AAScreen2Texture_buf(GRRLIB_texImg *tx, u8 gx_clear) +{ + if (tx == NULL) return; + if (tx->data == NULL) return; + if (tx->w != rmode->fbWidth) return; + if (tx->h != rmode->efbHeight) return; + + GRRLIB_FlushTex(tx); + GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_TRUE); + //GX_DrawDone(); + GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); + GX_SetTexCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetTexCopyDst(rmode->fbWidth, rmode->efbHeight, GX_TF_RGBA8, GX_FALSE); + GX_CopyTex(tx->data, gx_clear); + GX_PixModeSync(); + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + GRRLIB_FlushTex(tx); +} + +/** + * Make a snapshot of the screen in a texture. + * @return A pointer to a texture representing the screen or NULL if an error occurs. + */ +GRRLIB_texImg GRRLIB_AAScreen2Texture() { + GRRLIB_texImg my_texture; + + memset(&my_texture, 0, sizeof(my_texture)); + my_texture.w = 0; + my_texture.h = 0; + //my_texture.data = memalign (32, rmode->fbWidth * rmode->efbHeight * 4); + my_texture.data = mem_alloc(rmode->fbWidth * rmode->efbHeight * 4); + if (my_texture.data == NULL) goto out; + my_texture.w = rmode->fbWidth; + my_texture.h = rmode->efbHeight; + out: + return my_texture; +} + +/** + * Resets all the GX settings to the default + */ +void GRRLIB_ResetVideo() { + f32 yscale; + u32 xfbHeight; + Mtx44 perspective; + + // other gx setup + yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight); + xfbHeight = GX_SetDispCopyYScale(yscale); + GX_SetScissor(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetDispCopyDst(rmode->fbWidth, xfbHeight); + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE)); + + if (rmode->aa) + GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); + else + GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + + GX_SetDispCopyGamma(GX_GM_1_0); + + // setup the vertex descriptor + // tells the flipper to expect direct data + GX_ClearVtxDesc(); + GX_InvVtxCache (); + GX_InvalidateTexAll(); + + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + + + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + GX_SetZMode (GX_FALSE, GX_LEQUAL, GX_TRUE); + + GX_SetNumChans(1); + GX_SetNumTexGens(1); + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + guMtxIdentity(GXmodelView2D); + guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -50.0F); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + guOrtho(perspective,0, 480, 0, 640, 0, 300.0F); + GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC); + + GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetAlphaUpdate(GX_TRUE); + + GX_SetCullMode(GX_CULL_NONE); +} + +//AA jitter values +const float _jitter2[2][2] = { + { 0.246490f, 0.249999f }, + { -0.246490f, -0.249999f } +}; +const float _jitter3[3][2] = { + { -0.373411f, -0.250550f }, + { 0.256263f, 0.368119f }, + { 0.117148f, -0.117570f } +}; + +const float _jitter4wide[4][2] = { + { -0.208147f, 0.353730f }, + { 0.203849f, -0.353780f }, + { -0.292626f, -0.149945f }, + { 0.296924f, 0.149994f } +}; + +const float _jitter4[4][2] = { + { -0.108147f, 0.253730f }, + { 0.103849f, -0.253780f }, + { -0.192626f, -0.049945f }, + { 0.196924f, 0.049994f } +}; + +bool grrlib_wide_aa = true; + +void GRRLIB_prepareAAPass(int aa_cnt, int aaStep) +{ + float x = 0.0f; + float y = 0.0f; + u32 w = rmode->fbWidth; + u32 h = rmode->efbHeight; + switch (aa_cnt) { + case 2: + x += _jitter2[aaStep][0]; + y += _jitter2[aaStep][1]; + break; + case 3: + x += _jitter3[aaStep][0]; + y += _jitter3[aaStep][1]; + break; + case 4: + if (grrlib_wide_aa) { + x += _jitter4wide[aaStep][0]; + y += _jitter4wide[aaStep][1]; + } else { + x += _jitter4[aaStep][0]; + y += _jitter4[aaStep][1]; + } + break; + } + //GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + GX_SetViewport(0+x, 0+y, rmode->fbWidth, rmode->efbHeight, 0, 1); + GX_SetScissor(0, 0, w, h); + GX_InvVtxCache(); + GX_InvalidateTexAll(); +} + +int aa_method = 1; + +void GRRLIB_drawAAScene(int aa_cnt, GRRLIB_texImg *texAABuffer) +{ + if (aa_method) return; + GXTexObj texObj[4]; + Mtx modelViewMtx; + u8 texFmt = GX_TF_RGBA8; + u32 tw = rmode->fbWidth; + u32 th = rmode->efbHeight; + float w = 640.f; + float h = 480.f; + float x = 0.f; + float y = 0.f; + int i = 0; + + GX_SetNumChans(0); + for (i = 0; i < aa_cnt; ++i) { + GX_InitTexObj(&texObj[i], texAABuffer[i].data, tw , th, texFmt, GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_LoadTexObj(&texObj[i], GX_TEXMAP0 + i); + } + + GX_SetNumTexGens(1); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + GX_SetTevKColor(GX_KCOLOR0, (GXColor){0xFF / 1, 0xFF / 5, 0xFF, 0xFF}); // Renders better gradients than 0xFF / aa + GX_SetTevKColor(GX_KCOLOR1, (GXColor){0xFF / 2, 0xFF / 6, 0xFF, 0xFF}); + GX_SetTevKColor(GX_KCOLOR2, (GXColor){0xFF / 3, 0xFF / 7, 0xFF, 0xFF}); + GX_SetTevKColor(GX_KCOLOR3, (GXColor){0xFF / 4, 0xFF / 8, 0xFF, 0xFF}); + for (i = 0; i < aa_cnt; ++i) { + GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_K0_R + i); + GX_SetTevKAlphaSel(GX_TEVSTAGE0 + i, GX_TEV_KASEL_K0_R + i); + GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL); + GX_SetTevColorIn(GX_TEVSTAGE0 + i, i == 0 ? GX_CC_ZERO : GX_CC_CPREV, GX_CC_TEXC, GX_CC_KONST, GX_CC_ZERO); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + i, i == 0 ? GX_CA_ZERO : GX_CA_APREV, GX_CA_TEXA, GX_CA_KONST, GX_CA_ZERO); + GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + } + +/* + GX_SetTevKColor(GX_KCOLOR0, (GXColor){0xFF / aa_cnt, 0, 0, 0}); // Causes a color accuracy loss, in previous and better code i was using the final blender with alpha = 1/1, 1/2, 1/3 etc. + for (i = 0; i < aa_cnt; ++i) { + GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_K0_R); + GX_SetTevKAlphaSel(GX_TEVSTAGE0 + i, GX_TEV_KASEL_K0_R); + GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL); + GX_SetTevColorIn(GX_TEVSTAGE0 + i, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, i == 0 ? GX_CC_ZERO : GX_CC_CPREV); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + i, GX_CA_ZERO, GX_CA_TEXA, GX_CA_KONST, i == 0 ? GX_CA_ZERO : GX_CA_APREV); + GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + } +*/ + + GX_SetNumTevStages(aa_cnt); + // + GX_SetAlphaUpdate(GX_TRUE); + GX_SetCullMode(GX_CULL_NONE); + GX_SetZMode(GX_DISABLE, GX_ALWAYS, GX_FALSE); + GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + // + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + guMtxIdentity(modelViewMtx); + GX_LoadPosMtxImm(modelViewMtx, GX_PNMTX0); + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(x, y, 0.f); + GX_TexCoord2f32(0.f, 0.f); + GX_Position3f32(x + w, y, 0.f); + GX_TexCoord2f32(1.f, 0.f); + GX_Position3f32(x + w, y + h, 0.f); + GX_TexCoord2f32(1.f, 1.f); + GX_Position3f32(x, y + h, 0.f); + GX_TexCoord2f32(0.f, 1.f); + GX_End(); + + GX_SetNumChans(1); + GX_SetNumTexGens(1); + GX_SetNumTevStages(1); +} + +/* + // aa using viewport -- doesn't work + int w = rmode->fbWidth; + int h = rmode->efbHeight; + int x, y, w2, h2; + w2 = w * 2; + h2 = h * 2; + switch (aaStep) { + default: + case 0: x = 0; y = 0; break; + case 1: x = w; y = 0; break; + case 2: x = 0; y = h; break; + case 3: x = w; y = h; break; + } + GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + GX_SetViewport(-x, -y, w2, h2, 0, 1); + GX_SetScissor(0, 0, w, h); + GX_SetScissorBoxOffset(0, 0); + GX_InvVtxCache(); + GX_InvalidateTexAll(); +*/ + +void GRRLIB_DrawImgRect (float x, float y, float w, float h, GRRLIB_texImg *tex, const u32 color) +{ + guVector p[4]; + p[0].x = x; + p[0].y = y; + p[1].x = x + w; + p[1].y = y; + p[2].x = x + w; + p[2].y = y + h; + p[3].x = x; + p[3].y = y + h; + GRRLIB_DrawImgQuad(p, tex, color); +} + +void GRRLIB_DrawImgNoAA(const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, const u32 color) +{ + int aa = GRRLIB_Settings.antialias; + GRRLIB_Settings.antialias = false; + GRRLIB_DrawImg(xpos, ypos, tex, degrees, scaleX, scaleY, color); + GRRLIB_Settings.antialias = aa; +} + +void tx_store(struct GRRLIB_texImg *dest, struct GRRLIB_texImg *src) +{ + if (src) { + memcpy(dest, src, sizeof(struct GRRLIB_texImg)); + SAFE_FREE(src); + } else { + memset(dest, 0, sizeof(struct GRRLIB_texImg)); + } +} + +/** + * Draw a part of a texture. + * @param pos Vector array of the 4 points. + * @param partx Specifies the x-coordinate of the upper-left corner in the texture. + * @param party Specifies the y-coordinate of the upper-left corner in the texture. + * @param partw Specifies the width in the texture. + * @param parth Specifies the height in the texture. + * @param tex The texture containing the tile to draw. + * @param color Color in RGBA format. + */ +void GRRLIB_DrawPartQuad (const guVector pos[4], const GRRLIB_texImg *tex, + const f32 partx, const f32 party, const f32 partw, const f32 parth, + const u32 color) +{ + GXTexObj texObj; + Mtx m1, mv; + f32 s1, s2, t1, t2; + + if (tex == NULL || tex->data == NULL) return; + + // The 0.001f/x is the frame correction formula by spiffen + s1 = (partx /tex->w) +(0.001f /tex->w); + s2 = ((partx + partw)/tex->w) -(0.001f /tex->w); + t1 = (party /tex->h) +(0.001f /tex->h); + t2 = ((party + parth)/tex->h) -(0.001f /tex->h); + + GX_InitTexObj(&texObj, tex->data, + tex->w, tex->h, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + guMtxIdentity (m1); + guMtxConcat(GXmodelView2D, m1, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(pos[0].x, pos[0].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(pos[1].x, pos[1].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(pos[2].x, pos[2].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(pos[3].x, pos[3].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + + +/** + * Draw a tile range in a quad. + * @param pos Vector array of the 4 points. + * @param tex The texture to draw. + * @param color Color in RGBA format. + * @param frame Specifies the frame to draw. + * @param nx num frames on x + * @param ny num frames on y + */ +void GRRLIB_DrawTileRectQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color, const int frame, int nx, int ny) +{ + GXTexObj texObj; + Mtx m, m1, m2, mv; + f32 s1, s2, t1, t2; + + if (tex == NULL || tex->data == NULL) return; + + // The 0.001f/x is the frame correction formula by spiffen + // x + s1 = (( (frame %tex->nbtilew) ) /(f32)tex->nbtilew) +(0.001f /tex->w); + s2 = (( (frame %tex->nbtilew) +nx) /(f32)tex->nbtilew) -(0.001f /tex->w); + // y + t1 = (((int)(frame /tex->nbtilew) ) /(f32)tex->nbtileh) +(0.001f /tex->h); + t2 = (((int)(frame /tex->nbtilew) +ny) /(f32)tex->nbtileh) -(0.001f /tex->h); + + GX_InitTexObj(&texObj, tex->data, + tex->tilew * tex->nbtilew, tex->tileh * tex->nbtileh, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, 1, 1, 1.0f); + guVector axis = (guVector) {0, 0, 1 }; + guMtxRotAxisDeg(m2, &axis, 0); + guMtxConcat (m2, m1, m); + guMtxConcat (GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(pos[0].x, pos[0].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(pos[1].x, pos[1].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(pos[2].x, pos[2].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(pos[3].x, pos[3].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +u32 fixGX_GetTexBufferSize(u16 wd, u16 ht, u32 fmt, u8 mipmap, u8 maxlod) +{ + if (mipmap) return GX_GetTexBufferSize(wd, ht, fmt, mipmap, maxlod + 1); + return GX_GetTexBufferSize(wd, ht, fmt, mipmap, maxlod); +} + +int GRRLIB_DataSize(uint w, uint h, int fmt, int lod) +{ + if (!fmt) fmt = GX_TF_RGBA8; + return fixGX_GetTexBufferSize(w, h, fmt, lod > 0, lod); +} + +int GRRLIB_TextureSize(GRRLIB_texImg *tx) +{ + return GRRLIB_DataSize(tx->w, tx->h, tx->tex_format, tx->tex_lod); +} + +bool GRRLIB_AllocTextureDataX(GRRLIB_texImg *tx, int w, int h, int fmt, int lod) +{ + if (!tx) return false; + if (!fmt) fmt = GX_TF_RGBA8; + int size = GRRLIB_DataSize(w, h, fmt, lod); + memset(tx, 0, sizeof(GRRLIB_texImg)); + tx->data = mem_alloc(size); + if (!tx->data) return false; + memset(tx->data, 0, size); + tx->w = w; + tx->h = h; + tx->tex_format = fmt; + tx->tex_lod = lod; + GRRLIB_SetHandle(tx, 0, 0); + GRRLIB_FlushTex(tx); + return true; +} + +bool GRRLIB_AllocTextureData(GRRLIB_texImg *tx, const uint w, const uint h) +{ + return GRRLIB_AllocTextureDataX(tx, w, h, 0, 0); +} + +bool GRRLIB_ReallocTextureData(GRRLIB_texImg *tx, const uint w, const uint h) +{ + GRRLIB_FreeTextureData(tx); + return GRRLIB_AllocTextureDataX(tx, w, h, 0, 0); +} + +void GRRLIB_FreeData(GRRLIB_texImg *tex) +{ + if (tex) SAFE_FREE(tex->data); +} + +void GRRLIB_FreeTextureData(GRRLIB_texImg *tex) +{ + if (tex) { + SAFE_FREE(tex->data); + memset(tex, 0, sizeof(GRRLIB_texImg)); + } +} + +bool GRRLIB_CloneTexture(GRRLIB_texImg *dest, GRRLIB_texImg *src) +{ + dest->data = NULL; + int size = GRRLIB_TextureSize(src); + bool ret = GRRLIB_AllocTextureDataX(dest, + src->w, src->h, src->tex_format, src->tex_lod); + if (!ret) return false; + void *data = dest->data; + *dest = *src; + dest->data = data; + memcpy(dest->data, src->data, size); + return true; +} + +// if src is in mem1/2 then use src +// else copy src to mem1/2 and free src +bool GRRLIB_TextureMEM2(GRRLIB_texImg *dest, GRRLIB_texImg *src) +{ + if (!src->data) return false; + GRRLIB_FreeTextureData(dest); + if (mem_inside(3, src->data)) { + *dest = *src; + src->data = NULL; + } else { + GRRLIB_CloneTexture(dest, src); + GRRLIB_FreeTextureData(src); + } + return dest->data != NULL; +} + +bool GRRLIB_TextureToMEM2(GRRLIB_texImg *tx) +{ + if (!tx || !tx->data) return false; + if (mem_inside(3, tx->data)) return true; + GRRLIB_texImg tmp; + GRRLIB_CloneTexture(&tmp, tx); + GRRLIB_FreeData(tx); + tx->data = tmp.data; + GRRLIB_FlushTex(tx); + return tx->data != NULL; +} + +// override GRRLIB_texSetup.h + +/** + * Create an empty texture. + * @param w Width of the new texture to create. + * @param h Height of the new texture to create. + * @return A GRRLIB_texImg structure newly created. + */ + +GRRLIB_texImg* GRRLIB_CreateEmptyTexture(const uint w, const uint h) +{ + GRRLIB_texImg *my_texture = (struct GRRLIB_texImg *)calloc(1, sizeof(GRRLIB_texImg)); + if (my_texture) { + if (!GRRLIB_AllocTextureData(my_texture, w, h)) { + SAFE_FREE(my_texture); + return NULL; + } + } + return my_texture; +} + +/** + * Write the contents of a texture in the data cache down to main memory. + * For performance the CPU holds a data cache where modifications are stored before they get written down to main memory. + * @param tex The texture to flush. + */ +void GRRLIB_FlushTex (GRRLIB_texImg *tex) +{ + int size = GRRLIB_TextureSize(tex); + if (tex->data) DCFlushRange(tex->data, size); +} + +/** + * Free memory allocated for texture. + * @param tex A GRRLIB_texImg structure. + */ +void GRRLIB_FreeTexture(GRRLIB_texImg *tex) +{ + if (tex != NULL) { + SAFE_FREE(tex->data); + SAFE_FREE(tex); + } +} + +/** + * Clear a texture to transparent black. + * @param tex Texture to clear. + */ +void GRRLIB_ClearTex(GRRLIB_texImg* tex) { + int size = GRRLIB_TextureSize(tex); + bzero(tex->data, size); + GRRLIB_FlushTex(tex); +} + diff --git a/source/Makefile b/source/Makefile new file mode 100644 index 0000000..f7c71d6 --- /dev/null +++ b/source/Makefile @@ -0,0 +1,7 @@ + +default: + $(MAKE) -C .. + +clean: + $(MAKE) -C .. clean + diff --git a/source/NintendontConfig.h b/source/NintendontConfig.h new file mode 100644 index 0000000..5ba6b9b --- /dev/null +++ b/source/NintendontConfig.h @@ -0,0 +1,85 @@ + +#ifndef __NINTENDONT_CONFIG_H__ +#define __NINTENDONT_CONFIG_H__ + +//#include "NintendontVersion.h" +//#include "Metadata.h" + +#define NIN_CFG_VERSION 0x00000002 + +#define NIN_CFG_MAXPAD 4 + +typedef struct NIN_CFG +{ + unsigned int Magicbytes; // 0x01070CF6 + unsigned int Version; // 0x00000001 + unsigned int Config; + unsigned int VideoMode; + unsigned int Language; + char GamePath[255]; + char CheatPath[255]; + unsigned int MaxPads; + unsigned int GameID; +} NIN_CFG; + +enum ninconfig +{ + NIN_CFG_CHEATS = (1<<0), + NIN_CFG_DEBUGGER = (1<<1), // Only for Wii Version + NIN_CFG_DEBUGWAIT = (1<<2), // Only for Wii Version + NIN_CFG_MEMCARDEMU = (1<<3), + NIN_CFG_CHEAT_PATH = (1<<4), + NIN_CFG_FORCE_WIDE = (1<<5), + NIN_CFG_FORCE_PROG = (1<<6), + NIN_CFG_AUTO_BOOT = (1<<7), + NIN_CFG_HID = (1<<8), + NIN_CFG_OSREPORT = (1<<9), + NIN_CFG_USB = (1<<10), + NIN_CFG_LED = (1<<11), +}; + +enum ninvideomode +{ + NIN_VID_AUTO = (0<<16), + NIN_VID_FORCE = (1<<16), + NIN_VID_NONE = (2<<16), + + NIN_VID_MASK = NIN_VID_AUTO|NIN_VID_FORCE|NIN_VID_NONE, + + NIN_VID_FORCE_PAL50 = (1<<0), + NIN_VID_FORCE_PAL60 = (1<<1), + NIN_VID_FORCE_NTSC = (1<<2), + NIN_VID_FORCE_MPAL = (1<<3), + + NIN_VID_FORCE_MASK = NIN_VID_FORCE_PAL50|NIN_VID_FORCE_PAL60|NIN_VID_FORCE_NTSC|NIN_VID_FORCE_MPAL, + + NIN_VID_PROG = (1<<4), //important to prevent blackscreens +}; + +enum ninlanguage +{ + NIN_LAN_ENGLISH = 0, + NIN_LAN_GERMAN = 1, + NIN_LAN_FRENCH = 2, + NIN_LAN_SPANISH = 3, + NIN_LAN_ITALIAN = 4, + NIN_LAN_DUTCH = 5, + +/* Auto will use English for E/P region codes and + only other languages when these region codes are used: D/F/S/I/J */ + + NIN_LAN_AUTO = -1, +}; + +enum VideoModes +{ + GCVideoModeNone = 0, + GCVideoModePAL60 = 1, + GCVideoModeNTSC = 2, + GCVideoModePROG = 3, +}; + +#define NIN_RAW_MEMCARD_SIZE 2*1024*1024 //2MB +#define NIN_MEMCARD_BLOCKS 0x00000010 //251 Blocks + +#endif diff --git a/source/RuntimeIOSPatch.c b/source/RuntimeIOSPatch.c new file mode 100644 index 0000000..e1a1ab1 --- /dev/null +++ b/source/RuntimeIOSPatch.c @@ -0,0 +1,86 @@ +// Copyright 2010 Joseph Jordan +// This code is licensed to you under the terms of the GNU GPL, version 2; +// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +#include +#include +#include + +#include "RuntimeIOSPatch.h" + +#define HAVE_AHBPROT ((*(vu32*)0xcd800064 == 0xFFFFFFFF) ? 1 : 0) +#define MEM_REG_BASE 0xd8b4000 +#define MEM_PROT (MEM_REG_BASE + 0x20a) + +static void disable_memory_protection() { + write32(MEM_PROT, read32(MEM_PROT) & 0x0000FFFF); +} + +static u32 apply_patch(char *name, const u8 *old, u32 old_size, const u8 *patch, u32 patch_size, u32 patch_offset) { + u8 *ptr_start = (u8*)*((u32*)0x80003134), *ptr_end = (u8*)0x94000000; + u32 found = 0; + u8 *location = NULL; + while (ptr_start < (ptr_end - patch_size)) { + if (!memcmp(ptr_start, old, old_size)) { + found++; + location = ptr_start + patch_offset; + u8 *start = location; + u32 i; + for (i = 0; i < patch_size; i++) { + *location++ = patch[i]; + } + DCFlushRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64); + ICInvalidateRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64); + } + ptr_start++; + } + return found; +} + +static const u8 di_readlimit_old[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 +}; +static const u8 di_readlimit_patch[] = { 0x7e, 0xd4 }; + +const u8 isfs_permissions_old[] = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 }; +const u8 isfs_permissions_patch[] = { 0x42, 0x8B, 0xE0, 0x01, 0x25, 0x66 }; +static const u8 setuid_old[] = { 0xD1, 0x2A, 0x1C, 0x39 }; +static const u8 setuid_patch[] = { 0x46, 0xC0 }; +const u8 es_identify_old[] = { 0x28, 0x03, 0xD1, 0x23 }; +const u8 es_identify_patch[] = { 0x00, 0x00 }; +const u8 hash_old[] = { 0x20, 0x07, 0x23, 0xA2 }; +const u8 hash_patch[] = { 0x00 }; +const u8 new_hash_old[] = { 0x20, 0x07, 0x4B, 0x0B }; +const u8 addticket_vers_check[] = { 0xD2, 0x01, 0x4E, 0x56 }; +const u8 addticket_patch[] = { 0xE0 }; +const u8 es_set_ahbprot_pattern[] = { 0x68, 0x5B, 0x22, 0xEC, 0x00, 0x52, 0x18, 0x9B, 0x68, 0x1B, 0x46, 0x98, 0x07, 0xDB }; +const u8 es_set_ahbprot_patch[] = { 0x01 }; + +u32 IOSPATCH_Apply() { + u32 count = 0; + s32 ret = 0; + + if (HAVE_AHBPROT) { + disable_memory_protection(); + ret = apply_patch("es_set_ahbprot", es_set_ahbprot_pattern, sizeof(es_set_ahbprot_pattern), es_set_ahbprot_patch, sizeof(es_set_ahbprot_patch), 25); + } + //if (ret) { + // IOS_ReloadIOS(IOS_GetVersion()); + //} else { + // return 0; + //} + + //if (HAVE_AHBPROT) { + // disable_memory_protection(); + //count += apply_patch("di_readlimit", di_readlimit_old, sizeof(di_readlimit_old), di_readlimit_patch, sizeof(di_readlimit_patch), 12); + //count += apply_patch("isfs_permissions", isfs_permissions_old, sizeof(isfs_permissions_old), isfs_permissions_patch, sizeof(isfs_permissions_patch), 0); + //count += apply_patch("es_setuid", setuid_old, sizeof(setuid_old), setuid_patch, sizeof(setuid_patch), 0); + //count += apply_patch("es_identify", es_identify_old, sizeof(es_identify_old), es_identify_patch, sizeof(es_identify_patch), 2); + //count += apply_patch("hash_check", hash_old, sizeof(hash_old), hash_patch, sizeof(hash_patch), 1); + //count += apply_patch("new_hash_check", new_hash_old, sizeof(new_hash_old), hash_patch, sizeof(hash_patch), 1); + //count += apply_patch("add ticket patch", addticket_vers_check, sizeof(addticket_vers_check), addticket_patch, sizeof(addticket_patch), 0); + //} + return count; +} diff --git a/source/RuntimeIOSPatch.h b/source/RuntimeIOSPatch.h new file mode 100644 index 0000000..23a671b --- /dev/null +++ b/source/RuntimeIOSPatch.h @@ -0,0 +1,11 @@ +// Copyright 2010 Joseph Jordan +// This code is licensed to you under the terms of the GNU GPL, version 2; +// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +#ifndef _IOSPATCH_H +#define _IOSPATCH_H + +#include + +u32 IOSPATCH_Apply(); + +#endif /* _IOSPATCH_H */ diff --git a/source/apploader.c b/source/apploader.c new file mode 100644 index 0000000..0f307da --- /dev/null +++ b/source/apploader.c @@ -0,0 +1,1290 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "apploader.h" +#include "wdvd.h" +#include "video.h" +#include "wpad.h" +#include "cfg.h" +#include "patchcode.h" /*FISHEARS*/ +#include "dol.h" +#include "wiip.h" +#include "gettext.h" +#include "menu.h" +#include "dolmenu.h" + +/* Apploader function pointers */ +typedef int (*app_main)(void **dst, int *size, int *offset); +typedef void (*app_init)(void (*report)(const char *fmt, ...)); +typedef void *(*app_final)(); +typedef void (*app_entry)(void (**init)(void (*report)(const char *fmt, ...)), int (**main)(), void *(**final)()); + +/* Apploader pointers */ +static u8 *appldr = (u8 *)0x81200000; + + +/* Constants */ +#define APPLDR_OFFSET 0x2440 + +/* Variables */ +static u32 buffer[0x20] ATTRIBUTE_ALIGN(32); + +/* Forward Declarations */ +void PatchCountryStrings(void *Address, int Size); +void maindolpatches(void *dst, int len); +bool cfg_PatchReturnTo(void *Address, int Size); +u32 Load_Dol_from_sd(); +u32 Load_Dol_from_disc_menu(); +u32 Load_Dol_from_disc(u32 doloffset); +void getdolnames(); +u32 getdoloffsetbyname(char *name); +bool Remove_001_Protection(void *Address, int Size); +void Anti_002_fix(void *Address, int Size); +//u32 load_dol_image (void *dolstart, bool clear_bss); + +u32 dolcount = 0; +u32 dolindex[10]; + +static void __noprint(const char *fmt, ...) +{ +} + +bool compare_videomodes(GXRModeObj* mode1, GXRModeObj* mode2) +{ + if (mode1->viTVMode != mode2->viTVMode || mode1->fbWidth != mode2->fbWidth || mode1->efbHeight != mode2->efbHeight || mode1->xfbHeight != mode2->xfbHeight || + mode1->viXOrigin != mode2->viXOrigin || mode1->viYOrigin != mode2->viYOrigin || mode1->viWidth != mode2->viWidth || mode1->viHeight != mode2->viHeight || + mode1->xfbMode != mode2->xfbMode || mode1->field_rendering != mode2->field_rendering || mode1->aa != mode2->aa || mode1->sample_pattern[0][0] != mode2->sample_pattern[0][0] || + mode1->sample_pattern[1][0] != mode2->sample_pattern[1][0] || mode1->sample_pattern[2][0] != mode2->sample_pattern[2][0] || + mode1->sample_pattern[3][0] != mode2->sample_pattern[3][0] || mode1->sample_pattern[4][0] != mode2->sample_pattern[4][0] || + mode1->sample_pattern[5][0] != mode2->sample_pattern[5][0] || mode1->sample_pattern[6][0] != mode2->sample_pattern[6][0] || + mode1->sample_pattern[7][0] != mode2->sample_pattern[7][0] || mode1->sample_pattern[8][0] != mode2->sample_pattern[8][0] || + mode1->sample_pattern[9][0] != mode2->sample_pattern[9][0] || mode1->sample_pattern[10][0] != mode2->sample_pattern[10][0] || + mode1->sample_pattern[11][0] != mode2->sample_pattern[11][0] || mode1->sample_pattern[0][1] != mode2->sample_pattern[0][1] || + mode1->sample_pattern[1][1] != mode2->sample_pattern[1][1] || mode1->sample_pattern[2][1] != mode2->sample_pattern[2][1] || + mode1->sample_pattern[3][1] != mode2->sample_pattern[3][1] || mode1->sample_pattern[4][1] != mode2->sample_pattern[4][1] || + mode1->sample_pattern[5][1] != mode2->sample_pattern[5][1] || mode1->sample_pattern[6][1] != mode2->sample_pattern[6][1] || + mode1->sample_pattern[7][1] != mode2->sample_pattern[7][1] || mode1->sample_pattern[8][1] != mode2->sample_pattern[8][1] || + mode1->sample_pattern[9][1] != mode2->sample_pattern[9][1] || mode1->sample_pattern[10][1] != mode2->sample_pattern[10][1] || + mode1->sample_pattern[11][1] != mode2->sample_pattern[11][1] || mode1->vfilter[0] != mode2->vfilter[0] || + mode1->vfilter[1] != mode2->vfilter[1] || mode1->vfilter[2] != mode2->vfilter[2] || mode1->vfilter[3] != mode2->vfilter[3] || mode1->vfilter[4] != mode2->vfilter[4] || + mode1->vfilter[5] != mode2->vfilter[5] || mode1->vfilter[6] != mode2->vfilter[6] ) + { + return false; + } else + { + return true; + } +} + + +void patch_videomode(GXRModeObj* mode1, GXRModeObj* mode2) +{ + mode1->viTVMode = mode2->viTVMode; + mode1->fbWidth = mode2->fbWidth; + mode1->efbHeight = mode2->efbHeight; + mode1->xfbHeight = mode2->xfbHeight; + mode1->viXOrigin = mode2->viXOrigin; + mode1->viYOrigin = mode2->viYOrigin; + mode1->viWidth = mode2->viWidth; + mode1->viHeight = mode2->viHeight; + mode1->xfbMode = mode2->xfbMode; + mode1->field_rendering = mode2->field_rendering; + mode1->aa = mode2->aa; + mode1->sample_pattern[0][0] = mode2->sample_pattern[0][0]; + mode1->sample_pattern[1][0] = mode2->sample_pattern[1][0]; + mode1->sample_pattern[2][0] = mode2->sample_pattern[2][0]; + mode1->sample_pattern[3][0] = mode2->sample_pattern[3][0]; + mode1->sample_pattern[4][0] = mode2->sample_pattern[4][0]; + mode1->sample_pattern[5][0] = mode2->sample_pattern[5][0]; + mode1->sample_pattern[6][0] = mode2->sample_pattern[6][0]; + mode1->sample_pattern[7][0] = mode2->sample_pattern[7][0]; + mode1->sample_pattern[8][0] = mode2->sample_pattern[8][0]; + mode1->sample_pattern[9][0] = mode2->sample_pattern[9][0]; + mode1->sample_pattern[10][0] = mode2->sample_pattern[10][0]; + mode1->sample_pattern[11][0] = mode2->sample_pattern[11][0]; + mode1->sample_pattern[0][1] = mode2->sample_pattern[0][1]; + mode1->sample_pattern[1][1] = mode2->sample_pattern[1][1]; + mode1->sample_pattern[2][1] = mode2->sample_pattern[2][1]; + mode1->sample_pattern[3][1] = mode2->sample_pattern[3][1]; + mode1->sample_pattern[4][1] = mode2->sample_pattern[4][1]; + mode1->sample_pattern[5][1] = mode2->sample_pattern[5][1]; + mode1->sample_pattern[6][1] = mode2->sample_pattern[6][1]; + mode1->sample_pattern[7][1] = mode2->sample_pattern[7][1]; + mode1->sample_pattern[8][1] = mode2->sample_pattern[8][1]; + mode1->sample_pattern[9][1] = mode2->sample_pattern[9][1]; + mode1->sample_pattern[10][1] = mode2->sample_pattern[10][1]; + mode1->sample_pattern[11][1] = mode2->sample_pattern[11][1]; + mode1->vfilter[0] = mode2->vfilter[0]; + mode1->vfilter[1] = mode2->vfilter[1]; + mode1->vfilter[2] = mode2->vfilter[2]; + mode1->vfilter[3] = mode2->vfilter[3]; + mode1->vfilter[4] = mode2->vfilter[4]; + mode1->vfilter[5] = mode2->vfilter[5]; + mode1->vfilter[6] = mode2->vfilter[6]; +} + +GXRModeObj* vmodes[] = { + &TVNtsc240Ds, + &TVNtsc240DsAa, + &TVNtsc240Int, + &TVNtsc240IntAa, + &TVNtsc480IntDf, + &TVNtsc480IntAa, + &TVNtsc480Prog, + &TVMpal480IntDf, + &TVPal264Ds, + &TVPal264DsAa, + &TVPal264Int, + &TVPal264IntAa, + &TVPal524IntAa, + &TVPal528Int, + &TVPal528IntDf, + &TVPal574IntDfScale, + &TVEurgb60Hz240Ds, + &TVEurgb60Hz240DsAa, + &TVEurgb60Hz240Int, + &TVEurgb60Hz240IntAa, + &TVEurgb60Hz480Int, + &TVEurgb60Hz480IntDf, + &TVEurgb60Hz480IntAa, + &TVEurgb60Hz480Prog, + &TVEurgb60Hz480ProgSoft, + &TVEurgb60Hz480ProgAa +}; + +GXRModeObj* PAL2NTSC[]={ + &TVMpal480IntDf, &TVNtsc480IntDf, + &TVPal264Ds, &TVNtsc240Ds, + &TVPal264DsAa, &TVNtsc240DsAa, + &TVPal264Int, &TVNtsc240Int, + &TVPal264IntAa, &TVNtsc240IntAa, + &TVPal524IntAa, &TVNtsc480IntAa, + &TVPal528Int, &TVNtsc480IntAa, + &TVPal528IntDf, &TVNtsc480IntDf, + &TVPal574IntDfScale, &TVNtsc480IntDf, + &TVEurgb60Hz240Ds, &TVNtsc240Ds, + &TVEurgb60Hz240DsAa, &TVNtsc240DsAa, + &TVEurgb60Hz240Int, &TVNtsc240Int, + &TVEurgb60Hz240IntAa, &TVNtsc240IntAa, + &TVEurgb60Hz480Int, &TVNtsc480IntAa, + &TVEurgb60Hz480IntDf, &TVNtsc480IntDf, + &TVEurgb60Hz480IntAa, &TVNtsc480IntAa, + &TVEurgb60Hz480Prog, &TVNtsc480Prog, + &TVEurgb60Hz480ProgSoft,&TVNtsc480Prog, + &TVEurgb60Hz480ProgAa, &TVNtsc480Prog, + 0,0 +}; + +GXRModeObj* NTSC2PAL[]={ + &TVNtsc240Ds, &TVPal264Ds, + &TVNtsc240DsAa, &TVPal264DsAa, + &TVNtsc240Int, &TVPal264Int, + &TVNtsc240IntAa, &TVPal264IntAa, + &TVNtsc480IntDf, &TVPal528IntDf, + &TVNtsc480IntAa, &TVPal524IntAa, + &TVNtsc480Prog, &TVPal528IntDf, + 0,0 +}; + +GXRModeObj* NTSC2PAL60[]={ + &TVNtsc240Ds, &TVEurgb60Hz240Ds, + &TVNtsc240DsAa, &TVEurgb60Hz240DsAa, + &TVNtsc240Int, &TVEurgb60Hz240Int, + &TVNtsc240IntAa, &TVEurgb60Hz240IntAa, + &TVNtsc480IntDf, &TVEurgb60Hz480IntDf, + &TVNtsc480IntAa, &TVEurgb60Hz480IntAa, + &TVNtsc480Prog, &TVEurgb60Hz480Prog, + 0,0 +}; + +bool Search_and_patch_Video_Modes(void *Address, u32 Size, GXRModeObj* Table[]) +{ + u8 *Addr = (u8 *)Address; + bool found = 0; + u32 i; + + while(Size >= sizeof(GXRModeObj)) + { + for(i = 0; Table[i]; i+=2) + { + if(compare_videomodes(Table[i], (GXRModeObj*)Addr)) + { + found = 1; + patch_videomode((GXRModeObj*)Addr, Table[i+1]); + Addr += (sizeof(GXRModeObj)-4); + Size -= (sizeof(GXRModeObj)-4); + break; + } + } + + Addr += 4; + Size -= 4; + } + + return found; +} + +bool Search_and_patch_Video_To(void *Address, u32 Size, + GXRModeObj* Table[], GXRModeObj* vmode) +{ + u8 *Addr = (u8 *)Address; + bool found = 0; + u32 i; + + while(Size >= sizeof(GXRModeObj)) + { + for(i = 0; Table[i]; i++) + { + if(compare_videomodes(Table[i], (GXRModeObj*)Addr)) + { + found = 1; + patch_videomode((GXRModeObj*)Addr, vmode); + Addr += (sizeof(GXRModeObj)-4); + Size -= (sizeof(GXRModeObj)-4); + break; + } + } + + Addr += 4; + Size -= 4; + } + + return found; +} + +s32 Apploader_Run(entry_point *entry) +{ + app_entry appldr_entry; + app_init appldr_init; + app_main appldr_main; + app_final appldr_final; + + u32 appldr_len; + s32 ret; + + void* dst_array[64]; + int len_array[64]; + int last_index = -1; + int fststart = 0; + + wipreset(); + + get_time(&TIME.load1); + /* Read apploader header */ + ret = WDVD_Read(buffer, 0x20, APPLDR_OFFSET); + if (ret < 0) + return ret; + TIME.size += 0x20; + + /* Calculate apploader length */ + appldr_len = buffer[5] + buffer[6]; + + /* Read apploader code */ + ret = WDVD_Read(appldr, appldr_len, APPLDR_OFFSET + 0x20); + if (ret < 0) + return ret; + TIME.size += appldr_len; + + // used mem range by the loader + //void *mem_start = (void*)0x80b00000; // as set in Makefile + void *mem_start = (void*)0x80a80000; // as set in Makefile + void *mem_end = memalign(32,32); + //printf("malloc = %p sta = %p\n", mem, &ret); + + /* Set apploader entry function */ + appldr_entry = (app_entry)buffer[4]; + + /* Call apploader entry */ + appldr_entry(&appldr_init, &appldr_main, &appldr_final); + + /* Initialize apploader */ + appldr_init(__noprint); + + // dolStart, dolEnd added for giantpune's return-to patch by Dr. Clipper + u32 dolStart = 0x90000000; + u32 dolEnd = 0x0; + + //if (CFG.ios_yal) + printf("."); + for (;;) { + void *dst = NULL; + s32 len = 0, offset = 0; + + /* Run apploader main function */ + ret = appldr_main(&dst, &len, &offset); + if (!ret) + break; + + // check for overlap + //printf("%p [%4x] %p\n", dst, len, dst+len); + if ( ((dst > mem_start) && (dst < mem_end)) + || ((dst+len > mem_start) && (dst+len < mem_end)) ) + { + printf(gt("ERROR: memory overlap!")); + printf("\n"); + printf(gt("dest: %p - %p"), dst, dst+len); + printf("\n"); + printf(gt("used: %p - %p"), mem_start, mem_end); + printf("\n"); + sleep(2); + printf(gt("Press %s button to exit."), (button_names[CFG.button_exit.num])); + printf("\n"); + Menu_PrintWait(); + } + + /* Read data from DVD */ + WDVD_Read(dst, len, (u64)(offset << 2)); + TIME.size += len; + + //if (CFG.ios_yal) + printf("."); + + if (CFG.delay_patch) { + // From NeoGamma: delay patches after load complete + last_index++; + dst_array[last_index] = dst; + len_array[last_index] = len; + } else { + maindolpatches(dst, len); + } + + // dolStart, dolEnd added for giantpune's return-to patch by Dr. Clipper + if( (u32)dst < dolStart )dolStart = (u32)dst; + if( (u32)dst + len > dolEnd ) dolEnd = (u32)dst + len; + DCFlushRange(dst, len); + } + get_time(&TIME.load2); + + int j = 0; + if (CFG.delay_patch) { + dst_array[last_index+1] = (void *)0x81800000; + + fststart = 0; + while ( j <= last_index && (u32)dst_array[last_index-j] + len_array[last_index-j] == (u32)dst_array[last_index-j+1]) + { + fststart = last_index - j; + j++; + } + if (fststart == 0) + { + for (j = 4; j <= last_index; j++) + { + if ((u32)dst_array[j] == *(u32 *)0x80000038) + { + fststart = j; + } + } + if (fststart == 0) + { + fststart = last_index; + } + } + } + + + + + /* Set entry point from apploader */ + *entry = appldr_final(); + //if (CFG.ios_yal) + printf("\n\n"); + + if (CFG.delay_patch) { + // delayed patching (NeoGamma) + for (j=3;j= ALT_DOL_DISC) + { + u32 doloffset = 0; + getdolnames(); + if (CFG.game.alt_dol >= ALT_DOL_PLUS) + { + int dolm_i = CFG.game.alt_dol - ALT_DOL_PLUS + 1; + if (dolm_i <= dolmenubuffer[0].count) { + STRCOPY(CFG.game.dol_name, dolmenubuffer[dolm_i].dolname); + dolparameter = dolmenubuffer[dolm_i].parameter; + printf_x("alt.dol+: %s\n", dolmenubuffer[dolm_i].name); + doloffset = getdoloffsetbyname(CFG.game.dol_name); + printf_(">> %s (%d)\n", CFG.game.dol_name, dolparameter); + printf_(""); + if (doloffset == 0) sleep(1); + } + } else { + doloffset = Load_Dol_from_disc_menu(); + if (doloffset == 0) + { + printf_x(gt("Alternative .dol:")); + printf("\n"); + printf_(gt("None found on disc")); + printf("\n"); + sleep(2); + } + } + if (doloffset) + { + *entry = (void*)Load_Dol_from_disc(doloffset); + if (*entry == NULL) return -1; + printf_(gt("Load OK!")); + printf("\n"); + } + } else if (CFG.game.alt_dol == ALT_DOL_SD) + { + u32 new_entry; + printf_x(gt("Alternative .dol:")); + printf("\n"); + new_entry = Load_Dol_from_sd(); + if (new_entry == 0) { + // non-fatal error + printf_(gt("Press any button...")); + printf("\n"); + Wpad_WaitButtons(); + // continue without alt.dol + } else if (new_entry == (u32)-1) { + // fatal error + return -1; + } else { + // ok. + *entry = (void*)new_entry; + printf_(gt("Load OK!")); + printf("\n"); + } + } + __console_flush(0); + usleep(500000); + if (CFG.debug) { + printf_(gt("Press any button...")); + printf("\n"); + Wpad_WaitButtons(); + } + } + // printf("%p\n", *entry); Wpad_WaitButtons(); // exit(0); + + // cios 249 rev13 - 002 fix (by WiiPower) + *(u32 *)0x80003140 = *(u32 *)0x80003188; + // *(u32 *)0x80003188 = *(u32 *)0x80003140; + /* + u8 ios = CFG.ios; + if (*(u8 *)0x80003189 == ios) + { + *(u32 *)0x80003140 = *(u32 *)0x80003188; + } else + { + if (ios == IOS_GetVersion()) + { + *(u32 *)0x80003188 = *(u32 *)0x80003140; + } else + { + *(u8 *)0x80003141 = ios; + *(u8 *)0x80003189 = ios; + *(u16 *)0x80003142 = 0xffff; + *(u16 *)0x8000318A = 0xffff; + } + } + */ + + + DCFlushRange((void*)0x80000000, 0x3f00); + + return 0; +} + + +extern GXRModeObj *disc_vmode; + +// Based in Waninkoko patch +void __Patch_CoverRegister(void *buffer, u32 len) +{ + const u8 oldcode[] = { + 0x54, 0x60, 0xF7, 0xFF, 0x40, 0x82, 0x00, 0x0C, + 0x54, 0x60, 0x07, 0xFF, 0x41, 0x82, 0x00, 0x0C }; + const u8 newcode[] = { + 0x54, 0x60, 0xF7, 0xFF, 0x40, 0x82, 0x00, 0x0C, + 0x54, 0x60, 0x07, 0xFF, 0x48, 0x00, 0x00, 0x0C }; + int n; + /* Patch cover register */ + for(n=0;n<(len-sizeof(oldcode));n+=4) + { + if (memcmp(buffer+n, (void *) oldcode, sizeof(oldcode)) == 0) + { + memcpy(buffer+n, (void *) newcode, sizeof(newcode)); + } + } +} + +// PoP patch by Dr. Clipper (thanks giantpune, WiiCrazy) +bool PrinceOfPersiaPatch() +{ + if (memcmp("SPX", (char *)0x80000000, 3) == 0 || memcmp("RPW", (char *)0x80000000, 3) == 0) { + u8 *p = (u8 *)0x807AEB6A; + *p++ = 0x6F; + *p++ = 0x6A; + *p++ = 0x7A; + *p++ = 0x6B; + p = (u8 *)0x807AEB75; + *p++ = 0x69; + *p++ = 0x39; + *p++ = 0x7C; + *p++ = 0x7A; + p = (u8 *)0x807AEB82; + *p++ = 0x68; + *p++ = 0x6B; + *p++ = 0x73; + *p++ = 0x76; + p = (u8 *)0x807AEB92; + *p++ = 0x75; + *p++ = 0x70; + *p++ = 0x80; + *p++ = 0x71; + p = (u8 *)0x807AEB9D; + *p++ = 0x6F; + *p++ = 0x3F; + *p++ = 0x82; + *p++ = 0x80; + return true; + } + return false; +} + +// NSMB patch by WiiPower +bool NewSuperMarioBrosPatch(void *Address, int Size) +{ + if (memcmp("SMN", (char *)0x80000000, 3) == 0) + { + u8 SearchPattern1[32] = { // PAL + 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, + 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, + 0x48, 0x12, 0xD9, 0x39, 0x7C, 0x7B, 0x1B, 0x78, + 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; + u8 SearchPattern2[32] = { // NTSC + 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, + 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, + 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, + 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; + u8 PatchData[4] = { 0x4E, 0x80, 0x00, 0x20 }; + + void *Addr = Address; + void *Addr_end = Address+Size; + + while (Addr <= Addr_end-sizeof(SearchPattern1)) + { + if (memcmp(Addr, SearchPattern1, sizeof(SearchPattern1))==0 + || memcmp(Addr, SearchPattern2, sizeof(SearchPattern2))==0) + { + memcpy(Addr,PatchData,sizeof(PatchData)); + return true; + } + Addr += 4; + } + } + return false; +} + +// from sneek by crediar +void sneek_video_patch(void *addr, u32 len) +{ + u8 *addr_start = addr; + u8 *addr_end = addr+len; + + while(addr_start < addr_end) + { + if( *(vu32*)(addr_start) == 0x3C608000 ) + { + if( ((*(vu32*)(addr_start+4) & 0xFC1FFFFF ) == 0x800300CC) && ((*(vu32*)(addr_start+8) >> 24) == 0x54 ) ) + { + //dbgprintf("DIP:[patcher] Found VI pattern:%08X\n", (u32)(addr_start) | 0x80000000 ); + *(vu32*)(addr_start+4) = 0x5400F0BE | ((*(vu32*)(addr_start+4) & 0x3E00000) >> 5 ); + } + } + addr_start += 4; + } +} + +void patch_video_modes(void *dst, int len) +{ + GXRModeObj** table = NULL; + if (CFG.game.video_patch == CFG_VIDEO_PATCH_ALL) + { + Search_and_patch_Video_To(dst, len, vmodes, disc_vmode); + } + else if (CFG.game.video_patch == CFG_VIDEO_PATCH_SNEEK) + { + sneek_video_patch(dst, len); + } + else if (CFG.game.video_patch == CFG_VIDEO_PATCH_SNEEK_ALL) + { + sneek_video_patch(dst, len); + Search_and_patch_Video_To(dst, len, vmodes, disc_vmode); + } + else + { + if (CFG.game.video_patch == CFG_VIDEO_PATCH_ON + && (CFG.game.video == CFG_VIDEO_SYS)) + { + switch(CONF_GetVideo()) + { + case CONF_VIDEO_PAL: + if(CONF_GetEuRGB60() > 0) + { + table = NTSC2PAL60; + } + else + { + table = NTSC2PAL; + } + break; + + case CONF_VIDEO_MPAL: + table = NTSC2PAL; + break; + + default: + table = PAL2NTSC; + break; + } + Search_and_patch_Video_Modes(dst, len, table); + } + + // force PAL50 (Narolez) + if (CFG.game.video == CFG_VIDEO_PAL50) { + Search_and_patch_Video_Modes(dst, len, NTSC2PAL); + } + if (CFG.game.video == CFG_VIDEO_PAL60) { + Search_and_patch_Video_Modes(dst, len, NTSC2PAL60); + } + if (CFG.game.video == CFG_VIDEO_NTSC) { + Search_and_patch_Video_Modes(dst, len, PAL2NTSC); + } + } +} + +void maindolpatches(void *dst, int len) +{ + DCFlushRange(dst, len); + + if (CFG.game.clean == CFG_CLEAN_ALL) return; + + wipregisteroffset((u32)dst, len); + + patch_video_modes(dst, len); + + if (CFG.game.ocarina) { + dogamehooks(dst,len); + } + if (CFG.game.vidtv) { + vidolpatcher(dst,len); + } + /*LANGUAGE PATCH - FISHEARS*/ + if (CFG.game.language != CFG_LANG_CONSOLE) { + langpatcher(dst,len); + } + // Country Patch by WiiPower + if(CFG.game.country_patch) { + PatchCountryStrings(dst, len); + } + + // 002b fix from NeoGammaR4 by WiiPower: + if (CFG.game.fix_002) { + Anti_002_fix(dst, len); + } + // disc in drive check + if (CFG.patch_dvd_check && !CFG.disable_dvd_patch && !CFG.game.clean) { + __Patch_CoverRegister(dst, len); + } + // NSMB patch by WiiPower + if (!CFG.disable_nsmb_patch) { + NewSuperMarioBrosPatch(dst, len); + } + // PoP patch + if (!CFG.disable_pop_patch) { + PrinceOfPersiaPatch(); + } + + DCFlushRange(dst, len); +} + +bool disable_return_to_patch = false; + +bool cfg_PatchReturnTo(void *Address, int Size) +{ + bool ret = false; + if (disable_return_to_patch) return ret; + // giantpune's return-to patch added by Dr. Clipper + if (CFG.return_to > 2 && !CFG.game.clean) { + ret = PatchReturnTo(Address, Size, CFG.return_to); + if (ret) { + //gprintf("return-to patched\n" ); + DCFlushRange(Address, Size); + } + } + return ret; +} + + +// ALT. DOL + + +typedef struct { + u8 filetype; + char name_offset[3]; + u32 fileoffset; + u32 filelen; +} __attribute__((packed)) FST_ENTRY; + + +char *fstfilename(u32 index) +{ + FST_ENTRY *fst = (FST_ENTRY *)*(u32 *)0x80000038; + u32 count = fst[0].filelen; + u32 stringoffset; + if (index < count) + { + stringoffset = *(u32 *)&(fst[index]) % (256*256*256); + return (char *)(*(u32 *)0x80000038 + count*12 + stringoffset); + } else + { + return NULL; + } +} + +u32 fstfileoffset(u32 index) +{ + FST_ENTRY *fst = (FST_ENTRY *)*(u32 *)0x80000038; + u32 count = fst[0].filelen; + if (index < count) + { + return fst[index].fileoffset; + } else + { + return 0; + } +} + +u32 Load_Dol_from_disc(u32 doloffset) +{ + int ret; + void *dol_header = NULL; + u32 entrypoint; + + dol_header = memalign(32, sizeof(dolheader)); + if (dol_header == NULL) + { + printf(gt("Out of memory")); + printf("\n"); + sleep(2); + return 0; + } + + //dvddone = 0; + //ret = bwDVD_LowRead(dol_header, sizeof(dolheader), doloffset, __dvd_readidcb); + //DVD_CHECK(); + ret = WDVD_Read(dol_header, sizeof(dolheader), (doloffset<<2)); + if (ret < 0) goto error; + + entrypoint = load_dol_start(dol_header); + + if (entrypoint == 0) + { +error: + printf(gt("Invalid .dol")); + printf("\n"); + sleep(2); + free(dol_header); + return 0; + } + + void *offset; + u32 pos; + u32 len; + + // dolStart, dolEnd added by Dr. Clipper for giantpune's return-to patch + u32 dolStart = 0x90000000; + u32 dolEnd = 0x0; + + int sec_idx = 0; + + printf_("..."); + while (load_dol_image(&offset, &pos, &len)) + { + if (len != 0) + { + //dvddone = 0; + //ret = bwDVD_LowRead(offset, len, (doloffset+pos/4), __dvd_readidcb); + //DVD_CHECK(); + dbg_printf("\rdol [%d] @ 0x%08x [%6x] 0x%08x\n", sec_idx, + (int)offset, len, (int)offset + len); + ret = WDVD_Read(offset, len, (doloffset<<2) + pos); + if (ret < 0) goto error; + + maindolpatches(offset, len); + + if( (u32)offset < dolStart ) + dolStart = (u32)offset; + + if( (u32)offset + len > dolEnd ) + dolEnd = (u32)offset + len; + + Remove_001_Protection(offset, len); + } + sec_idx++; + printf("."); + } + printf("\n"); + + cfg_PatchReturnTo((void*)dolStart, dolEnd - dolStart); + + free(dol_header); + + return entrypoint; +} + +void getdolnames() +{ + FST_ENTRY *fst = (FST_ENTRY *)*(u32 *)0x80000038; + u32 count = fst[0].filelen; + int i; + + dolcount = 0; + + for (i=1;i"); + } else + { + printf("\r"); + printf_("<%s>", fstfilename(dolindex[dolselect-1])); + } + printf(" "); + + pressed = 0; + + pressed = Wpad_WaitButtonsCommon(); + + if (pressed == WPAD_BUTTON_LEFT) + { + if (dolselect > 0) + { + dolselect--; + } else + { + dolselect = dolcount; + } + } + + if (pressed == WPAD_BUTTON_RIGHT) + { + if (dolselect < dolcount) + { + dolselect++; + } else + { + dolselect = 0; + } + } + + if (pressed & CFG.button_save.mask) + { + //Con_Clear(); + printf("\n"); + + // remember alt.dol name + u8 *gameid = (u8*)0x80000000; + int ret; + + if (dolselect == 0) { + STRCOPY(CFG.game.dol_name, "main.dol"); + } else { + STRCOPY(CFG.game.dol_name, fstfilename(dolindex[dolselect-1])); + } + printf_(gt("Saving settings...")); + printf(" "); + ret = CFG_save_game_opt(gameid); + if (ret) printf(gt("OK!")); + else { + printf("\n"); + printf_(gt("Error saving settings!")); + } + printf("\n"); + *CFG.game.dol_name = 0; + + sleep(1); + goto start; + } + + if (pressed & CFG.button_confirm.mask) + { + start: + printf("\n"); + //Con_Clear(); + if (dolselect == 0) + { + return 0; + } else + { + return fstfileoffset(dolindex[dolselect-1]); + } + } + } +} + + + +u32 Load_Dol_from_sd() +{ + int ret; + FILE* file; + void *dol_header; + u32 entrypoint; + + char fname[128]; + char gameidbuffer4[5]; + memset(gameidbuffer4, 0, 5); + memcpy(gameidbuffer4, (char*)0x80000000, 4); + //snprintf(buf, 128, "sd:/NeoGamma/%s.dol", gameidbuffer4); + snprintf(fname, sizeof(fname), "%s/%s.dol", USBLOADER_PATH, gameidbuffer4); + printf_("%s\n", fname); + + file = fopen(fname, "rb"); + + if(file == NULL) + { + printf_(gt("Not Found!")); + printf("\n"); + sleep(4); + return 0; + } + + int filesize; + fseek(file, 0, SEEK_END); + filesize = ftell(file); + fseek(file, 0, SEEK_SET); + + dol_header = memalign(32, sizeof(dolheader)); + if (dol_header == NULL) + { + printf(gt("Out of memory")); + printf("\n"); + sleep(2); + fclose(file); + return 0; + } + + ret = fread( dol_header, 1, sizeof(dolheader), file); + if(ret != sizeof(dolheader)) + { + printf(gt("Error reading dol header")); + printf("\n"); + sleep(2); + free(dol_header); + fclose(file); + return 0; + } + + entrypoint = load_dol_start(dol_header); + + if (entrypoint == 0) + { + printf(gt("Invalid .dol")); + printf("\n"); + sleep(2); + free(dol_header); + fclose(file); + return 0; + } + + void *offset; + u32 pos; + u32 len; + + // dolStart, dolEnd added by Dr. Clipper for giantpune's return-to patch + u32 dolStart = 0x90000000; + u32 dolEnd = 0x0; + + int sec_idx = 0; + + printf_("..."); + while (load_dol_image(&offset, &pos, &len)) + { + if(pos+len > filesize) + { + printf(gt(".dol too small")); + printf("\n"); + sleep(2); + free(dol_header); + fclose(file); + return -1; + } + + if (len != 0) + { + dbg_printf("\rdol [%d] @ 0x%08x [%6x] 0x%08x\n", sec_idx, + (int)offset, len, (int)offset + len); + fseek(file, pos, 0); + ret = fread( offset, 1, len, file); + if(ret != len) + { + printf(gt("Error reading .dol")); + printf("\n"); + sleep(2); + free(dol_header); + fclose(file); + return -1; + } + maindolpatches(offset, len); + + if( (u32)offset < dolStart ) + dolStart = (u32)offset; + + if( (u32)offset + len > dolEnd ) + dolEnd = (u32)offset + len; + + Remove_001_Protection(offset, len); + } + sec_idx++; + printf("."); + } + printf("\n"); + + cfg_PatchReturnTo((void*)dolStart, dolEnd - dolStart); + + free(dol_header); + fclose(file); + + return entrypoint; +} + + +static u8 *diskid = (u8 *)0x80000000; + +void PatchCountryStrings(void *Address, int Size) +{ + u8 SearchPattern[4] = { 0x00, 0x00, 0x00, 0x00 }; + u8 PatchData[4] = { 0x00, 0x00, 0x00, 0x00 }; + u8 *Addr = (u8*)Address; + + int wiiregion = CONF_GetRegion(); + + switch (wiiregion) + { + case CONF_REGION_JP: + SearchPattern[0] = 0x00; + SearchPattern[1] = 0x4A; // J + SearchPattern[2] = 0x50; // P + break; + case CONF_REGION_EU: + SearchPattern[0] = 0x02; + SearchPattern[1] = 0x45; // E + SearchPattern[2] = 0x55; // U + break; + case CONF_REGION_KR: + SearchPattern[0] = 0x04; + SearchPattern[1] = 0x4B; // K + SearchPattern[2] = 0x52; // R + break; + case CONF_REGION_CN: + SearchPattern[0] = 0x05; + SearchPattern[1] = 0x43; // C + SearchPattern[2] = 0x4E; // N + break; + case CONF_REGION_US: + default: + SearchPattern[0] = 0x01; + SearchPattern[1] = 0x55; // U + SearchPattern[2] = 0x53; // S + } + + switch (diskid[3]) + { + case 'J': + PatchData[1] = 0x4A; // J + PatchData[2] = 0x50; // P + break; + + case 'D': + case 'F': + case 'P': + case 'X': + case 'Y': + PatchData[1] = 0x45; // E + PatchData[2] = 0x55; // U + break; + + case 'E': + default: + PatchData[1] = 0x55; // U + PatchData[2] = 0x53; // S + } + + while (Size >= 4) + { + if (Addr[0] == SearchPattern[0] && Addr[1] == SearchPattern[1] && Addr[2] == SearchPattern[2] && Addr[3] == SearchPattern[3]) + { + //*Addr = PatchData[0]; + Addr += 1; + *Addr = PatchData[1]; + Addr += 1; + *Addr = PatchData[2]; + Addr += 1; + //*Addr = PatchData[3]; + Addr += 1; + Size -= 4; + } else + { + Addr += 4; + Size -= 4; + } + } +} + +bool Remove_001_Protection(void *Address, int Size) +{ + u8 SearchPattern[16] = { 0x40, 0x82, 0x00, 0x0C, 0x38, 0x60, 0x00, 0x01, 0x48, 0x00, 0x02, 0x44, 0x38, 0x61, 0x00, 0x18 }; + u8 PatchData[16] = { 0x40, 0x82, 0x00, 0x04, 0x38, 0x60, 0x00, 0x01, 0x48, 0x00, 0x02, 0x44, 0x38, 0x61, 0x00, 0x18 }; + + void *Addr = Address; + void *Addr_end = Address+Size; + + while(Addr <= Addr_end-sizeof(SearchPattern)) + { + if(memcmp(Addr, SearchPattern, sizeof(SearchPattern))==0) + { + memcpy(Addr,PatchData,sizeof(PatchData)); + return true; + } + Addr += 4; + } + return false; +} + +void Anti_002_fix(void *Address, int Size) +{ + u8 SearchPattern[12] = { 0x2C, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x14, 0x3C, 0x60, 0x80, 0x00 }; + u8 PatchData[12] = { 0x2C, 0x00, 0x00, 0x00, 0x40, 0x82, 0x02, 0x14, 0x3C, 0x60, 0x80, 0x00 }; + + void *Addr = Address; + void *Addr_end = Address+Size; + + while(Addr <= Addr_end-sizeof(SearchPattern)) + { + if(memcmp(Addr, SearchPattern, sizeof(SearchPattern))==0) + { + memcpy(Addr,PatchData,sizeof(PatchData)); + } + Addr += 4; + } +} + diff --git a/source/apploader.h b/source/apploader.h new file mode 100644 index 0000000..1124909 --- /dev/null +++ b/source/apploader.h @@ -0,0 +1,12 @@ +#ifndef _APPLOADER_H_ +#define _APPLOADER_H_ + +/* Entry point */ +typedef void (*entry_point)(void); + +/* Prototypes */ +s32 Apploader_Run(entry_point *); + +bool disable_return_to_patch; + +#endif diff --git a/source/banner.c b/source/banner.c new file mode 100644 index 0000000..1b4ac64 --- /dev/null +++ b/source/banner.c @@ -0,0 +1,674 @@ + +// Banner sounds by oggzee +// Fixes by Clipper + +#include +#include +#include +#include +#include + +#include "disc.h" +#include "cfg.h" +#include "wdvd.h" +#include "wpad.h" +#include "wbfs.h" +#include "libwbfs/libwbfs.h" +#include "gettext.h" + +#define dbg4_printf if (CFG.debug & 4) printf + +void dbg_pause() +{ + if ((CFG.debug & 5) != 5) return; + printf("(press button)"); + int b = Wpad_WaitButtons(); + printf("*\n"); + if (b & CFG.button_exit.mask) { + printf("exit\n"); + exit(0); + } +} + +void dbg_hex_dump(void *p, int size) +{ + if ((CFG.debug & 5) != 5) return; + hex_dump2(p, size); +} + +typedef struct { + //u8 zeroes[128]; // padding + u8 zeroes[0x40]; // padding + u8 imet[4]; // "IMET" + u8 unk[8]; // 0x0000060000000003 fixed, unknown purpose + u32 sizes[3]; // icon.bin, banner.bin, sound.bin + u32 flag1; // unknown + u8 names[10][84]; // JP, EN, DE, FR, ES, IT, NL, ZHCN, ZHTW, KO + u8 zeroes_2[0x24C]; // padding + u8 crypto[16]; // MD5 of 0x40 to 0x640 in header. +} IMET; + +struct U8_archive_header +{ + u8 tag[4]; // 0x55AA382D "U.8-" + u32 rootnode_offset; // offset to root_node, always 0x20. + u32 header_size; // size of header from root_node to end of string table. + u32 data_offset; // offset to data -- this is rootnode_offset + header_size, aligned to 0x40. + u8 zeroes[16]; +}; + +struct U8_node +{ + u8 type; + u8 name_offset[3]; //really a "u24" + u32 data_offset; + u32 size; +}; + +typedef struct { + u32 imd5; // 'IMD5' + u32 filesize; //size of rest of file + u8 zeroes[8]; //padding + u8 crypto[16]; //MD5 of rest of file +} IMD5; + +// Header +typedef struct { + char bns[4]; // 'BNS ' + u32 version; // 0xFEFF0100 endianness and format version check + u32 filesize; // size of entire BNS + u16 headersize; // size of BNS header (including chunkinfo) + u16 chunkcount; // number of chunks + struct { + u32 offset; // offset from start of BNS of chunk header + u32 size; // size of chunk including header + } chunkinfo[0]; //[chunkcount]; // info for each chunk +} BNS; + +// Chunk header +typedef struct { + char type[4]; // 'INFO' or 'DATA' + u32 size; // size including header +} BNS_chunk; + +// INFO chunk +typedef struct { + u8 unk; // 0, possibly format control + u8 loop; // loop flag, 0 = no loop, 1 = loop + u8 channels; // channel count + u8 unk2; // 0, padding? + u16 samplerate; // sample rate (Hz) + u16 pad; // 0 + u32 loopstart; // loop start sample + u32 samples; // total sample count + u32 offset; // offset (in INFO) to channel info offset list + u32 unk4; // 0, padding? +} INFO_chunk; + +// channel info offset list +typedef struct { +// u32 offset[channels]; // offset (in INFO) to channel info for each channel + u32 offset[0]; // offset (in INFO) to channel info for each channel +} channel_info_offset_list; + +// channel info (for DSP ADPCM) +typedef struct { + u32 data_offset; // offset (in DATA) of audio data for channel + u32 info_offset; // offset (in INFO) of DSP info (0x30 byte block) + u32 unk; // 0 +} channel_info; + + +void adpcm_decode(void *dsp_buf, void *adpcm_info, void *adpcm_data, + int samples, int channels, int cur_chan) +{ + // read: + short *CoEfficients = adpcm_info; //[16]; + u8 *cdata = adpcm_data; + // write: + //int dsp_size = size / 8 * 14; + int rsize = samples * 8 / 14; + short *dsp = dsp_buf; + // state + int rpos = 0, wpos = cur_chan; + short adpcmhistory1 = 0; + short adpcmhistory2 = 0; + + while (rpos < rsize) { + int sample; + short nibbles[14]; + int index1; + int i,j; + u8 header; + short delta; + u8 thisbyte; + short hist = adpcmhistory1; + short hist2 = adpcmhistory2; + // If we have loop data then we write to our looping file. + //if (mscdata.Position == looppos) + // bwpdata = new BinaryWriter(msploop); + + //header = brcdata.ReadByte(); + header = cdata[rpos++]; + i = header & 0xFF; + delta = (short) (1 << (i & 0xff & 0xf)); + index1 = (i & 0xff) >> 4; + + for(i = 0; i < 14; i += 2) { + //thisbyte = brcdata.ReadByte(); + thisbyte = cdata[rpos++]; + j = (thisbyte & 0xff) >> 4; + nibbles[i] = (short) j; + j = thisbyte & 0xff & 0xf; + nibbles[i+1] = (short) j; + } + + for(i = 0; i < 14; i++) { + if(nibbles[i] >= 8) + nibbles[i] = (short)(nibbles[i] - 16); + } + + for(i = 0; i<14 ; i++) { + sample = (delta * nibbles[i])<<11; + sample += CoEfficients[index1 * 2] * hist; + sample += CoEfficients[index1 * 2 + 1] * hist2; + sample = sample + 1024; + sample = sample >> 11; + if(sample > 32767) + sample = 32767; + + if(sample < -32768) + sample = -32768; + //bwpdata.Write((short)sample); + dsp[wpos] = (short)sample; + wpos += channels; + if (wpos >= samples * channels) { + rpos = rsize; + break; + } + hist2 = hist; + hist = (short)sample; + } + adpcmhistory1 = hist; + adpcmhistory2 = hist2; + + } +} + +u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size) +{ + u8 *data_end; + u8 *decompressed_data; + size_t unpacked_size; + u8 *in_ptr; + u8 *out_ptr; + u8 *out_end; + + in_ptr = data; + data_end = data + data_size; + // Assume this for now and grow when needed + unpacked_size = data_size; + decompressed_data = malloc(unpacked_size); + out_end = decompressed_data + unpacked_size; + out_ptr = decompressed_data; + + while (in_ptr < data_end) { + int bit; + u8 bitmask = *in_ptr; + + in_ptr++; + for (bit = 0x80; bit != 0; bit >>= 1) { + if (bitmask & bit) { + // Next section is compressed + u8 rep_length; + u16 rep_offset; + + rep_length = (*in_ptr >> 4) + 3; + rep_offset = *in_ptr & 0x0f; + in_ptr++; + rep_offset = *in_ptr | (rep_offset << 8); + in_ptr++; + if (out_ptr-decompressed_data < rep_offset) { + dbg4_printf(gt("Inconsistency in LZ77 encoding %x"), in_ptr-data); + dbg4_printf("\n"); + } + + for ( ; rep_length > 0; rep_length--) { + *out_ptr = out_ptr[-rep_offset-1]; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + } + } else { + // Just copy byte + *out_ptr = *in_ptr; + out_ptr++; + if (out_ptr >= out_end) { + // Need to grow buffer + decompressed_data = realloc(decompressed_data, unpacked_size*2); + out_ptr = decompressed_data + unpacked_size; + unpacked_size *= 2; + out_end = decompressed_data + unpacked_size; + } + in_ptr++; + } + } + } + + *decompressed_size = (out_ptr - decompressed_data); + return decompressed_data; +} + +void parse_bns(void *data_bns, SoundInfo *snd) +{ + BNS *bns = data_bns; + BNS_chunk *b_chunk = NULL; + INFO_chunk *i_chunk = NULL; + void *d_chunk = NULL; + channel_info_offset_list *ci_off; + channel_info *ci; + void *dsp_data = NULL; + int dsp_size = 0; + int i; + + dbg4_printf("BNS: %.4s 0x%x %x %d\n", bns->bns, + bns->version, bns->filesize, bns->chunkcount); + if (memcmp(bns, "BNS ", 4) != 0) return; + + for (i=0; ichunkcount; i++) { + b_chunk = (void*)bns + bns->chunkinfo[i].offset; + dbg4_printf("%d %4x %4x %.4s %x\n", i, + bns->chunkinfo[i].offset, + bns->chunkinfo[i].size, + b_chunk->type, + b_chunk->size); + if (memcmp(b_chunk->type, "INFO", 4)==0) { + if (!i_chunk) i_chunk = (void*)(b_chunk+1); + dbg4_printf(" l:%d c:%d s:%d %x %x %x\n", + i_chunk->loop, + i_chunk->channels, + i_chunk->samplerate, + i_chunk->loopstart, + i_chunk->samples, + i_chunk->offset); + } + if (memcmp(&b_chunk->type, "DATA", 4)==0) { + if (!d_chunk) d_chunk = (void*)(b_chunk+1); + } + } + + if (!i_chunk || !d_chunk) return; + if (i_chunk->channels < 1 || i_chunk->channels > 2) return; + + dsp_size = i_chunk->samples * i_chunk->channels * sizeof(short); + dsp_data = memalign(32, dsp_size + 14*2); + + ci_off = (void*)i_chunk + i_chunk->offset; + for (i=0; ichannels; i++) { + int off = ci_off->offset[i]; + ci = (void*)i_chunk + off; + dbg4_printf(" [%d] %x data: %x info: %x\n", i, off, + ci->data_offset, ci->info_offset); + void *adpcm_data = (void*)d_chunk + ci->data_offset; + void *adpcm_info = (void*)i_chunk + ci->info_offset; + adpcm_decode(dsp_data, adpcm_info, adpcm_data, + i_chunk->samples, i_chunk->channels, i); + } + snd->dsp_data = dsp_data; + snd->size = dsp_size; + snd->channels = i_chunk->channels; + snd->rate = i_chunk->samplerate; + snd->loop = 0; +} + + +/* +The canonical WAVE format starts with the RIFF header: + +Offset Size Name Description + +0 4 ChunkID Contains the letters "RIFF" in ASCII form + (0x52494646 big-endian form). +4 4 ChunkSize 36 + SubChunk2Size, or more precisely: + 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) + This is the size of the rest of the chunk + following this number. This is the size of the + entire file in bytes minus 8 bytes for the + two fields not included in this count: + ChunkID and ChunkSize. +8 4 Format Contains the letters "WAVE" + (0x57415645 big-endian form). + +The "WAVE" format consists of two subchunks: "fmt " and "data": +The "fmt " subchunk describes the sound data's format: + +12 4 Subchunk1ID Contains the letters "fmt " + (0x666d7420 big-endian form). +16 4 Subchunk1Size 16 for PCM. This is the size of the + rest of the Subchunk which follows this number. +20 2 AudioFormat PCM = 1 (i.e. Linear quantization) + Values other than 1 indicate some + form of compression. +22 2 NumChannels Mono = 1, Stereo = 2, etc. +24 4 SampleRate 8000, 44100, etc. +28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8 +32 2 BlockAlign == NumChannels * BitsPerSample/8 + The number of bytes for one sample including + all channels. I wonder what happens when + this number isn't an integer? +34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc. + 2 ExtraParamSize if PCM, then doesn't exist + X ExtraParams space for extra parameters + +The "data" subchunk contains the size of the data and the actual sound: + +36 4 Subchunk2ID Contains the letters "data" + (0x64617461 big-endian form). +40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 + This is the number of bytes in the data. + You can also think of this as the size + of the read of the subchunk following this + number. +44 * Data The actual sound data. +*/ + +typedef struct RIFF +{ + char ChunkID[4]; // Contains the letters "RIFF" in ASCII form + int ChunkSize; // 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) + char Format[4]; // Contains the letters "WAVE" + //The "WAVE" format consists of two subchunks: "fmt " and "data": + + //The "fmt " subchunk describes the sound data's format: + char Subchunk1ID[4];// Contains the letters "fmt " + int Subchunk1Size; // 16 for PCM. This is the size of the rest of the Subchunk + short AudioFormat; // PCM = 1 (i.e. Linear quantization) + short NumChannels; // Mono = 1, Stereo = 2, etc. + int SampleRate; // 8000, 44100, etc. + int ByteRate; // == SampleRate * NumChannels * BitsPerSample/8 + short BlockAlign; // == NumChannels * BitsPerSample/8 + short BitsPerSample;// 8 bits = 8, 16 bits = 16, etc. + // 2 ExtraParamSize // if PCM, then doesn't exist + // X ExtraParams // space for extra parameters +} RIFF; + +typedef struct RIFF_chnk +{ + char id[4]; + int size; +} RIFF_chnk; + +void parse_riff(void *data, SoundInfo *snd) +{ + RIFF *riff = data; + int riff_size = _le32(&riff->ChunkSize); + int sub1_size = _le32(&riff->Subchunk1Size); + dbg_hex_dump(data, 128); + dbg4_printf("'%.4s %x %.4s %.4s %x\n", + riff->ChunkID, _le32(&riff->ChunkSize), riff->Format, + riff->Subchunk1ID, sub1_size); + if (memcmp(riff->ChunkID, "RIFF", 4)) return; + if (memcmp(riff->Format, "WAVE", 4)) return; + if (memcmp(riff->Subchunk1ID, "fmt ", 4)) return; + + short AudioFormat = _le16(&riff->AudioFormat); + short NumChannels = _le16(&riff->NumChannels); + short BitsPerSample = _le16(&riff->BitsPerSample); + int SampleRate = _le32(&riff->SampleRate); + if (AudioFormat != 1 || BitsPerSample != 16) return; + if (NumChannels < 1 || NumChannels > 2) return; + + dbg4_printf("f:%d c:%d r:%d b:%d\n", AudioFormat, NumChannels, + SampleRate, BitsPerSample); + RIFF_chnk *chnk = ((void*)riff) + 12 + 8 + sub1_size; + RIFF_chnk *riff_data = NULL; + while (((void*)chnk > data) && ((void*)chnk < data + riff_size)) { + int size = _le32(&chnk->size); + dbg4_printf("[%.4s] %x\n", chnk->id, size); + dbg_pause(); + if (memcmp(chnk->id, "data", 4)==0) { + riff_data = chnk; + break; + } + if (size <= 0) break; + chnk = ((void*)chnk) + sizeof(RIFF_chnk) + size; + } + if (riff_data == NULL) return; + + int Size = _le32(&riff_data->size); + short *ss = (void*)(riff_data + 1); + short *dsp_data; + int i; + dsp_data = memalign(32, Size); + if (!dsp_data) return; + // byte order + for (i=0; idsp_data = dsp_data; + snd->channels = NumChannels; + snd->rate = SampleRate; + snd->size = Size; + snd->loop = 0; +} + +/* +Byte order: Big-endian + +Offset Length Contents + 0 4 bytes "FORM" + 4 4 bytes + 8 4 bytes "AIFF" +[ // Chunks + 4 bytes + 4 bytes + (x)bytes +]* + +COMM chunk: Must be defined + 0 4 bytes "COMM" + 4 4 bytes // (=18) + 8 2 bytes + 10 4 bytes + 14 2 bytes // 1..32 + 16 10 bytes + +SSND chunk: Must be defined + 0 4 bytes "SSND" + 4 4 bytes + 8 4 bytes + 12 4 bytes // (=0) + 16 (n)bytes Comment + 16+(n) (s)bytes // (s) := (x) - (n) - 8 +*/ + +typedef struct CHNK +{ + char id[4]; + int size; +} CHNK; + +typedef struct AIFF +{ + char id[4]; // FORM + int size; + char aiff[4]; // AIFF +} AIFF; + +/* +typedef struct COMM +{ + char id[4]; // 4 bytes "COMM" + int size; // 4 bytes // (=18) + u16 channels; // 2 bytes + int frames; // 4 bytes + u16 bits; // 2 bytes // 1..32 + char rate[10]; //10 bytes +} COMM; +*/ +typedef struct COMM +{ + char id[4]; // 4 bytes "COMM" + int size; // 4 bytes // (=18) + u16 channels; // 2 bytes + u8 frames[4]; // 4 bytes + u16 bits; // 2 bytes // 1..32 + u8 rate[10]; + //u16 pad1; + //u16 rate; + //u8 pad[6]; //10 bytes +} COMM; + +typedef struct SSND +{ + char id[4]; // 4 bytes "SSND" + int size; // 4 bytes + int offset; // 4 bytes + int bsize; // 4 bytes // (=0) + //(n)bytes Comment + //(s)bytes // (s) := (x) - (n) - 8 +} SSND; + +double ext_to_double(unsigned char a_extended[10]) +{ + int exponent,i; + char found; + union { + double dval; + unsigned char cval[8]; + } value; + unsigned char extended[10]; + memcpy(extended, a_extended, 10); + //for (i=0;i<10;i++) dbg4_printf("%02x ", extended[i]); puts(""); + /* I'll assume little endian for now. Easy to fix to big endian anyway */ + /* also assuming positive for simplicity */ + found=0; + for (i = 2; i < 10; i++) found |= extended[i]; + if (!found) return 0; + exponent = (((extended[0]&0x7F) << 8) | extended[1]) - 15359; + found=0; + while(!found) { + found = (extended[2] & 0x80); + for (i = 2; i < 9; i++) extended[i] = (extended[i] << 1) | (extended[i+1] >> 7); + extended[9] = extended[9] << 1; + exponent--; + } + /* Little Endian here: + value.cval[7] = extended[0]&0x80 | (exponent >> 4); + value.cval[6] = (exponent << 4) | (extended[2] >> 4); + for (i=0;i<6; i++) { + value.cval[5-i] = (extended[i+2] << 4) | (extended[i+3] >> 4); + } + */ + value.cval[0] = (extended[0]&0x80) | (exponent >> 4); + value.cval[1] = (exponent << 4) | (extended[2] >> 4); + for (i=0;i<6; i++) { + value.cval[i+2] = (extended[i+2] << 4) | (extended[i+3] >> 4); + } + return value.dval; +} + +void parse_aiff(void *data, SoundInfo *snd) +{ + AIFF *aiff = data; + CHNK *chnk = (CHNK*)(aiff+1); + COMM *comm = NULL; + SSND *ssnd = NULL; + int rate = 0; + dbg4_printf("%.4s %x %.4s\n", aiff->id, aiff->size, aiff->aiff); + while ((void*)chnk < data + aiff->size) { + dbg4_printf("[%.4s] %d %x\n", chnk->id, chnk->size, chnk->size); + if (memcmp(chnk->id, "COMM", 4)==0 && !comm) { + comm = (COMM*)chnk; + rate = (int)ext_to_double(comm->rate); + dbg4_printf("c:%d f:%x b:%d r:%d\n", comm->channels, + _be32(comm->frames), comm->bits, rate); + } + if (memcmp(chnk->id, "SSND", 4)==0 && !ssnd) { + ssnd = (SSND*)chnk; + dbg4_printf("%d %d\n", ssnd->offset, ssnd->bsize); + } + // next chunk: + chnk = (void*)(chnk+1) + chnk->size; + } + if (!comm || !ssnd) return; + int dsp_size = ssnd->size - ssnd->offset - 8; + void *dsp_data = memalign(32, dsp_size); + if (!dsp_data) return; + memcpy(dsp_data, (void*)(ssnd+1) + ssnd->offset, dsp_size); + snd->dsp_data = dsp_data; + snd->channels = comm->channels; + snd->rate = rate; + snd->size = dsp_size; + snd->loop = 0; +} + +void parse_banner_title(void *banner, u8 *title, s32 lang) +{ + memcpy(title, ((IMET*)banner)->names[lang], 84); +} + +void parse_banner_snd(void *banner, SoundInfo *snd) +{ + //extern char opening_bnr[]; + //banner = opening_bnr; + void *data_hdr = banner + sizeof(IMET); + struct U8_archive_header *u8_hdr; + struct U8_node *node; + int i, num, off; + char *name_start, *name; + u8 u8_tag[4] = {0x55, 0xAA, 0x38, 0x2D}; // "U.8-" + + dbg_hex_dump(banner, 128); + u8_hdr = data_hdr; + dbg4_printf("imet: %.4s\n", ((IMET*)banner)->imet); + + //printf("Title is: %s\n",banner_title); + dbg4_printf("U8: %.4s 0x%x\n", u8_hdr->tag, u8_hdr->rootnode_offset); + dbg_pause(); + if (memcmp(u8_hdr->tag, u8_tag, 4)) return; + + node = data_hdr + u8_hdr->rootnode_offset; + num = node->size; + name_start = (char*)&node[num]; + if (num>5) num = 5; + for (i=1; ifilesize; + void *lz_data = NULL; + retry: + dbg4_printf("[%.4s]\n", (char*)sound_data); + dbg_pause(); + if (memcmp(sound_data, "BNS ", 4)==0) { + parse_bns(sound_data, snd); + } else if (memcmp(sound_data, "RIFF", 4)==0) { + parse_riff(sound_data, snd); + } else if (memcmp(sound_data, "FORM", 4)==0) { + parse_aiff(sound_data, snd); + } else if (memcmp(sound_data, "LZ77", 4)==0 && lz_data == NULL) { + unsigned lz_hdr = ((unsigned*)sound_data)[1]; + lz_data = decompress_lz77(sound_data+8, size-8, &size); + dbg4_printf("de-LZ: %p %x %x %x %x %.4s\n", lz_data, + node[i].size, size, lz_hdr, lz_hdr>>8, (char*)lz_data); + sound_data = lz_data; + goto retry; + } + SAFE_FREE(lz_data); + } + } + dbg4_printf("snd: %p %x\n", snd->dsp_data, snd->size); +} + + diff --git a/source/cache.c b/source/cache.c new file mode 100644 index 0000000..064b7cb --- /dev/null +++ b/source/cache.c @@ -0,0 +1,2001 @@ + +// by oggzee & usptactical + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cache.h" +#include "mem.h" +#include "cfg.h" +#include "gui.h" +#include "coverflow.h" +//#include "disc.h" +//#include "gettext.h" + +//#define cc_dbg dbg_printf +#define cc_dbg(...) + +//0x2000000 = 32 MB +//0x1700000 = 23 MB +#define COVER_CACHE_DATA_SIZE 0x1900000 // 25 MB + +extern struct discHdr *all_gameList; +extern struct discHdr *gameList; +extern s32 gameCnt, all_gameCnt; + +#if 0 + +struct Cover_State +{ + char used; + char id[8]; + int gid; // ccache.game[gid] + GRRLIB_texImg tx; + //int lru; +}; + +struct Game_State +{ + int request; + int state; // idle, loading, present, missing + int cid; // cache index + int agi; // actual all_gameList[agi] (mapping gameList -> all_gameList) + char id[8]; +}; + +struct Cover_Cache +{ + void *data; + int width4, height4; + int csize; + int num, cover_alloc, lru; + struct Cover_State *cover; + int num_game, game_alloc; + struct Game_State *game; + volatile int run; + volatile int idle; + volatile int restart; + volatile int quit; + lwp_t lwp; + lwpq_t queue; + mutex_t mutex; +}; + +struct Cover_Cache ccache; +int ccache_init = 0; +int ccache_inv = 0; + +//**************************************************************************** +//************************** START CACHE CODE ****************************** +//**************************************************************************** + +void game_populate_ids() +{ + int i; + char *id; + + for (i=0; i= 0) { + memcheck_ptr(ccache.game, &ccache.game[gid]); + ccache.game[gid].state = CS_IDLE; + } + *ccache.cover[i].id = 0; + ccache.cover[i].used = 0; + //ccache.cover[i].lru = 0; + ccache.cover[i].gid = -1; +} + +int cache_alloc(char *id) +{ + int i; + i = cache_find(id); + if (i >= 0) goto found; + i = cache_find_free(); + if (i >= 0) goto found; + i = cache_find_lru(); + if (i >= 0) goto found; + return -1; + found: + cache_free(i); + return i; +} + +#ifdef DEBUG_CACHE + +struct Cache_Debug +{ + int cc_rq_prio; + int cc_i; + int cc_line; + int cc_line_lock; + int cc_line_unlock; + int cc_locked; + int cc_cid; + void *cc_img; + void *cc_buf; +}; + +struct Cache_Debug ccdbg; + +#define ___ ccdbg.cc_line = __LINE__ +#define CACHE_LOCK() do{ \ + ccdbg.cc_line_lock = __LINE__; \ + LWP_MutexLock(ccache.mutex); \ + ccdbg.cc_locked = 1; }while(0) + +#define CACHE_UNLOCK() do{ \ + ccdbg.cc_line_unlock = __LINE__; \ + LWP_MutexUnlock(ccache.mutex); \ + ccdbg.cc_locked = 0; }while(0) + +#define CCDBG(X) X + +#else + +#define ___ +#define CACHE_LOCK() LWP_MutexLock(ccache.mutex) +#define CACHE_UNLOCK() LWP_MutexUnlock(ccache.mutex) +#define CCDBG(X) + +#endif + + +/** + * This is the main cache thread. This method loops through all the game indexes + * (0 through ccache.num_game) and loads each requested cover. The covers are loaded + * based on the priority level (rq_prio) -> 1 is highest priority, 3 is lowest. + * + */ + +void* cache_thread(void *arg) +{ + int i, ret, actual_i; + char *id; + void *img; + void *buf; + int cid; + int rq_prio; + bool resizeToFullCover = false; + int cover_height, cover_width; + char path[200]; + //cc_dbg("thread started\n"); + + ___; + while (!ccache.quit) { + ___; + CACHE_LOCK(); + ccache.idle = 0; + //cc_dbg("thread running\n"); + + restart: + + ccache.restart = 0; + ___; + //if (0) // disable + for (rq_prio=1; rq_prio<=3; rq_prio++) { + ___; + for (i=0; i 0 && img) { + ___; + cid = cache_alloc(id); + if (cid < 0) { + // should not happen + free(img); + ccache.game[actual_i].state = CS_IDLE; + goto end_request; + } + buf = ccache.data + ccache.csize * cid; + ___; + CACHE_UNLOCK(); + ___; + memcheck_ptr(ccache.cover, &ccache.cover[cid]); + CCDBG(ccdbg.cc_img = img); + CCDBG(ccdbg.cc_buf = buf); + CCDBG(ccdbg.cc_cid = cid); + //sleep(3);//dbg + + LWP_MutexUnlock(ccache.mutex); + + //make sure the cover height and width didn't change on us before we resize it + if ((cover_height == COVER_HEIGHT) && + (cover_width == COVER_WIDTH)) { + + if (resizeToFullCover) { + ccache.cover[cid].tx = Gui_LoadTexture_fullcover(img, COVER_WIDTH, COVER_HEIGHT, COVER_WIDTH_FRONT, buf, path); + resizeToFullCover = false; + } else { + if (CFG.cover_style == CFG_COVER_STYLE_FULL && CFG.gui_compress_covers) { + ccache.cover[cid].tx = Gui_LoadTexture_CMPR(img, COVER_WIDTH, COVER_HEIGHT, buf, path); + } else { + ccache.cover[cid].tx = Gui_LoadTexture_RGBA8(img, COVER_WIDTH, COVER_HEIGHT, buf, path); + } + if (!ccache.cover[cid].tx.data && CFG.cover_style == CFG_COVER_STYLE_FULL) { + //corrupted image - try to load the 2D cover + ret = Gui_LoadCover_style((u8*)id, &img, true, CFG_COVER_STYLE_2D, path); + if (ret && img) + ccache.cover[cid].tx = Gui_LoadTexture_fullcover(img, COVER_WIDTH, COVER_HEIGHT, COVER_WIDTH_FRONT, buf, path); + } + } + } else { + CCDBG(ccdbg.cc_img = NULL); + CCDBG(ccdbg.cc_buf = NULL); + free(img); + if (ccache.quit) goto quit; + CACHE_LOCK(); + goto end_request; + } + + CCDBG(ccdbg.cc_img = NULL); + CCDBG(ccdbg.cc_buf = NULL); + free(img); + ___; + if (ccache.quit) goto quit; + ___; + CACHE_LOCK(); + ___; + if (ccache.cover[cid].tx.data == NULL) { + cache_free(cid); + goto noimg; + } + // mark + STRCOPY(ccache.cover[cid].id, id); + // check if req change + if (ccache.game[actual_i].request) { + ccache.cover[cid].used = 1; + ccache.cover[cid].gid = actual_i; + ccache.game[actual_i].cid = cid; + ccache.game[actual_i].state = CS_PRESENT; + //cc_dbg("Load OK %d %d %s\n", i, cid, id); + + } else { + ccache.game[actual_i].state = CS_IDLE; + } + } else { + noimg: + ccache.game[actual_i].state = CS_MISSING; + //cc_dbg("Load FAIL %d %s\n", i, id); + } + end_request: + // request processed. + ccache.game[actual_i].request = 0; + ___; + } + } + ___; + CCDBG(ccdbg.cc_rq_prio = 0); + CCDBG(ccdbg.cc_i = -1); + ccache.idle = 1; + //cc_dbg("thread idle\n"); + // all processed. wait. + while (!ccache.restart && !ccache.quit) { + ___; + CACHE_UNLOCK(); + //cc_dbg("thread sleep\n"); + ___; + LWP_ThreadSleep(ccache.queue); + //cc_dbg("thread wakeup\n"); + ___; + CACHE_LOCK(); + ___; + } + ___; + ccache.restart = 0; + CACHE_UNLOCK(); + } + + quit: + ___; + ccache.quit = 0; + + return NULL; +} + +#undef ___ + +void cache_wait_idle() +{ + int i; + if (!ccache_init) return; + // wait till idle + i = 1000; // 1 second + while (!ccache.idle && i) { + usleep(1000); + i--; + } +} + +/** + * This method is used to set the caching priority of the passed in game index. + * If the image is already cached or missing then the CoverCache is updated for + * the cover. + * + * @param game_i the game index + * @param rq_prio the cache priority: 1 = high priority + */ +void cache_request_1(int game_i, int rq_prio) +{ + int cid, actual_i; + if (game_i < 0 || game_i >= ccache.num_game) return; + + actual_i = cache_game_find(game_i); + if (ccache.game[actual_i].request>0) return; + // check if already present + if (ccache.game[actual_i].state == CS_PRESENT) { + cid = ccache.game[actual_i].cid; + ccache.cover[cid].used = 1; + return; + } + // check if already missing + if (ccache.game[actual_i].state == CS_MISSING) { + return; + } + // check if already cached + cid = cache_find((char*)gameList[game_i].id); + if (cid >= 0) { + ccache.cover[cid].used = 1; + ccache.cover[cid].gid = actual_i; + ccache.game[actual_i].cid = cid; + ccache.game[actual_i].state = CS_PRESENT; + return; + } + // add request + ccache.game[actual_i].request = rq_prio; +} + + +/** + * This method is used to set the caching priority of the passed in game index + * as well as the next x number of games. + * + * @param game_i the starting game index + * @param num the number of images to set the priority on + * @param rq_prio the cache priority: 1 = high priority + */ +void cache_request(int game_i, int num, int rq_prio) +{ + int i, idle; + // setup requests + CACHE_LOCK(); + for (i=0; i= ccache.num_game) + nextRight = 0; + cache_request_1(nextRight, 2); + + if (nextLeft < 0) + nextLeft = ccache.num_game - 1; + cache_request_1(nextLeft, 2); + + nextRight++; + nextLeft--; + } + + // now set the priority to low for +-10 covers + for (i=0; i<10; i++) { + if (nextRight >= ccache.num_game) + nextRight = 0; + cache_request_1(nextRight, 3); + + if (nextLeft < 0) + nextLeft = ccache.num_game - 1; + cache_request_1(nextLeft, 3); + + nextRight++; + nextLeft--; + } + + // restart thread + ccache.restart = 1; + idle = ccache.idle; + CACHE_UNLOCK(); + LWP_ThreadSignal(ccache.queue); + if (idle) { + //cc_dbg("thread idle wait restart\n"); + i = 10; + while (ccache.restart && i) { + usleep(10); + LWP_ThreadSignal(ccache.queue); + i--; + } + if (ccache.restart) { + //cc_dbg("thread fail restart\n"); + } + } + //cc_dbg("cache restart done\n"); +} + + +void cache_release_1(int game_i) +{ + int cid; + if (game_i < 0 || game_i >= ccache.num_game) return; + memcheck_ptr(ccache.game, &ccache.game[game_i]); + ccache.game[game_i].request = 0; + if (ccache.game[game_i].state == CS_PRESENT) { + // release if already cached + cid = ccache.game[game_i].cid; + memcheck_ptr(ccache.cover, &ccache.cover[cid]); + ccache.cover[cid].used = 0; //-- + ccache.game[game_i].state = CS_IDLE; + } +} + +void cache_release(int game_i, int num) +{ + int i; + // cancel requests + CACHE_LOCK(); + for (i=0; i ccache.game_alloc || ccache.game == NULL) + { + ccache.game_alloc = all_gameCnt; + size = sizeof(struct Game_State) * ccache.game_alloc; + ccache.game = realloc(ccache.game, size); + if (ccache.game == NULL) goto err; + resized = true; + } + cache_num_game(); + + memcheck(); + // cover table + // check cover size + + if (CFG.cover_style == CFG_COVER_STYLE_FULL && CFG.gui_compress_covers) { + ccache.width4 = COVER_WIDTH; + ccache.height4 = COVER_HEIGHT; + ccache.csize = (ccache.width4 * ccache.height4)/2; + } else { + ccache.width4 = (COVER_WIDTH + 3) >> 2 << 2; + ccache.height4 = (COVER_HEIGHT + 3) >> 2 << 2; + ccache.csize = ccache.width4 * ccache.height4 * 4; + } + + new_num = COVER_CACHE_DATA_SIZE / ccache.csize; + if (new_num > ccache.cover_alloc || ccache.cover == NULL) { + ccache.cover_alloc = new_num; + size = sizeof(struct Cover_State) * ccache.cover_alloc; + ccache.cover = realloc(ccache.cover, size); + if (ccache.cover == NULL) goto err; + resized = true; + } + ccache.num = new_num; + + memcheck(); + if (resized) { + // clear both tables + size = sizeof(struct Game_State) * ccache.game_alloc; + memset(ccache.game, 0, size); + size = sizeof(struct Cover_State) * ccache.cover_alloc; + memset(ccache.cover, 0, size); + ccache.lru = 0; + } + memcheck(); + + //cc_dbg("cache_resize %p %d %d %p %d %d\n", + // ccache.game, ccache.game_alloc, ccache.num_game, + // ccache.cover, ccache.cover_alloc, ccache.num); + + return 0; + +err: + // fatal + printf(gt("ERROR: cache: out of memory")); + printf("\n"); + sleep(1); + return -1; +} + +/** + * Initializes the cover cache + */ +int Cache_Init() +{ + int ret; + + //update the cache tables size + if (ccache_inv || !ccache_init) { + // call invalidate again, for safety + // this will cancel all requests and wait till idle + Cache_Invalidate(); + ret = cache_resize_tables(); + if (ret) return -1; + ccache_inv = 0; + } + ccache.num_game = gameCnt; + + if (ccache_init) return 0; + ccache_init = 1; + + ccache.data = LARGE_memalign(32, COVER_CACHE_DATA_SIZE); + + ccache.lwp = LWP_THREAD_NULL; + ccache.queue = LWP_TQUEUE_NULL; + ccache.mutex = LWP_MUTEX_NULL; + + // start thread + LWP_InitQueue(&ccache.queue); + LWP_MutexInit(&ccache.mutex, FALSE); + ccache.run = 1; + //cc_dbg("running thread\n"); + ret = LWP_CreateThread(&ccache.lwp, cache_thread, NULL, NULL, 32*1024, 40); + if (ret < 0) { + //cache_thread_stop(); + return -1; + } + //cc_dbg("lwp ret: %d id: %x\n", ret, ccache.lwp); + return 0; +} + +void Cache_Invalidate() +{ + if (!ccache_init) return; + CACHE_LOCK(); + // clear requests + memset(ccache.game, 0, sizeof(struct Game_State) * ccache.game_alloc); + CACHE_UNLOCK(); + // wait till idle + cache_wait_idle(); + if (!ccache.idle) { + //printf("\nERROR: cache not idle\n"); sleep(3); + } + // clear all + memset(ccache.game, 0, sizeof(struct Game_State) * ccache.game_alloc); + memset(ccache.cover, 0, sizeof(struct Cover_State) * ccache.cover_alloc); + + ccache_inv = 1; +} + +void Cache_Close() +{ + int i; + if (!ccache_init) return; + ccache_init = 0; + CACHE_LOCK(); + ccache.quit = 1; + CACHE_UNLOCK(); + LWP_ThreadSignal(ccache.queue); + i = 1000; // 1 second + while (ccache.quit && i) { + usleep(1000); + LWP_ThreadSignal(ccache.queue); + i--; + } + if (ccache.quit) { + printf("\n"); + printf(gt("ERROR: Cache Close")); + printf("\n"); + sleep(5); + } else { + LWP_JoinThread(ccache.lwp, NULL); + } + LWP_CloseQueue(ccache.queue); + LWP_MutexDestroy(ccache.mutex); + ccache.lwp = LWP_THREAD_NULL; + ccache.queue = LWP_TQUEUE_NULL; + ccache.mutex = LWP_MUTEX_NULL; + SAFE_FREE(ccache.cover); + SAFE_FREE(ccache.game); +} + +void draw_Cache() +{ +#ifdef DEBUG_CACHE + unsigned int i, x, y, c, cid; + + // thread state + x = 15; + y = 30; + c = ccache.idle ? 0x00FF00FF : 0xFF0000FF; + GRRLIB_Rectangle(x, y, 8, 8, c, 1); + x += 10; + c = !ccache.restart ? 0x00FF00FF : 0xFF0000FF; + GRRLIB_Rectangle(x, y, 8, 8, c, 1); + x += 10; + c = !ccache.quit ? 0x00FF00FF : 0xFF0000FF; + GRRLIB_Rectangle(x, y, 8, 8, c, 1); + x += 20; + GRRLIB_Printf(x, y, tx_cfont, 0xFFFFFFFF, 1, "%d %d %d", + ccdbg.cc_rq_prio, ccdbg.cc_i, ccdbg.cc_line); + // lock state + x = 15; + y = 40; + c = !ccdbg.cc_locked ? 0x00FF00FF : 0xFF0000FF; + GRRLIB_Rectangle(x, y, 8, 8, c, 1); + x += 40; + GRRLIB_Printf(x, y, tx_cfont, 0xFFFFFFFF, 1, "%d %d %p %p %d", + ccdbg.cc_line_lock, ccdbg.cc_line_unlock, + ccdbg.cc_img, ccdbg.cc_buf, ccdbg.cc_cid); + + // cover state + for (i=0; iid); +} + +bool cover_key_cmp(void *cb, void *key, int handle) +{ + Cover_Key *k1 = key; + Cover_Key *k2 = &ccache.cstate[handle].key; + return memcmp(k1, k2, sizeof(*k1)) == 0; +} + +int* cover_hnext(void *cb, int handle) +{ + return &ccache.cstate[handle].hnext; +} + +void make_cover_key(Cover_Key *ck, u8 *id, int style, int format) +{ + memset(ck, 0, sizeof(*ck)); + strcopy((char*)ck->id, (char*)id, sizeof(ck->id)); + ck->style = style; + ck->format = format; +} + +Cover_State* cache_find_cover(u8 *id, int style, int format) +{ + Cover_Key ck; + int i; + make_cover_key(&ck, id, style, format); + CACHE_LOCK(); + i = hash_get(&ccache.hash, &ck); + CACHE_UNLOCK(); + if (i < 0) return NULL; + return &ccache.cstate[i]; +} + +Cover_State* cache_find_free() +{ + int i; + Cover_State *cs = NULL; + for (i=0; iused) continue; + if (cs->request) continue; + if (cs->state != CS_IDLE) continue; + if (cs->key.id[0]) continue; + return cs; + } + return NULL; +} + +Cover_State* cache_find_lru(bool present) +{ + Cover_State *cs = NULL; + Cover_State *lru_cs = NULL; + Cover_State *hq_cs = NULL; + int lru = -1; + int hq_lru = -1; + int hq_cnt = 0; + int rgb_cnt = 0; + int i; + CACHE_LOCK(); + for (i=0; iused) continue; + if (cs->request) continue; + if (cs->state == CS_LOADING) continue; + if (present) { + if (cs->state != CS_PRESENT) continue; + } else { + if (cs->state == CS_PRESENT) continue; + } + if (cs->lru > lru) { + lru = cs->lru; + lru_cs = cs; + //if (lru >= LRU_MAX / 2) break; + } + if (present) { + // limit FULL/HQ RGBA to 3 + if ( (cs->key.style == CFG_COVER_STYLE_FULL + || cs->key.style == CFG_COVER_STYLE_HQ) + && cs->key.format == CC_FMT_RGBA) + { + rgb_cnt++; + if (rgb_cnt > 3) { + lru_cs = cs; + break; + } + } + // limit HQ to 10 + if (cs->key.style == CFG_COVER_STYLE_HQ) { + hq_cnt++; + if (cs->lru > hq_lru) { + hq_lru = cs->lru; + hq_cs = cs; + } + } + if (hq_cnt > 10) { + lru_cs = hq_cs; + } + } + } + CACHE_UNLOCK(); + return lru_cs; +} + +void cache_free_buf(void *ptr) +{ + CACHE_LOCK(); + if (ptr) heap_free(&ccache.heap, ptr); + CACHE_UNLOCK(); +} + +#define CACHE_SAFE_FREE(ptr) if(ptr){cache_free_buf(ptr);ptr=NULL;} + +void* cache_alloc_buf(int size) +{ + CACHE_LOCK(); + void *ptr = heap_alloc(&ccache.heap, size); + CACHE_UNLOCK(); + return ptr; +} + +void cache_free_cover(Cover_State *cs) +{ + int i = cs - ccache.cstate; + CACHE_LOCK(); + hash_remove(&ccache.hash, &cs->key, i); + CACHE_SAFE_FREE(cs->tx.data); + memset(cs, 0, sizeof(*cs)); + CACHE_UNLOCK(); +} + +Cover_State* cache_free_lru(bool present) +{ + Cover_State *cs; + cs = cache_find_lru(present); + if (cs) { + cache_free_cover(cs); + } + return cs; +} + +void* cache_alloc_data(int size) +{ + void *ptr = NULL; + CACHE_LOCK(); + do { + ptr = cache_alloc_buf(size); + if (!ptr) { + // try to free + // find lru with data + Cover_State *cs = cache_free_lru(true); + if (!cs) break; + } + } while (!ptr); + CACHE_UNLOCK(); + if (!ptr) dbg_printf("cc: alloc err %d\n", size); + return ptr; +} + +Cover_State* cache_get_free_cover() +{ + Cover_State *cs = NULL; + cs = cache_find_free(); + if (!cs) { + cs = cache_free_lru(false); + } + if (!cs) { + cs = cache_free_lru(true); + } + return cs; +} + +Cover_State* cache_alloc_cover(u8 *id, int style, int format) +{ + Cover_State *cs = NULL; + CACHE_LOCK(); + cs = cache_get_free_cover(); + if (cs) { + make_cover_key(&cs->key, id, style, format); + int i = cs - ccache.cstate; + hash_add(&ccache.hash, &cs->key, i); + } + CACHE_UNLOCK(); + return cs; +} + +// find existing mark as used and return +// or create new, set key, mark as used and return +// return NULL on error +Cover_State* cache_get_cover(u8 *id, int style, int format) +{ + Cover_State *cs = NULL; + CACHE_LOCK(); + cs = cache_find_cover(id, style, format); + if (!cs) { + cs = cache_alloc_cover(id, style, format); + } + if (cs) { + cs->used = 1; + } + CACHE_UNLOCK(); + return cs; +} + +// + +struct CIMG_HDR +{ + char tag[4]; + char id[8]; + short version; + short gxformat; + short width; + short height; + short lod; + short hq; + short pad[4]; +}; + +char* cache_image_path(u8 *id, char *path, int size) +{ + char *coverpath = cfg_get_covers_path(CFG_COVER_STYLE_CACHE); + snprintf(path, size, "%s/%.6s.ccc", coverpath, (char*)id); + return coverpath; +} + +// the normal quality 512x512 mipmap is saved first followed by HQ +// so that it loads faster since HQ is used less frequently +int cache_save_image(u8 *id, GRRLIB_texImg *tx) +{ + char filepath[200]; + char *dir; + struct CIMG_HDR hdr; + int fd, ret; + int size, hq_size = 0; + u8 *data; + + if (!id || !*id || !tx || !tx->data) return -1; + dir = cache_image_path(id, filepath, sizeof(filepath)); + dbg_printf("cc save: %s\n", filepath); + mkdir(dir, 0777); + fd = open(filepath, O_CREAT | O_WRONLY, 0333); + if (fd < 0) return -1; + + memset(&hdr, 0, sizeof(hdr)); + memcpy(hdr.tag, "CIMG", 4); + memcpy(hdr.id, id, 6); + hdr.version = 1; + hdr.gxformat = tx->tex_format; + hdr.width = tx->w; + hdr.height = tx->h; + hdr.lod = tx->tex_lod; + hdr.hq = 0; + data = tx->data; + if (tx->w > 512 && tx->tex_lod) { + // hq + hq_size = fixGX_GetTexBufferSize(hdr.width, hdr.height, + hdr.gxformat, GX_FALSE, 1); + data += hq_size; + hdr.width /= 2; + hdr.height /= 2; + hdr.lod--; + hdr.hq = 1; + } + + ret = write(fd, &hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) goto error; + + size = fixGX_GetTexBufferSize(hdr.width, hdr.height, + hdr.gxformat, hdr.lod ? GX_TRUE : GX_FALSE, hdr.lod); + ret = write(fd, data, size); + if (ret != size) goto error; + + if (hdr.hq) { + ret = write(fd, tx->data, hq_size); + if (ret != hq_size) goto error; + } + close(fd); + return 0; + +error: + if (fd >= 0) close(fd); + remove(filepath); + return -1; +} + +// style: FULL OR HQ +// FULL is always loaded, HQ only if requested and available in file +// hqavail is set to what is available in the file +int cache_load_image(u8 *id, GRRLIB_texImg *tx, int style, bool *hqavail) +{ + char filepath[200]; + struct CIMG_HDR hdr; + int fd, ret; + int size, hq_size = 0, data_size; + u8 *data = NULL; + + if (!id || !*id || !tx || tx->data) return -1; + memset(tx, 0, sizeof(*tx)); + memset(&hdr, 0, sizeof(hdr)); + *hqavail = false; + cache_image_path(id, filepath, sizeof(filepath)); + fd = open(filepath, O_RDONLY); + if (fd < 0) return -1; + dbg_printf("cc load: %s\n", filepath); + ret = read(fd, &hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) goto error; + if (memcmp(hdr.tag, "CIMG", 4)) goto error; + if (memcmp(hdr.id, id, 6)) goto error; + if (hdr.version != 1) goto error; + if (hdr.gxformat != GX_TF_CMPR) goto error; + if (hdr.width > 512) goto error; + if (hdr.height > 512) goto error; + + size = fixGX_GetTexBufferSize(hdr.width, hdr.height, + hdr.gxformat, hdr.lod ? GX_TRUE : GX_FALSE, hdr.lod); + + if (style == CFG_COVER_STYLE_HQ && hdr.hq) { + // load HQ too + hdr.width *= 2; + hdr.height *= 2; + hdr.lod++; + hq_size = fixGX_GetTexBufferSize(hdr.width, hdr.height, + hdr.gxformat, GX_FALSE, 1); + } + + data_size = size + hq_size; + data = cache_alloc_data(data_size); + if (!data) { + close(fd); + return -2; // alloc error + } + + ret = read(fd, data + hq_size, size); + if (ret != size) goto error; + + if (hq_size) { + ret = read(fd, data, hq_size); + if (ret != hq_size) goto error; + //memset(data, 0xAA, 16000); // dbg red stripe + } + + tx->data = data; + tx->w = hdr.width; + tx->h = hdr.height; + tx->tex_lod = hdr.lod; + tx->tex_format = hdr.gxformat; + GRRLIB_FlushTex(tx); + *hqavail = hdr.hq ? true : false; + close(fd); + return 0; + +error: + CACHE_SAFE_FREE(data); + memset(tx, 0, sizeof(*tx)); + close(fd); + return -1; +} + + +int cache_convert_image(u8 *id) +{ + char path[200]; + void *img = NULL; + GRRLIB_texImg tx; + u32 width=0, height=0; + int gx_lod = CC_MIPMAP_LOD; + int ret; + memset(&tx, 0, sizeof(tx)); + ret = Gui_LoadCover_style(id, &img, false, CFG_COVER_STYLE_HQ, path); + if (ret <= 0) return -1; + ret = __Gui_GetPngDimensions(img, &width, &height); + if (ret < 0) goto out; + width = upperPower(width); + height = upperPower(height); + if (width > 512) gx_lod++; // +1 if HQ + tx = Gui_LoadTexture_MIPMAP(img, width, height, gx_lod, NULL, path); + if (!tx.data) { ret = -1; goto out; } + ret = cache_save_image(id, &tx); +out: + SAFE_FREE(img); + SAFE_FREE(tx.data); + return ret; +} + +void cache_check_convert_image(u8 *id) +{ + struct stat st1, st2; + char path1[200], path2[200]; + int ret1, ret2; + ret1 = find_cover_path(id, CFG_COVER_STYLE_FULL, path1, sizeof(path1), &st1); + cache_image_path(id, path2, sizeof(path2)); + ret1 = stat(path1, &st1); + if (ret1 == 0) { + ret2 = stat(path2, &st2); + if (ret2 == 0 && st1.st_mtime <= st2.st_mtime) return; + // ignore if modify time in future + if (ret2 == 0 && st1.st_mtime > time(NULL)) return; + cache_convert_image(id); + } +} + + +void cache_cover_load(Cover_State *cs) +{ + char path[200]; + void *img = NULL; + void *buf = NULL; + int size; + bool resizeToFullCover = false; + GRRLIB_texImg tx; + int ret; + int state = CS_IDLE; + long long t1, tload = 0, tdecode = 0; + bool hqavail = false; + Cover_State *csfull = NULL; + memset(&tx, 0, sizeof(tx)); + + if (cs->key.style == CFG_COVER_STYLE_HQ) { + //sleep(2);//dbg + // if FULL is present it will have the info if HQ is available + CACHE_LOCK(); + csfull = cache_find_cover(cs->key.id, CFG_COVER_STYLE_FULL, cs->key.format); + if (csfull) hqavail = csfull->hq_available; + CACHE_UNLOCK(); + if (csfull && !hqavail) { + state = CS_MISSING; + goto out; + } + } + + // use cached converted image + if (cs->key.style == CFG_COVER_STYLE_FULL || cs->key.style == CFG_COVER_STYLE_HQ) { + if (cs->key.format == CC_FMT_MIPMAP) { + t1 = gettime(); + cache_check_convert_image(cs->key.id); + tdecode = diff_usec(t1, gettime()); + t1 = gettime(); + ret = cache_load_image(cs->key.id, &tx, cs->key.style, &hqavail); + tload = diff_usec(t1, gettime()); + if (ret == 0 && tx.data) goto out; + if (ret == -2) { + // alloc error + state = CS_IDLE; + goto out; + } + } + } + + t1 = gettime(); + //load the cover image + ret = Gui_LoadCover_style((u8*)cs->key.id, &img, false, cs->key.style, path); + tload = diff_usec(t1, gettime()); + //cc_dbg("cc ld: %.6s %d %d = %d %p\n", + // cs->key.id, cs->key.style, cs->key.format, ret, img); + if (ret <= 0 && cs->key.style == CFG_COVER_STYLE_FULL) { + //try to load the 2D cover + ;overlay_2d:; + tload = 0; + SAFE_FREE(img); + ret = Gui_LoadCover_style((u8*)cs->key.id, &img, true, CFG_COVER_STYLE_2D, path); + //cc_dbg("cc ld 2d: %.6s %d %d = %d %p\n", + // cs->key.id, cs->key.style, cs->key.format, ret, img); + resizeToFullCover = true; + } + if (ret <= 0 || !img) { + state = CS_MISSING; + goto out; + } + if (resizeToFullCover) { + int cover_width_front = (int)(tx_nocover_full.h / 1.4) >> 2 << 2; + size = tx_nocover_full.w * tx_nocover_full.h * 4; + if (CFG.gui_compress_covers) { + size /= 8; + } + buf = cache_alloc_data(size); + if (!buf) { + state = CS_IDLE; + goto out; + } + // XXX format depends on CFG.gui_compress_covers + // needs to be an arg. + tx = Gui_LoadTexture_fullcover(img, + tx_nocover_full.w, tx_nocover_full.h, + cover_width_front, buf, path); + if (!tx.data) { + CACHE_SAFE_FREE(buf); + } else { + ccache.stat_n2d++; + } + } else { + u32 width=0, height=0; + // Get image dimensions + ret = __Gui_GetPngDimensions(img, &width, &height); + if (ret < 0) goto invalid_img; + //cc_dbg("cc sz %d x %d / %d x %d\n", width, height, COVER_WIDTH, COVER_HEIGHT); + if (width > 512) hqavail = true; + if (cs->key.style == CFG_COVER_STYLE_FULL) { + // scale down HQ if FULL requested + if (width > 512) { + width = 512; + height = 340; + //if (cs->key.format == CC_FMT_CMPR) height = 336; + } + //if (width > 64) { width = 64; height = 64; } // dbg + } + int gx_fmt = GX_TF_RGBA8; + int gx_mipmap = GX_FALSE; + int gx_lod = 0; + switch (cs->key.format) { + case CC_FMT_CMPR: + gx_fmt = GX_TF_CMPR; + // 512x512 CMPR looks better than 512x340 + // but is slower - old AA with 512x512 is too slow + //height = width; + break; + case CC_FMT_MIPMAP: + gx_fmt = GX_TF_CMPR; + gx_mipmap = GX_TRUE; + gx_lod = CC_MIPMAP_LOD; + width = upperPower(width); + height = upperPower(height); + if (width > 512) gx_lod++; // +1 if HQ + break; + } + size = fixGX_GetTexBufferSize(width, height, gx_fmt, gx_mipmap, gx_lod); + buf = cache_alloc_data(size); + if (!buf) { + state = CS_IDLE; + goto out; + } + t1 = gettime(); + switch (cs->key.format) { + default: + case CC_FMT_C4x4: + tx = Gui_LoadTexture_RGBA8(img, width, height, buf, path); + break; + case CC_FMT_CMPR: + tx = Gui_LoadTexture_CMPR(img, width, height, buf, path); + break; + case CC_FMT_MIPMAP: + tx = Gui_LoadTexture_MIPMAP(img, width, height, gx_lod, buf, path); + break; + } + tdecode = diff_usec(t1, gettime()); + //cc_dbg("cc tx %d (%p %d %d %p) = %p\n", cs->key.format, + // img, COVER_WIDTH, COVER_HEIGHT, buf, tx.data); + if (!tx.data) { + ;invalid_img:; + tdecode = 0; + state = CS_MISSING; + CACHE_SAFE_FREE(buf); + // corrupted image or out of mem? + dbg_printf("cc error %.6s\n", cs->key.id); + if (cs->key.style == CFG_COVER_STYLE_FULL) { + // try to load the 2D cover + goto overlay_2d; + } + } + } +out: + SAFE_FREE(img); + CACHE_LOCK(); + if (tx.data) { + state = CS_PRESENT; + if (cs->key.style == CFG_COVER_STYLE_HQ && !hqavail) { + // HQ requested but only full found + // if full exists then discard currently loaded + // else store as full and mark HQ as not available + csfull = cache_find_cover(cs->key.id, CFG_COVER_STYLE_FULL, cs->key.format); + if (!csfull) { + csfull = cache_get_cover(cs->key.id, CFG_COVER_STYLE_FULL, cs->key.format); + csfull->used = 0; + } + if (csfull) { + if (csfull->state != CS_PRESENT) { + csfull->state = CS_PRESENT; + csfull->tx = tx; + csfull->request = 0; + buf = NULL; + } + csfull->hq_available = false; + } + CACHE_SAFE_FREE(buf); + memset(&tx, 0, sizeof(tx)); + state = CS_MISSING; + } + } + cs->state = state; + cs->request = 0; + cs->tx = tx; + cs->hq_available = hqavail; + if (tload && tx.data) { + ccache.stat_load += tload; + ccache.stat_decode += tdecode; + } + CACHE_UNLOCK(); + if (tx.data) cc_dbg("cc load: %.6s %d %d = %p %d\n", + cs->key.id, cs->key.style, cs->key.format, tx.data, state); +} + +void cache_process_requests() +{ + Cover_State *cs; + int prio; + int i; + do { + ccache.new_request = 0; + for (prio = 3; prio > 0; prio--) { + ;restart:; + for (i=0; irequest == prio) { + cs->state = CS_LOADING; + } + CACHE_UNLOCK(); + if (cs->state == CS_LOADING) { + // load + //sleep(1);//dbg + cache_cover_load(cs); + // need to restart loop at higher priority? + if (ccache.new_request > prio) { + //cc_dbg("new rq %d %d\n", ccache.new_request, prio); + prio = ccache.new_request; + ccache.new_request = 0; + goto restart; + } + } + } + } + //if (ccache.new_request) cc_dbg("new rq %d\n", ccache.new_request); + } while (ccache.new_request); +} + +void* cache_thread(void *arg) +{ + cc_dbg("cc started\n"); + while (1) { + // lock + CACHE_LOCK(); + if (!ccache.stop && ccache.new_request == 0) { + cc_dbg("cc waiting\n"); + ccache.waiting = true; + // LWP_CondWait unlocks mutex on enter + LWP_CondWait(ccache.cond, ccache.mutex); + // LWP_CondWait locks mutex on exit + ccache.waiting = false; + cc_dbg("cc -> running\n"); + } + // unlock + CACHE_UNLOCK(); + if (ccache.stop) { + break; + } + cache_process_requests(); + } + return NULL; +} + +int Cache_Init() +{ + if (ccache_init) return 0; + int ret; + ccache.data = mem2_alloc(COVER_CACHE_DATA_SIZE); + if (!ccache.data) return -1; + heap_init(&ccache.heap, ccache.data, COVER_CACHE_DATA_SIZE); + hash_init(&ccache.hash, 0, NULL, cover_key_hash, cover_key_cmp, cover_hnext); + ccache.lwp = LWP_THREAD_NULL; + ccache.mutex = LWP_MUTEX_NULL; + ccache.cond = LWP_COND_NULL; + LWP_MutexInit(&ccache.mutex, true); + LWP_CondInit(&ccache.cond); + // start thread + dbg_printf("cc. start\n"); + ret = LWP_CreateThread(&ccache.lwp, cache_thread, NULL, NULL, 32*1024, 40); + if (ret < 0) { + return -1; + } + //cc_dbg("lwp ret: %d id: %x\n", ret, ccache.lwp); + ccache_init = 1; + return 0; +} + +void Cache_Wakeup() +{ + //cc_dbg("cc wakeup\n"); + CACHE_LOCK(); + bool waiting = ccache.waiting; + CACHE_UNLOCK(); + // wake + if (waiting) { + cc_dbg("cc signal\n"); + LWP_CondSignal(ccache.cond); + } +} + +void Cache_Close() +{ + dbg_printf("cc close\n"); + if (!ccache_init) return; + if (ccache.lwp == LWP_THREAD_NULL) return; + CACHE_LOCK(); + ccache.stop = true; + CACHE_UNLOCK(); + Cache_Wakeup(); + // wait for exit and cleanup + LWP_JoinThread(ccache.lwp, NULL); + LWP_MutexDestroy(ccache.mutex); + LWP_CondDestroy(ccache.cond); + hash_close(&ccache.hash); + //heap_close + SAFE_FREE(ccache.data); + memset(&ccache, 0, sizeof(ccache)); + ccache.lwp = LWP_THREAD_NULL; + ccache_init = 0; + dbg_printf("cc stop\n"); +} + +GRRLIB_texImg* cache_request_cover1(int game_index, int style, int format, int priority, int *state) +{ + Cover_State *cs = NULL; + GRRLIB_texImg *tx = NULL; + bool need_wake = false; + u8 *id = NULL; + if (game_index < 0 || game_index >= gameCnt) goto error; + if (style > CFG_COVER_STYLE_HQ) goto error; + id = gameList[game_index].id; + //cc_dbg("cc req: %d %.6s %d %d %d\n", game_index, id, style, format, priority); + if (priority) { + cs = cache_get_cover(id, style, format); + if (!cs) goto error; + // cs will already be marked as used by cache_get_cover + } else { + // if priority == CC_PRIO_NONE then + // return the cover only if already loaded + // but don't updte LRU + CACHE_LOCK(); + cs = cache_find_cover(id, style, format); + if (cs) cs->used = 1; + CACHE_UNLOCK(); + if (!cs) { + if (state) *state = CS_IDLE; + return NULL; + } + + } + if (priority > 0) { + CACHE_LOCK(); + if (cs->state == CS_IDLE) { + if (cs->request == 0) { + need_wake = true; + //cc_dbg("cc rq: %d %.6s %d %d %d\n", game_index, id, style, format, priority); + } + if (priority > cs->request) { + cs->request = priority; + // mark new request + if (priority > ccache.new_request) { + ccache.new_request = priority; + } + } + } + cs->lru = 0; + CACHE_UNLOCK(); + } + if (state) *state = cs->state; + tx = &cs->tx; + if (need_wake) Cache_Wakeup(); + //cc_dbg("cc req = %d %p\n", cs->state, tx); + return tx; +error: + // out of cover slots or invalid index + if (state) *state = CS_ERROR; + return NULL; +} + +void cache_remap_style_fmt(int *cstyle, int *fmt) +{ + switch (*cstyle) { + case CFG_COVER_STYLE_HQ_OR_FULL_RGB: + *cstyle = CFG_COVER_STYLE_HQ_OR_FULL; + *fmt = CC_FMT_C4x4; + break; + case CFG_COVER_STYLE_FULL_RGB: + *cstyle = CFG_COVER_STYLE_FULL; + *fmt = CC_FMT_C4x4; + break; + case CFG_COVER_STYLE_HQ_RGB: + *cstyle = CFG_COVER_STYLE_HQ; + *fmt = CC_FMT_C4x4; + break; + case CFG_COVER_STYLE_FULL_CMPR: + *cstyle = CFG_COVER_STYLE_FULL; + *fmt = CC_FMT_CMPR; + break; + case CFG_COVER_STYLE_HQ_CMPR: + *cstyle = CFG_COVER_STYLE_HQ; + *fmt = CC_FMT_CMPR; + break; + case CFG_COVER_STYLE_FULL_MIPMAP: + *cstyle = CFG_COVER_STYLE_FULL; + *fmt = CC_FMT_MIPMAP; + break; + case CFG_COVER_STYLE_HQ_MIPMAP: + *cstyle = CFG_COVER_STYLE_HQ; + *fmt = CC_FMT_MIPMAP; + break; + } +} + +GRRLIB_texImg* cache_request_cover(int game_index, int style, int format, int priority, int *state) +{ + GRRLIB_texImg *tx; + int my_state; + cache_remap_style_fmt(&style, &format); + if (style == CFG_COVER_STYLE_HQ_OR_FULL) { + // HQ or FULL requested, try HQ first: + tx = cache_request_cover1(game_index, CFG_COVER_STYLE_HQ, + format, priority, &my_state); + if (my_state == CS_PRESENT) { + // HQ present: return it + if (state) *state = my_state; + return tx; + } + if ((my_state == CS_IDLE && priority > 0) || my_state == CS_LOADING) { + // HQ requested or loading: return FULL if present + // but don't add a request for FULL (CC_PRIO_NONE) + return cache_request_cover1(game_index, CFG_COVER_STYLE_FULL, + format, CC_PRIO_NONE, state); + } + // HQ not found: request FULL + style = CFG_COVER_STYLE_FULL; + } + return cache_request_cover1(game_index, style, format, priority, state); +} + +/** + * This method is used to set the caching priority of the passed in game index + * as well as the next x number of games. + * + * @param game_i the starting game index + * @param num the number of images to set the priority on + * @param rq_prio the cache priority: 1 = high priority + */ +void cache_request(int game_i, int num, int rq_prio) +{ + int i; + //cc_dbg("cc_req(%d %d %d)\n", game_i, num, rq_prio); + for (i = game_i; i < game_i + num; i++) { + cache_request_cover(i, CFG.cover_style, CC_FMT_C4x4, CC_PRIO_HIGH, NULL); + } +} + + +/** + * This method is used to set the caching priority of the passed in game index + * as well as the next x number of games before and after the index. All the + * rest of the covers are given a lower priority. + * + * @param game_i the starting game index + * @param num the number of images before and after the game_i to set the priority on + * @param rq_prio the cache priority: 1 = high priority + */ +void cache_request_before_and_after(int game_i, int num, int rq_prio) +{ + int i; + int format = CC_FMT_C4x4; + if (CFG.gui_compress_covers) format = CC_COVERFLOW_FMT; + //cc_dbg("cc_rqba(%d %d %d)\n", game_i, num, format); + if (showingFrontCover) { + cache_request_cover(game_i, CFG_COVER_STYLE_FULL, format, CC_PRIO_HIGH, NULL); + } else { + cache_request_cover(game_i, CFG_COVER_STYLE_HQ_OR_FULL, format, CC_PRIO_HIGH, NULL); + } + for (i = game_i - num; i < game_i + num; i++) { + if (i == game_i) continue; + cache_request_cover(i, CFG_COVER_STYLE_FULL, format, CC_PRIO_MED, NULL); + } + num += 5; + for (i = game_i - num; i < game_i + num; i++) { + if (i == game_i) continue; + cache_request_cover(i, CFG_COVER_STYLE_FULL, format, CC_PRIO_LOW, NULL); + } +} + +void cache_release_cover(int game_index, int style, int format) +{ + Cover_State *cs = NULL; + u8 *id = gameList[game_index].id; + cs = cache_get_cover(id, style, format); + if (cs) { + CACHE_LOCK(); + cs->used = 0; + if (cs->request > 0) cs->request--; + if (cs->lru < LRU_MAX) cs->lru++; + CACHE_UNLOCK(); + } +} + +void cache_release_full(bool full) +{ + Cover_State *cs; + int i; + CACHE_LOCK(); + for (i=0; iused = 0; + if (full) { + // completely release + cs->request = 0; + } else { + // decrease priority + if (cs->request > 0) cs->request--; + } + if (cs->lru < LRU_MAX) cs->lru++; + } + CACHE_UNLOCK(); +} + +void cache_release_all() +{ + cache_release_full(false); +} + +void Cache_Invalidate() +{ +} + +void cache_wait_idle() +{ + cache_release_full(true); + while (!ccache.waiting) { + LWP_YieldThread(); + usleep(1000); + } +} + +void Cache_Invalidate_Cover(u8 *id, int style) +{ + int i; + bool do_free; + if (!ccache_init) return; + Cover_State *cs; + cache_wait_idle(); + CACHE_LOCK(); + for (i = 0; i < CACHE_STATE_NUM; i++) { + cs = &ccache.cstate[i]; + if (strcmp((char*)cs->key.id, (char*)id) == 0) { + do_free = false; + if (cs->key.style == style) do_free = true; + if (cs->key.style == CFG_COVER_STYLE_FULL + && (style == CFG_COVER_STYLE_2D || style == CFG_COVER_STYLE_HQ)) + { + do_free = true; + } + if (do_free) cache_free_cover(cs); + } + } + if (style == CFG_COVER_STYLE_FULL || style == CFG_COVER_STYLE_HQ) { + cache_check_convert_image(id); + } + CACHE_UNLOCK(); +} + +void cache_num_game() +{ +} + +void cache_stats(char *str, int size) +{ + int i; + int present = 0; + int missing = 0; + int nfree = 0; + int nstyle[5]; + int style; + memset(nstyle, 0, sizeof(nstyle)); + Cover_State *cs; + for (i = 0; i < CACHE_STATE_NUM; i++) { + cs = &ccache.cstate[i]; + if (cs->state == CS_MISSING) missing++; + if (cs->state == CS_PRESENT) { + present++; + style = cs->key.style; + if (style >= 0 && style < 5) { + nstyle[style]++; + } + + } + } + heap_stats hs; + heap_stat(&ccache.heap, &hs); +#define MBf (1024.0 * 1024.0) + nfree = CACHE_STATE_NUM - (present + missing); + snprintf(str, size, + "ccache p: %d m: %d f: %d / %d\n" + "cc mem: s:%.1f u:%.1f f:%.1f [%d,%d]\n" + "cc tm: load: %.3f decode: %.3f\n" + "cc 2d:%d 3d:%d d:%d f:%d hq:%d f2d:%d\n", + present, missing, nfree, CACHE_STATE_NUM, + hs.size / MBf, hs.used / MBf, hs.free / MBf, + ccache.heap.used_list.num, + ccache.heap.free_list.num, + (float)ccache.stat_load / 1000000.0, + (float)ccache.stat_decode / 1000000.0, + nstyle[0], nstyle[1], nstyle[2], nstyle[3], nstyle[4], + ccache.stat_n2d + ); +} + +#endif + +//**************************************************************************** +//**************************** END CACHE CODE ****************************** +//**************************************************************************** diff --git a/source/cache.h b/source/cache.h new file mode 100644 index 0000000..c5038e6 --- /dev/null +++ b/source/cache.h @@ -0,0 +1,56 @@ + +// by oggzee & usptactical + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include +#include +#include + +#include "my_GRRLIB.h" + +#define CS_IDLE 0 // free +#define CS_LOADING 1 +#define CS_PRESENT 2 +#define CS_MISSING 3 +#define CS_ERROR 4 // (invalid req or internal error) + +#define CC_PRIO_NONE 0 +#define CC_PRIO_LOW 1 +#define CC_PRIO_MED 2 +#define CC_PRIO_HIGH 3 + +#define CC_FMT_RGBA 0 +#define CC_FMT_C4x4 1 // RGBA8 4x4 +#define CC_FMT_CMPR 2 +#define CC_FMT_MIPMAP 3 +// default LOD: +#define CC_MIPMAP_LOD 4 // +1 if HQ +// default format for full and HQ covers: +#define CC_COVERFLOW_FMT CC_FMT_MIPMAP + +extern int ccache_inv; + +int cache_game_find(int gi); +void cache_request(int, int, int); +void cache_request_before_and_after(int, int, int); +void cache_release_all(void); +int Cache_Init(void); +void Cache_Close(void); +void Cache_Invalidate(void); +void cache_wait_idle(void); +void cache_num_game(); + +//#define DEBUG_CACHE +void draw_Cache(); + +// request with CC_PRIO_NONE will get current state without creating a request +GRRLIB_texImg* cache_request_cover(int game_index, int style, int format, int priority, int *state); + +void Cache_Invalidate_Cover(u8 *id, int style); +void cache_stats(char *str, int size); +void cache_remap_style_fmt(int *cstyle, int *fmt); + +#endif + diff --git a/source/cfg.c b/source/cfg.c new file mode 100644 index 0000000..fa9b62c --- /dev/null +++ b/source/cfg.c @@ -0,0 +1,3983 @@ + +// by oggzee, usptactical & Dr. Clipper + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dir.h" +#include "cfg.h" +#include "wpad.h" +#include "gui.h" +#include "video.h" +#include "fat.h" +#include "net.h" +#include "xml.h" +#include "gettext.h" +#include "menu.h" +#include "console.h" +#include "sys.h" +#include "menu.h" +#include "wbfs.h" +#include "wgui.h" + +char FAT_DRIVE[8] = SDHC_DRIVE; +char USBLOADER_PATH[200] = "sd:/usb-loader"; +char APPS_DIR[200] = ""; +char LAST_CFG_PATH[200]; +char direct_start_id_buf[] = "#GAMEID\0\0\0\0\0CFGUSB0000000000"; +char DIOS_MIOS_INFO[200] = ""; + +/* configurable fields */ + +int ENTRIES_PER_PAGE = 12; +int MAX_CHARACTERS = 30; + +int CONSOLE_XCOORD = 258; +int CONSOLE_YCOORD = 112; +int CONSOLE_WIDTH = 354; +int CONSOLE_HEIGHT = 304; + +int CONSOLE_FG_COLOR = 15; +int CONSOLE_BG_COLOR = 0; + +int COVER_XCOORD = 24; +int COVER_YCOORD = 104; + +struct CFG CFG; + +int COVER_WIDTH_FRONT; +int COVER_HEIGHT_FRONT; + +#define TITLE_MAX 64 + +struct ID_Title +{ + u8 id[8]; + char title[TITLE_MAX]; + int hnext; // linked list next with same hash +}; + +// renamed titles +int num_title = 0; //number of titles +struct ID_Title *cfg_title = NULL; +HashTable title_hash_id6; +HashTable title_hash_id4; + +#define MAX_CFG_GAME 500 +int num_cfg_game = 0; +struct Game_CFG_2 cfg_game[MAX_CFG_GAME]; + +struct TextMap map_video[] = +{ + { "system", CFG_VIDEO_SYS }, + { "game", CFG_VIDEO_GAME }, + { "auto", CFG_VIDEO_AUTO }, + { "pal50", CFG_VIDEO_PAL50 }, + { "pal60", CFG_VIDEO_PAL60 }, + { "ntsc", CFG_VIDEO_NTSC }, + { "pal480p",CFG_VIDEO_NUM }, + { NULL, -1 } +}; + +struct TextMap map_video_patch[] = +{ + { "0", CFG_VIDEO_PATCH_OFF }, + { "1", CFG_VIDEO_PATCH_ON }, + { "all", CFG_VIDEO_PATCH_ALL }, + { "sneek", CFG_VIDEO_PATCH_SNEEK }, + { "sneek+all", CFG_VIDEO_PATCH_SNEEK_ALL }, + { NULL, -1 } +}; + +char *names_vpatch[CFG_VIDEO_PATCH_NUM] = +{ + gts("Off"), + gts("On"), + gts("All"), + "sneek", + "sneek+all", +}; + +struct TextMap map_language[] = +{ + { "console", CFG_LANG_CONSOLE }, + { "japanese", CFG_LANG_JAPANESE }, + { "english", CFG_LANG_ENGLISH }, + { "german", CFG_LANG_GERMAN }, + { "french", CFG_LANG_FRENCH }, + { "spanish", CFG_LANG_SPANISH }, + { "italian", CFG_LANG_ITALIAN }, + { "dutch", CFG_LANG_DUTCH }, + { "s.chinese", CFG_LANG_S_CHINESE }, + { "t.chinese", CFG_LANG_T_CHINESE }, + { "korean", CFG_LANG_KOREAN }, + { NULL, -1 } +}; + +struct TextMap map_layout[] = +{ + { "original", CFG_LAYOUT_ORIG }, + { "original1", CFG_LAYOUT_ORIG }, + { "original2", CFG_LAYOUT_ORIG_12 }, + { "small", CFG_LAYOUT_SMALL }, + { "medium", CFG_LAYOUT_MEDIUM }, + { "large", CFG_LAYOUT_LARGE }, + { "large2", CFG_LAYOUT_LARGE_2 }, + { "large3", CFG_LAYOUT_LARGE_3 }, + { "ultimate1", CFG_LAYOUT_ULTIMATE1 }, + { "ultimate2", CFG_LAYOUT_ULTIMATE2 }, + { "ultimate3", CFG_LAYOUT_ULTIMATE3 }, + { "kosaic", CFG_LAYOUT_KOSAIC }, + { NULL, -1 } +}; + +struct TextMap map_ios[] = +{ + { "245", CFG_IOS_245 }, + { "246", CFG_IOS_246 }, + { "247", CFG_IOS_247 }, + { "248", CFG_IOS_248 }, + { "249", CFG_IOS_249 }, + { "250", CFG_IOS_250 }, + { "251", CFG_IOS_251 }, + { "222-mload", CFG_IOS_222_MLOAD }, + { "223-mload", CFG_IOS_223_MLOAD }, + { "224-mload", CFG_IOS_224_MLOAD }, + { "222-yal", CFG_IOS_222_YAL }, + { "223-yal", CFG_IOS_223_YAL }, + { "Auto", CFG_IOS_AUTO }, + { NULL, -1 } +}; + +u8 cIOS_base[10]; + +struct TextMap map_gui_style[] = +{ + { "grid", GUI_STYLE_GRID }, + { "flow", GUI_STYLE_FLOW }, + { "flow-z", GUI_STYLE_FLOW_Z }, + { "coverflow3d", GUI_STYLE_COVERFLOW + coverflow3d }, + { "coverflow2d", GUI_STYLE_COVERFLOW + coverflow2d }, + { "frontrow", GUI_STYLE_COVERFLOW + frontrow }, + { "vertical", GUI_STYLE_COVERFLOW + vertical }, + { "carousel", GUI_STYLE_COVERFLOW + carousel }, + { NULL, -1 } +}; + +struct TextMap map_button[] = +{ + { "-", CFG_BTN_M }, + { "+", CFG_BTN_P }, + { "A", CFG_BTN_A }, + { "B", CFG_BTN_B }, + { "H", CFG_BTN_H }, + { "1", CFG_BTN_1 }, + { "2", CFG_BTN_2 }, + { "nothing", CFG_BTN_NOTHING }, + { "options", CFG_BTN_OPTIONS }, + { "gui", CFG_BTN_GUI }, + { "reboot", CFG_BTN_REBOOT }, + { "exit", CFG_BTN_EXIT }, + { "hbc", CFG_BTN_HBC }, + { "screenshot", CFG_BTN_SCREENSHOT }, + { "install", CFG_BTN_INSTALL }, + { "remove", CFG_BTN_REMOVE }, + { "main_menu", CFG_BTN_MAIN_MENU }, + { "global_ops", CFG_BTN_GLOBAL_OPS }, + { "favorites", CFG_BTN_FAVORITES }, + { "boot_game", CFG_BTN_BOOT_GAME }, + { "boot_disc", CFG_BTN_BOOT_DISC }, + { "theme", CFG_BTN_THEME }, + { "profile", CFG_BTN_PROFILE }, + { "unlock", CFG_BTN_UNLOCK }, + { "sort", CFG_BTN_SORT }, + { "filter", CFG_BTN_FILTER }, + { "priiloader", CFG_BTN_PRIILOADER }, + { "channel", CFG_BTN_CHANNEL }, + { "wii_menu", CFG_BTN_WII_MENU }, + { "random", CFG_BTN_RANDOM }, + { NULL, -1 } +}; + +struct TextMap map_button_menu[] = +{ + //{ "A", NUM_BUTTON_A }, + { "B", NUM_BUTTON_B }, + { "1", NUM_BUTTON_1 }, + { "2", NUM_BUTTON_2 }, + { "-", NUM_BUTTON_MINUS }, + { "M", NUM_BUTTON_MINUS }, + { "+", NUM_BUTTON_PLUS }, + { "P", NUM_BUTTON_PLUS }, + { "H", NUM_BUTTON_HOME }, + { "X", NUM_BUTTON_X }, + { "Y", NUM_BUTTON_Y }, + { "Z", NUM_BUTTON_Z }, + { "C", NUM_BUTTON_C }, + { "L", NUM_BUTTON_L }, + { "R", NUM_BUTTON_R }, + { NULL, -1 } +}; + +struct TextMap map_hook[] = +{ + { "nohooks", 0 }, + { "vbi", 1 }, + { "wiipad", 2 }, + { "gcpad", 3 }, + { "gxdraw", 4 }, + { "gxflush", 5 }, + { "ossleep", 6 }, + { "axframe", 7 }, + { NULL, -1 } +}; + +char *hook_name[NUM_HOOK] = +{ + "No Hooks", + "VBI", + "Wii Pad", + "GC Pad", + "GXDraw", + "GXFlush", + "OSSleepThread", + "AXNextFrame" +}; + +struct TextMap map_nand_emu[] = +{ + { "off", 0 }, + { "partitial", 1 }, + { "full", 2 }, + { NULL, -1 } +}; + +struct TextMap map_boot_method[] = //combination of map_channel_boot and map_gc_boot +{ + { "Mighty Plugin", 0 }, + { "Neek2o Plugin", 1 }, + { "Default", 0 }, + { "DIOS MIOS", 1 }, + { "Devolution", 2 }, + { "Nintendont", 3 }, + { NULL, -1 } +}; + +struct TextMap map_channel_boot[] = +{ + { "Mighty Plugin", 0 }, + { "Neek2o Plugin", 1 }, + { NULL, -1 } +}; + +struct TextMap map_gc_boot[] = +{ + { "Default", 0 }, + { "DIOS MIOS", 1 }, + { "Devolution", 2 }, + { "Nintendont", 3 }, + { NULL, -1 } +}; + +struct playStat { + char id[7]; + s32 playCount; + time_t playTime; +}; + +bool playStatsRead = false; +int playStatsSize = 0; +struct playStat *playStats; + +int CFG_IOS_MAX = MAP_NUM(map_ios) - 1; +int CURR_IOS_IDX = -1; + + + +// theme +//#define MAX_THEME 100 +int num_theme = 0; +int cur_theme = -1; +char theme_list[MAX_THEME][MAX_THEME_NAME]; +char theme_path[200]; + +void game_set(char *name, char *val); +void load_theme(char *theme); +void cfg_setup1(); +void cfg_setup2(); +void cfg_setup3(); +void cfg_direct_start(int argc, char **argv); + + +void cfg_layout() +{ + // CFG_LAYOUT_ORIG (1.0 - base for most) + ENTRIES_PER_PAGE = 6; + MAX_CHARACTERS = 30; + CONSOLE_XCOORD = 260; + CONSOLE_YCOORD = 115; + CONSOLE_WIDTH = 340; + CONSOLE_HEIGHT = 218; + CONSOLE_FG_COLOR = 15; + CONSOLE_BG_COLOR = 0; + // orig 1.2 + COVER_XCOORD = 24; + COVER_YCOORD = 104; + //COVER_WIDTH = 160; // set by cover_style + //COVER_HEIGHT = 225; // set by cover_style + // widescreen + CFG.W_CONSOLE_XCOORD = CONSOLE_XCOORD; + CFG.W_CONSOLE_YCOORD = CONSOLE_YCOORD; + CFG.W_CONSOLE_WIDTH = CONSOLE_WIDTH; + CFG.W_CONSOLE_HEIGHT = CONSOLE_HEIGHT; + CFG.W_COVER_XCOORD = COVER_XCOORD; + CFG.W_COVER_YCOORD = COVER_YCOORD; + //CFG.W_COVER_WIDTH = 0; // auto + //CFG.W_COVER_HEIGHT = 0; // auto + switch (CFG.layout) { + + case CFG_LAYOUT_ORIG: // 1.0+ + ENTRIES_PER_PAGE = 6; + MAX_CHARACTERS = 30; + CONSOLE_XCOORD = 260; + CONSOLE_YCOORD = 115; + CONSOLE_WIDTH = 340; + CONSOLE_HEIGHT = 218; + CONSOLE_FG_COLOR = 15; + CONSOLE_BG_COLOR = 0; + break; + + case CFG_LAYOUT_ORIG_12: // 1.2+ + ENTRIES_PER_PAGE = 12; + MAX_CHARACTERS = 30; + CONSOLE_XCOORD = 258; + CONSOLE_YCOORD = 112; + CONSOLE_WIDTH = 354; + CONSOLE_HEIGHT = 304; + CONSOLE_FG_COLOR = 15; + CONSOLE_BG_COLOR = 0; + COVER_XCOORD = 24; + COVER_YCOORD = 104; + break; + + case CFG_LAYOUT_SMALL: // same as 1.0 + use more space + ENTRIES_PER_PAGE = 9; + MAX_CHARACTERS = 38; + break; + + case CFG_LAYOUT_MEDIUM: + ENTRIES_PER_PAGE = 19; //17; + MAX_CHARACTERS = 38; + CONSOLE_YCOORD = 50; + CONSOLE_HEIGHT = 380; + COVER_XCOORD = 28; + COVER_YCOORD = 175; + break; + + // nixx: + case CFG_LAYOUT_LARGE: + ENTRIES_PER_PAGE = 21; + MAX_CHARACTERS = 38; + CONSOLE_YCOORD = 40; + CONSOLE_HEIGHT = 402; + COVER_XCOORD = 28; + COVER_YCOORD = 175; + break; + + // usptactical: + case CFG_LAYOUT_LARGE_2: + ENTRIES_PER_PAGE = 21; + MAX_CHARACTERS = 38; + CONSOLE_YCOORD = 40; + CONSOLE_HEIGHT = 402; + COVER_XCOORD = 30; + COVER_YCOORD = 180; + break; + + // oggzee + case CFG_LAYOUT_LARGE_3: + ENTRIES_PER_PAGE = 21; + MAX_CHARACTERS = 38; + CONSOLE_YCOORD = 40; + CONSOLE_WIDTH = 344; + CONSOLE_HEIGHT = 402; + COVER_XCOORD = 42; + COVER_YCOORD = 102; + CFG.W_CONSOLE_XCOORD = CONSOLE_XCOORD; + CFG.W_CONSOLE_YCOORD = CONSOLE_YCOORD; + CFG.W_CONSOLE_WIDTH = CONSOLE_WIDTH; + CFG.W_CONSOLE_HEIGHT = CONSOLE_HEIGHT; + CFG.W_COVER_XCOORD = COVER_XCOORD+14; + CFG.W_COVER_YCOORD = COVER_YCOORD; + break; + + // Ultimate1: (WiiShizza) white background + case CFG_LAYOUT_ULTIMATE1: + ENTRIES_PER_PAGE = 12; + MAX_CHARACTERS = 37; + CONSOLE_YCOORD = 30; + CONSOLE_HEIGHT = 290; + CONSOLE_FG_COLOR = 0; + CONSOLE_BG_COLOR = 15; + COVER_XCOORD = 28; + COVER_YCOORD = 105; + break; + + // Ultimate2: (jservs7 / hungyip84) + case CFG_LAYOUT_ULTIMATE2: + ENTRIES_PER_PAGE = 12; + MAX_CHARACTERS = 37; + CONSOLE_YCOORD = 30; + CONSOLE_HEIGHT = 290; + CONSOLE_FG_COLOR = 15; + CONSOLE_BG_COLOR = 0; + COVER_XCOORD = 28; + COVER_YCOORD = 105; + break; + + // Ultimate3: (WiiShizza) white background, covers on right + case CFG_LAYOUT_ULTIMATE3: + ENTRIES_PER_PAGE = 12; + MAX_CHARACTERS = 37; + CONSOLE_XCOORD = 40; + CONSOLE_YCOORD = 71; + CONSOLE_WIDTH = 344; + CONSOLE_HEIGHT = 290; + CONSOLE_FG_COLOR = 0; + CONSOLE_BG_COLOR = 15; + COVER_XCOORD = 446; + COVER_YCOORD = 109; + CFG.W_CONSOLE_XCOORD = CONSOLE_XCOORD; + CFG.W_CONSOLE_YCOORD = CONSOLE_YCOORD; + CFG.W_CONSOLE_WIDTH = CONSOLE_WIDTH; + CFG.W_CONSOLE_HEIGHT = CONSOLE_HEIGHT; + CFG.W_COVER_XCOORD = 482; + CFG.W_COVER_YCOORD = 110; + break; + + // Kosaic (modified U3) + case CFG_LAYOUT_KOSAIC: + ENTRIES_PER_PAGE = 12; + MAX_CHARACTERS = 37; + CONSOLE_XCOORD = 40; + CONSOLE_YCOORD = 71; + CONSOLE_WIDTH = 344; + CONSOLE_HEIGHT = 290; + CONSOLE_FG_COLOR = 15; + CONSOLE_BG_COLOR = 0; + COVER_XCOORD = 446; + COVER_YCOORD = 109; + CFG.W_CONSOLE_XCOORD = 34; + CFG.W_CONSOLE_YCOORD = 71; + CFG.W_CONSOLE_WIDTH = CONSOLE_WIDTH; + CFG.W_CONSOLE_HEIGHT = CONSOLE_HEIGHT; + CFG.W_COVER_XCOORD = 444; + CFG.W_COVER_YCOORD = 109; + break; + + } +} + + +void set_colors(int x) +{ + switch(x) { + case CFG_COLORS_MONO: + CFG.color_header = + CFG.color_inactive = + CFG.color_footer = + CFG.color_help = + CFG.color_selected_bg = CONSOLE_FG_COLOR; + CFG.color_selected_fg = CONSOLE_BG_COLOR; + break; + + case CFG_COLORS_DARK: + CFG.color_header = 14; + CFG.color_selected_fg = 11; + CFG.color_selected_bg = 12; + CFG.color_inactive = 8; + CFG.color_footer = 6; + CFG.color_help = 2; + break; + + case CFG_COLORS_BRIGHT: + CFG.color_header = 6; + CFG.color_selected_fg = 11; + CFG.color_selected_bg = 12; + CFG.color_inactive = 7; + CFG.color_footer = 4; + CFG.color_help = 2; + break; + } +} + +void cfg_set_covers_path() +{ + //STRCOPY(CFG.covers_path_2d, CFG.covers_path); + snprintf(D_S(CFG.covers_path_2d), "%s/%s", CFG.covers_path, "2d"); + snprintf(D_S(CFG.covers_path_3d), "%s/%s", CFG.covers_path, "3d"); + snprintf(D_S(CFG.covers_path_disc), "%s/%s", CFG.covers_path, "disc"); + snprintf(D_S(CFG.covers_path_full), "%s/%s", CFG.covers_path, "full"); + snprintf(D_S(CFG.covers_path_cache), "%s/%s", CFG.covers_path, "cache"); + CFG.covers_path_2d_set = 0; +} + +char *cfg_get_covers_path(int style) +{ + switch (style) { + default: + case CFG_COVER_STYLE_2D: + if (CFG.covers_path_2d_set) { + return CFG.covers_path_2d; + } else { + return CFG.covers_path; + } + + case CFG_COVER_STYLE_3D: + return CFG.covers_path_3d; + + case CFG_COVER_STYLE_DISC: + return CFG.covers_path_disc; + + case CFG_COVER_STYLE_FULL: + case CFG_COVER_STYLE_HQ: + return CFG.covers_path_full; + + case CFG_COVER_STYLE_CACHE: + return CFG.covers_path_cache; + } + return NULL; +} + + +void cfg_set_cover_style(int style) +{ + CFG.cover_style = style; + CFG.W_COVER_WIDTH = 0; // auto + CFG.W_COVER_HEIGHT = 0; // auto + switch (style) { + case CFG_COVER_STYLE_2D: + COVER_WIDTH = 160; + COVER_HEIGHT = 225; + break; + case CFG_COVER_STYLE_3D: + COVER_WIDTH = 160; + COVER_HEIGHT = 225; + break; + case CFG_COVER_STYLE_DISC: + COVER_WIDTH = 160; + COVER_HEIGHT = 160; + break; + case CFG_COVER_STYLE_FULL: + COVER_WIDTH = 512; + if (CFG.gui_compress_covers) + COVER_HEIGHT = 336; + else + COVER_HEIGHT = 340; + //Calculate the width of the front cover only, given the cover dimensions above. + // Divide the height by 1.4, which is the 2d cover height:width ratio (225x160 = 1.4) + // and then divide by 4 and multiply by 4 so it's divisible. This makes it easier if + // we want to change the fullcover dimensions in the future. + COVER_WIDTH_FRONT = (int)(COVER_HEIGHT / 1.4) >> 2 << 2; + COVER_HEIGHT_FRONT = COVER_HEIGHT; + break; + } +} + +void cfg_set_cover_style_str(char *val) +{ + if (strcmp(val, "standard")==0) { + cfg_set_cover_style(CFG_COVER_STYLE_2D); + } else if (strcmp(val, "3d")==0) { + cfg_set_cover_style(CFG_COVER_STYLE_3D); + } else if (strcmp(val, "disc")==0) { + cfg_set_cover_style(CFG_COVER_STYLE_DISC); + } else if (strcmp(val, "full")==0) { + cfg_set_cover_style(CFG_COVER_STYLE_FULL); + } +} + +// setup cover style: path & url +/* +void cfg_setup_cover_style() +{ + switch (CFG.cover_style) { + case CFG_COVER_STYLE_3D: + STRCOPY(CFG.covers_path, CFG.covers_path_3d); + STRCOPY(CFG.cover_url, CFG.cover_url_3d); + break; + case CFG_COVER_STYLE_DISC: + STRCOPY(CFG.covers_path, CFG.covers_path_disc); + STRCOPY(CFG.cover_url, CFG.cover_url_disc); + break; + case CFG_COVER_STYLE_FULL: + STRCOPY(CFG.covers_path, CFG.covers_path_full); + STRCOPY(CFG.cover_url, CFG.cover_url_full); + break; + default: + case CFG_COVER_STYLE_2D: + STRCOPY(CFG.covers_path, CFG.covers_path_2d); + STRCOPY(CFG.cover_url, CFG.cover_url_2d); + break; + } +} +*/ + +void cfg_default_url() +{ + CFG.download_id_len = 6; + CFG.download_all = 1; + //strcpy(CFG.download_cc_pal, "EN"); + *CFG.download_cc_pal = 0; // means AUTO - console language + + *CFG.cover_url_2d = 0; + *CFG.cover_url_3d = 0; + *CFG.cover_url_disc = 0; + *CFG.cover_url_full = 0; + *CFG.cover_url_hq = 0; + + STRCOPY(CFG.cover_url_2d, + " http://art.gametdb.com/wii/cover/{CC}/{ID6}.png" + //" http://www.wiiboxart.com/artwork/cover/{ID6}.png" + //" http://boxart.rowdyruff.net/flat/{ID6}.png" + //" http://www.muntrue.nl/covers/ALL/160/225/boxart/{ID6}.png" + //" http://wiicover.gateflorida.com/sites/default/files/cover/2D%20Cover/{ID6}.png" + //" http://awiibit.com/BoxArt160x224/{ID6}.png" + ); + + STRCOPY(CFG.cover_url_3d, + " http://art.gametdb.com/wii/cover3D/{CC}/{ID6}.png" + //" http://www.wiiboxart.com/artwork/cover3D/{ID6}.png" + //" http://boxart.rowdyruff.net/3d/{ID6}.png" + //" http://www.muntrue.nl/covers/ALL/160/225/3D/{ID6}.png" + //" http://wiicover.gateflorida.com/sites/default/files/cover/3D%20Cover/{ID6}.png" + //" http://awiibit.com/3dBoxArt176x248/{ID6}.png" + ); + + STRCOPY(CFG.cover_url_disc, + " http://art.gametdb.com/wii/disc/{CC}/{ID6}.png" + " http://art.gametdb.com/wii/disccustom/{CC}/{ID6}.png" + //" http://www.wiiboxart.com/artwork/disc/{ID6}.png" + //" http://www.wiiboxart.com/artwork/disccustom/{ID6}.png" + //" http://boxart.rowdyruff.net/fulldisc/{ID6}.png" + //" http://www.muntrue.nl/covers/ALL/160/160/disc/{ID6}.png" + //" http://wiicover.gateflorida.com/sites/default/files/cover/Disc%20Cover/{ID6}.png" + //" http://awiibit.com/WiiDiscArt/{ID6}.png" + ); + + STRCOPY(CFG.cover_url_full, + " http://art.gametdb.com/wii/coverfull/{CC}/{ID6}.png" + //" http://www.wiiboxart.com/artwork/coverfull/{ID6}.png" + //" http://www.muntrue.nl/covers/ALL/512/340/fullcover/{ID6}.png" + //" http://wiicover.gateflorida.com/sites/default/files/cover/Full%20Cover/{ID6}.png" + ); + + STRCOPY(CFG.cover_url_hq, + " http://art.gametdb.com/wii/coverfullHQ/{CC}/{ID6}.png" + ); + + STRCOPY(CFG.gamercard_url, + " http://www.wiinnertag.com/wiinnertag_scripts/update_sign.php?key={KEY}&game_id={ID6}" + " http://www.messageboardchampion.com/ncard/API/?cmd=tdbupdate&key={KEY}&game={ID6}" + " http://tag.darkumbra.net/{KEY}.update={ID6}" + ); + + *CFG.gamercard_key = 0; +} + + +/** + * Method that populates the coverflow global settings structure + * @return void + */ +void CFG_Default_Coverflow() +{ + CFG_cf_global.covers_3d = true; + CFG_cf_global.number_of_covers = 7; + CFG_cf_global.selected_cover = 1; + CFG_cf_global.theme = coverflow3d; // default to coverflow 3D + + CFG_cf_global.cover_back_xpos = 0; + CFG_cf_global.cover_back_ypos = 0; + CFG_cf_global.cover_back_zpos = -45; + CFG_cf_global.cover_back_xrot = 0; + CFG_cf_global.cover_back_yrot = 0; + CFG_cf_global.cover_back_zrot = 0; + CFG_cf_global.screen_fade_alpha = 210; +} + +/** + * Method that populates the coverflow theme settings structure + * @return void + */ +void CFG_Default_Coverflow_Themes() +{ + int i = 0; + + //allocate the structure array + //SAFE_FREE(CFG_cf_theme); + //CFG_cf_theme = malloc(5 * sizeof *CFG_cf_theme); + + // coverflow 3D + CFG_cf_theme[i].rotation_frames = 40; + CFG_cf_theme[i].rotation_frames_fast = 7; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + if (CFG.widescreen) + CFG_cf_theme[i].number_of_side_covers = 4; + else + CFG_cf_theme[i].number_of_side_covers = 3; + CFG_cf_theme[i].reflections_color_bottom = 0x666666FF; + CFG_cf_theme[i].reflections_color_top = 0xAAAAAA33; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = false; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 0; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 0; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -70; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = true; + CFG_cf_theme[i].cover_distance_from_center_left_x = -21; + CFG_cf_theme[i].cover_distance_from_center_left_y = 0; + CFG_cf_theme[i].cover_distance_from_center_left_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -10; + CFG_cf_theme[i].cover_distance_between_covers_left_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 0; + CFG_cf_theme[i].cover_left_xpos = 0; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -93; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 70; + CFG_cf_theme[i].cover_left_zrot = 0; + CFG_cf_theme[i].cover_distance_from_center_right_x = 21; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 10; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = 0; + CFG_cf_theme[i].cover_right_xpos = 0; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -93; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = -70; + CFG_cf_theme[i].cover_right_zrot = 0; + + + // coverflow 2D + i++; + CFG_cf_theme[i].rotation_frames = 40; + CFG_cf_theme[i].rotation_frames_fast = 7; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + if (CFG.widescreen) + CFG_cf_theme[i].number_of_side_covers = 4; + else + CFG_cf_theme[i].number_of_side_covers = 3; + CFG_cf_theme[i].reflections_color_bottom = 0x666666FF; + CFG_cf_theme[i].reflections_color_top = 0xAAAAAA33; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = false; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 0; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 0; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -70; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = true; + CFG_cf_theme[i].cover_distance_from_center_left_x = -36; + CFG_cf_theme[i].cover_distance_from_center_left_y = 0; + CFG_cf_theme[i].cover_distance_from_center_left_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -22; + CFG_cf_theme[i].cover_distance_between_covers_left_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 0; + CFG_cf_theme[i].cover_left_xpos = 0; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -167; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 0; + CFG_cf_theme[i].cover_left_zrot = 0; + CFG_cf_theme[i].cover_distance_from_center_right_x = 36; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 22; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = 0; + CFG_cf_theme[i].cover_right_xpos = 0; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -167; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = 0; + CFG_cf_theme[i].cover_right_zrot = 0; + + // frontrow + i++; + CFG_cf_theme[i].rotation_frames = 40; + CFG_cf_theme[i].rotation_frames_fast = 7; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + CFG_cf_theme[i].number_of_side_covers = 4; + CFG_cf_theme[i].reflections_color_bottom = 0x666666FF; + CFG_cf_theme[i].reflections_color_top = 0xAAAAAA33; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = false; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 0; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 0; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -70; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = true; + CFG_cf_theme[i].cover_distance_from_center_left_x = -27; + CFG_cf_theme[i].cover_distance_from_center_left_y = 0; + CFG_cf_theme[i].cover_distance_from_center_left_z = 47; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -35; + CFG_cf_theme[i].cover_distance_between_covers_left_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 80; + CFG_cf_theme[i].cover_left_xpos = 0; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -87; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 0; + CFG_cf_theme[i].cover_left_zrot = 0; + if (CFG.widescreen) { + CFG_cf_theme[i].cover_distance_from_center_right_x = 37; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = -30; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 45; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = -50; + } else { + CFG_cf_theme[i].cover_distance_from_center_right_x = 34; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = -37; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 50; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = -80; + } + CFG_cf_theme[i].cover_right_xpos = 0; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -87; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = 0; + CFG_cf_theme[i].cover_right_zrot = 0; + + // vertical + i++; + CFG_cf_theme[i].rotation_frames = 40; + CFG_cf_theme[i].rotation_frames_fast = 7; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + CFG_cf_theme[i].number_of_side_covers = 4; + CFG_cf_theme[i].reflections_color_bottom = 0xFFFFFF00; + CFG_cf_theme[i].reflections_color_top = 0xFFFFFF00; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = true; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 0; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 8; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -85; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = false; + CFG_cf_theme[i].cover_distance_from_center_left_x = 0; + CFG_cf_theme[i].cover_distance_from_center_left_y = -25; + CFG_cf_theme[i].cover_distance_from_center_left_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -25; + CFG_cf_theme[i].cover_distance_between_covers_left_y = -5; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 0; + CFG_cf_theme[i].cover_left_xpos = -15; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -130; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 0; + CFG_cf_theme[i].cover_left_zrot = 0; + CFG_cf_theme[i].cover_distance_from_center_right_x = 0; + CFG_cf_theme[i].cover_distance_from_center_right_y = 25; + CFG_cf_theme[i].cover_distance_from_center_right_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_x = -25; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 5; + CFG_cf_theme[i].cover_distance_between_covers_right_z = 0; + CFG_cf_theme[i].cover_right_xpos = -15; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -130; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = 0; + CFG_cf_theme[i].cover_right_zrot = 0; + + // carousel + i++; + CFG_cf_theme[i].rotation_frames = 40; + CFG_cf_theme[i].rotation_frames_fast = 7; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + CFG_cf_theme[i].number_of_side_covers = 4; + CFG_cf_theme[i].reflections_color_bottom = 0x666666FF; + CFG_cf_theme[i].reflections_color_top = 0xAAAAAA33; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = false; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 17; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 0; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -122; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = true; + CFG_cf_theme[i].cover_distance_from_center_left_x = -22; + CFG_cf_theme[i].cover_distance_from_center_left_y = 0; + CFG_cf_theme[i].cover_distance_from_center_left_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -22; + CFG_cf_theme[i].cover_distance_between_covers_left_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 0; + CFG_cf_theme[i].cover_left_xpos = 0; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -120; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 12; + CFG_cf_theme[i].cover_left_zrot = 0; + CFG_cf_theme[i].cover_distance_from_center_right_x = 22; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 22; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = 0; + CFG_cf_theme[i].cover_right_xpos = 0; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -120; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = -12; + CFG_cf_theme[i].cover_right_zrot = 0; +/* + // Bookshelf + i++; + CFG_cf_theme[i].rotation_frames = 17; + CFG_cf_theme[i].rotation_frames_fast = 6; + CFG_cf_theme[i].cam_pos_x = 0.0; + CFG_cf_theme[i].cam_pos_y = 0.0; + CFG_cf_theme[i].cam_pos_z = 0.0; + CFG_cf_theme[i].cam_look_x = 0.0; + CFG_cf_theme[i].cam_look_y = 0.0; + CFG_cf_theme[i].cam_look_z = -1.0; + if (CFG.widescreen) + CFG_cf_theme[i].number_of_side_covers = 5; + else + CFG_cf_theme[i].number_of_side_covers = 5; + CFG_cf_theme[i].reflections_color_bottom = 0x666666FF; + CFG_cf_theme[i].reflections_color_top = 0xAAAAAA33; + CFG_cf_theme[i].alpha_rolloff = 0; + CFG_cf_theme[i].floating_cover = false; + CFG_cf_theme[i].title_text_xpos = -1; + CFG_cf_theme[i].title_text_ypos = 434; + CFG_cf_theme[i].cover_rolloff_x = 0; + CFG_cf_theme[i].cover_rolloff_y = 0; + CFG_cf_theme[i].cover_rolloff_z = 0; + CFG_cf_theme[i].cover_center_xpos = 1; + CFG_cf_theme[i].cover_center_ypos = 0; + CFG_cf_theme[i].cover_center_zpos = -75; + CFG_cf_theme[i].cover_center_xrot = 0; + CFG_cf_theme[i].cover_center_yrot = 0; + CFG_cf_theme[i].cover_center_zrot = 0; + CFG_cf_theme[i].cover_center_reflection_used = true; + CFG_cf_theme[i].cover_distance_from_center_left_x = -13; + CFG_cf_theme[i].cover_distance_from_center_left_y = 0; + CFG_cf_theme[i].cover_distance_from_center_left_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_x = -2; + CFG_cf_theme[i].cover_distance_between_covers_left_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_left_z = 0; + CFG_cf_theme[i].cover_left_xpos = 0; + CFG_cf_theme[i].cover_left_ypos = 0; + CFG_cf_theme[i].cover_left_zpos = -75; + CFG_cf_theme[i].cover_left_xrot = 0; + CFG_cf_theme[i].cover_left_yrot = 90; + CFG_cf_theme[i].cover_left_zrot = 0; + CFG_cf_theme[i].cover_distance_from_center_right_x = 13; + CFG_cf_theme[i].cover_distance_from_center_right_y = 0; + CFG_cf_theme[i].cover_distance_from_center_right_z = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_x = 2; + CFG_cf_theme[i].cover_distance_between_covers_right_y = 0; + CFG_cf_theme[i].cover_distance_between_covers_right_z = 0; + CFG_cf_theme[i].cover_right_xpos = 0; + CFG_cf_theme[i].cover_right_ypos = 0; + CFG_cf_theme[i].cover_right_zpos = -75; + CFG_cf_theme[i].cover_right_xrot = 0; + CFG_cf_theme[i].cover_right_yrot = 90; + CFG_cf_theme[i].cover_right_zrot = 0; +*/ +} + + +// theme controls the looks, not behaviour + +void CFG_Default_Theme() +{ + CFG_Default_Coverflow_Themes(); + *CFG.theme = 0; + snprintf(D_S(CFG.background), "%s/%s", USBLOADER_PATH, "background.png"); + snprintf(D_S(CFG.w_background), "%s/%s", USBLOADER_PATH, "background_wide.png"); + snprintf(D_S(CFG.bg_gui), "%s/%s", USBLOADER_PATH, "bg_gui.png"); + STRCOPY(CFG.theme_path, USBLOADER_PATH); + *CFG.bg_gui_wide = 0; + CFG.covers = 1; + CFG.hide_header = 0; + CFG.hide_hddinfo = CFG_HIDE_HDDINFO; + CFG.hide_footer = 0; + CFG.console_transparent = 0; + //CFG.buttons = CFG_BTN_OPTIONS_1; + strcpy(CFG.cursor, ">>"); + strcpy(CFG.cursor_space, " "); + strcpy(CFG.menu_plus, "[+] "); + strcpy(CFG.menu_plus_s, " "); + strcpy(CFG.favorite, "*"); + strcpy(CFG.saved, "#"); + + CFG.gui_text_cfg.color = 0x000000FF; // black + CFG.gui_text_cfg.outline = 0xFF; //0; + CFG.gui_text_cfg.outline_auto = 1; //0; + CFG.gui_text_cfg.shadow = 0; + CFG.gui_text_cfg.shadow_auto = 0; + + CFG.gui_text2_cfg.color = 0xFFFFFFFF; // white + CFG.gui_text2_cfg.outline = 0xFF; + CFG.gui_text2_cfg.outline_auto = 1; + CFG.gui_text2_cfg.shadow = 0; + CFG.gui_text2_cfg.shadow_auto = 0; + + CFG.gui_tc[GUI_TC_MENU] = wgui_fc; + CFG.gui_tc[GUI_TC_TITLE] = + CFG.gui_tc[GUI_TC_BUTTON] = + CFG.gui_tc[GUI_TC_RADIO] = + CFG.gui_tc[GUI_TC_CHECKBOX] = + CFG.gui_tc[GUI_TC_MENU]; + CFG.gui_tc[GUI_TC_INFO] = text_fc; + CFG.gui_tc[GUI_TC_ABOUT] = about_fc; + CFG.gui_window_color[GUI_COLOR_BASE] = 0xFFFFFF80; + CFG.gui_window_color[GUI_COLOR_POPUP] = 0xFFFFFFB0; + memset(CFG.gui_button, 0, sizeof(CFG.gui_button)); + CFG.gui_bar = 1; + + CFG.gui_title_top = 0; + + //set cover and console postition defaults + CFG.layout = CFG_LAYOUT_LARGE_3; + cfg_layout(); + + //set cover height and width + cfg_set_cover_style(CFG_COVER_STYLE_2D); + set_colors(CFG_COLORS_DARK); + + //set theme preview image size and pos + CFG.theme_previewX = -1; + CFG.theme_previewY = -1; + CFG.theme_previewW = 0; + CFG.theme_previewH = 0; + CFG.w_theme_previewX = -1; + CFG.w_theme_previewY = -1; + CFG.w_theme_previewW = 0; + CFG.w_theme_previewH = 0; + + //set up button mappings + CFG.button_A = CFG_BTN_BOOT_GAME; + CFG.button_B = CFG_BTN_GUI; + CFG.button_1 = CFG_BTN_OPTIONS; + CFG.button_2 = CFG_BTN_FAVORITES; + CFG.button_H = CFG_BTN_REBOOT; + CFG.button_P = CFG_BTN_INSTALL; + CFG.button_M = CFG_BTN_MAIN_MENU; + CFG.button_X = CFG_BTN_2; + CFG.button_Y = CFG_BTN_1; + CFG.button_Z = CFG_BTN_B; + CFG.button_C = CFG_BTN_A; + CFG.button_L = CFG_BTN_M; + CFG.button_R = CFG_BTN_P; + CFG.home = CFG_HOME_REBOOT; + + CFG.button_gui = NUM_BUTTON_B; + CFG.button_opt = NUM_BUTTON_1; + CFG.button_fav = NUM_BUTTON_2; + + CFG.button_confirm.mask = WPAD_BUTTON_A; + CFG.button_confirm.num = NUM_BUTTON_A; + CFG.button_cancel.mask = WPAD_BUTTON_B; + CFG.button_cancel.num = NUM_BUTTON_B; + CFG.button_exit.mask = WPAD_BUTTON_HOME; + CFG.button_exit.num = NUM_BUTTON_HOME; + CFG.button_other.mask = WPAD_BUTTON_1; + CFG.button_other.num = NUM_BUTTON_1; + CFG.button_save.mask = WPAD_BUTTON_2; + CFG.button_save.num = NUM_BUTTON_2; + + memset(&CFG.gui_cover_area, 0, sizeof(CFG.gui_cover_area)); + memset(&CFG.gui_title_area, 0, sizeof(CFG.gui_title_area)); + CFG.gui_clock_pos.x = -1; + CFG.gui_page_pos.x = -1; +} + +void CFG_default_path() +{ + snprintf(D_S(CFG.covers_path), "%s/%s", USBLOADER_PATH, "covers"); + cfg_set_covers_path(); + CFG_Default_Theme(); + snprintf(D_S(CFG.bg_gui_wide), "%s/%s", USBLOADER_PATH, "bg_gui_wide.png"); +} + +void CFG_Default() +{ + memset(&CFG, 0, sizeof(CFG)); + + // set coverflow defaults + CFG_Default_Coverflow(); + + CFG_default_path(); + cfg_default_url(); + //STRCOPY(CFG.gui_font, "font.png"); + STRCOPY(CFG.gui_font, "font_uni.png"); + + //CFG.home = CFG_HOME_REBOOT; +#ifdef BUILD_DBG + CFG.debug = BUILD_DBG; + CFG.home = CFG_HOME_SCRSHOT; +#else + CFG.debug = 0; + CFG.home = CFG_HOME_REBOOT; +#endif + CFG.device = CFG_DEV_ASK; + STRCOPY(CFG.partition, CFG_DEFAULT_PARTITION); + CFG.confirm_start = 1; + CFG.install_partitions = CFG_INSTALL_GAME; //CFG_INSTALL_ALL; + CFG.disable_format = 0; + CFG.disable_remove = 0; + CFG.disable_install = 0; + CFG.disable_options = 0; + CFG.music = 1; + CFG.widescreen = CFG_WIDE_AUTO; + CFG.gui = CFG_GUI_START_WGUI; + CFG.gui_menu = 1; + CFG.gui_start = 1; + CFG.gui_rows = 2; + CFG.admin_lock = 1; + CFG.admin_mode_locked = 1; + STRCOPY(CFG.unlock_password, CFG_UNLOCK_PASSWORD); + CFG.gui_antialias = 4; + CFG.gui_compress_covers = 1; + CFG.gui_pointer_scroll = 1; + // default game settings + CFG.game.video = CFG_VIDEO_AUTO; + CFG.game.hooktype = 1; // VBI + CFG.game.wide_screen = 0; // WIDE IS OFF + CFG.game.ntsc_j_patch = 0; + CFG.game.channel_boot = 0; + CFG.game.nodisc = 0; + CFG.game.screenshot = 0; + CFG.game.block_ios_reload = 2; // 2=auto + CFG.game.alt_controller_cfg = 0; + cfg_ios_set_idx(DEFAULT_IOS_IDX); + // all other game settings are 0 (memset(0) above) + STRCOPY(CFG.sort_ignore, "A,An,The"); + CFG.clock_style = 24; + // profiles + CFG.num_profiles = 1; + //CFG.current_profile = 0; + STRCOPY(CFG.profile_names[0], "default"); + STRCOPY(CFG.titles_url, "http://www.gametdb.com/titles.txt?LANG={DBL}"); + CFG.intro = 4; + CFG.fat_install_dir = 1; + CFG.fat_split_size = 4; + CFG.db_show_info = 1; + //CFG.db_ignore_titles = 0; + //CFG.write_playlog = 0; + CFG.write_playstats = 1; + STRCOPY(CFG.db_url, "http://www.gametdb.com/wiitdb.zip?LANG={DBL}&FALLBACK=true&GAMECUBE=true&WIIWARE=true"); + STRCOPY(CFG.db_language, auto_cc()); + STRCOPY(CFG.translation, getLang(CONF_GetLanguage())); + STRCOPY(CFG.sort, "title-asc"); + CFG.delay_patch = 1; + CFG.theme_previews = 1; + // dvd slot check is handled properly now by all cios + // so the patch is disabled by default + CFG.disable_dvd_patch = 1; + //CFG.dml = CFG_DM_2_2; + STRCOPY(CFG.nand_emu_path, "usb:/nand"); + STRCOPY(CFG.wbfs_fat_dir, "/wbfs"); + CFG.game.nand_emu = 0; +} + +bool map_auto_token(char *name, char *name2, char *val, struct TextMap *map, struct MenuButton *var) +{ + int single, all; + char *next; + char buf[12]; + if (strcmp(name, name2) != 0) return 0; + next = val; + all = 0; + while ((next = split_tokens(buf, next, ",", 12))) + { + int res; + buf[1]=0; + res = map_auto_i(name, name2, buf, map, &single); + if (res >= 0 && !all) var->num = single; + if (res >= 0) all |= buttonmap[MASTER][single]; + } + if (all) { + var->mask = all; + return 1; + } else return 0; +} + +bool cfg_map_auto_token(char *name, struct TextMap *map, struct MenuButton *var) +{ + return map_auto_token(name, cfg_name, cfg_val, map, var); +} + +u32 hash_id4(void *cb, void *id) +{ + // id4 is usually unique, except for customs + return hash_string_n(id, 4); +} + +u32 hash_id6(void *cb, void *id) +{ + return hash_string_n(id, 6); +} + +bool title_cmp_id6(void *cb, void *key, int i) +{ + if (i < 0 || i >= num_title) return false; + return strncmp((char*)cfg_title[i].id, (char*)key, 6) == 0; +} + +bool title_cmp_id4(void *cb, void *key, int i) +{ + if (i < 0 || i >= num_title) return false; + return strncmp((char*)cfg_title[i].id, (char*)key, 4) == 0; +} + +int* title_get_hnext(void *cb, int i) +{ + if (i < 0 || i >= num_title) return NULL; + return &cfg_title[i].hnext; +} + +struct ID_Title* cfg_get_id_title(u8 *id) +{ + // check ID6 first + int i = hash_get(&title_hash_id6, id); + if (i < 0) { + // if not found try ID4 + i = hash_get(&title_hash_id4, id); + } + if (i >= 0 && i < num_title) return &cfg_title[i]; + return NULL; +} + +char *cfg_get_title(u8 *id) +{ + // titles.txt + struct ID_Title *idt = cfg_get_id_title(id); + if (idt) return idt->title; + // wiitdb + if (!CFG.db_ignore_titles) { + gameXMLinfo *g = get_game_info_id(id); + if (!g) return NULL; + if (g->title[0] != 0) return g->title; + } + return NULL; +} + +char *get_title(struct discHdr *header) +{ + // titles.txt or wiitdb + char *title; + if ((memcmp("G",(char*)header->id,1)==0) && (strlen((char*)header->id)>6)) + title = header->title; + else + title = cfg_get_title(header->id); + if (title) return title; + // disc header + return header->title; +} + +void title_set(char *id, char *title) +{ + struct ID_Title *idt = cfg_get_id_title((u8*)id); + if (idt && strncmp(id, (char*)idt->id, 6) == 0) { + // replace + mbs_copy(idt->title, title, TITLE_MAX); + } else { + cfg_title = mem1_realloc(cfg_title, (num_title+1) * sizeof(struct ID_Title)); + if (!cfg_title) { + // error + num_title = 0; + return; + } + // add + memset(&cfg_title[num_title], 0, sizeof(cfg_title[num_title])); + strcopy((char*)cfg_title[num_title].id, id, 7); + mbs_copy(cfg_title[num_title].title, title, TITLE_MAX); + num_title++; + // update hash + if (id[3] == 0 || id[4] == 0) { + // id3 or id4 + hash_check_init(&title_hash_id4, 0, NULL, &hash_id4, &title_cmp_id4, &title_get_hnext); + hash_add(&title_hash_id4, id, num_title - 1); + } else { + // id6 + hash_check_init(&title_hash_id6, 0, NULL, &hash_id6, &title_cmp_id6, &title_get_hnext); + hash_add(&title_hash_id6, id, num_title - 1); + } + } +} + + +#define COPY_PATH(D,S) copy_path(D,S,sizeof(D)) + +void copy_path(char *dest, char *val, int size) +{ + if (strchr(val, ':')) { + // absolute path with drive (sd:/images/...) + strcopy(dest, val, size); + } else if (val[0] == '/') { + // absolute path without drive (/images/...) + snprintf(dest, size, "%s%s", FAT_DRIVE, val); + } else { + snprintf(dest, size, "%s/%s", USBLOADER_PATH, val); + } +} + +bool copy_theme_path(char *dest, char *val, int size) +{ + // check if it's an absolute path (contains : as in sd:/...) + if (strchr(val, ':')) { + // absolute path with drive (sd:/images/...) + strcopy(dest, val, size); + return true; + } else if (val[0] == '/') { + // absolute path without drive (/images/...) + snprintf(dest, size, "%s%s", FAT_DRIVE, val); + return true; + } else if (*theme_path) { + struct stat st; + snprintf(dest, size, "%s/%s", theme_path, val); + if (stat(dest, &st) == 0) return true; + } + snprintf(dest, size, "%s/%s", USBLOADER_PATH, val); + return false; +} + +int find_in_hdr_list(char *id, struct discHdr *list, int num) +{ + int i; + for (i=0; i= maxn) return false; + // add + memset(list[*num], 0, sizeof(list[0])); + memcpy(list[*num], id, 4); + *num += 1; + return true; +} + +bool is_in_game_list(u8 *id, char list[][8], int num) +{ + int i = find_in_game_list(id, list, num); + return (i >= 0); +} + +bool is_in_hide_list(struct discHdr *game) +{ + return is_in_game_list(game->id, CFG.hide_game, CFG.num_hide_game); +} + +void cfg_id_list(char *name, char list[][8], int *num_list, int max_list) +{ + int i; + + if (strcmp(name, cfg_name)==0) { + char id[8], *next = cfg_val; + while (next) { + *id = 0; + next = split_token(id, next, ',', sizeof(id)); + if (strcmp(id, "0")==0) { + // reset list + *num_list = 0; + } + if (strlen(id) == 4) { + if (*num_list >= max_list) continue; + // add id to hide list. + + i = find_in_game_list((u8*)id, list, *num_list); + if (i < 0) { + strcopy(list[*num_list], id, 8); + (*num_list)++; + } + } + } + } +} + +bool cfg_color(char *name, u32 *var) +{ + if (cfg_map(name, "black", (int*)var, 0x000000FF)) return true; + if (cfg_map(name, "white", (int*)var, 0xFFFFFFFF)) return true; + if (cfg_int_hex(name, (int*)var)) return true; + return false; +} + +// text color +void font_color_cfg_set(char *base_name, struct FontColor_CFG *fc) +{ + if (strncmp(cfg_name, base_name, strlen(base_name)) != 0) return; + + char name[100]; + char *old_name; + STRCOPY(name, cfg_name); + str_replace(name, base_name, "gui_text_", sizeof(name)); + old_name = cfg_name; + cfg_name = name; + + cfg_color("gui_text_color", &fc->color); + + if (cfg_color("gui_text_outline", &fc->outline)) { + if (strlen(cfg_val) == 2) fc->outline_auto = 1; + else fc->outline_auto = 0; + } + + if (cfg_color("gui_text_shadow", &fc->shadow)) { + if (strlen(cfg_val) == 2) fc->shadow_auto = 1; + else fc->shadow_auto = 0; + } + + cfg_name = old_name; +} + +bool font_color_set(char *name, struct FontColor *fc) +{ + if (strcmp(name, cfg_name)) return false; + char color_val[16]; + char *old_val = cfg_val; + char *next = cfg_val; + u32 *c = &fc->color; + int i = 0; + memset(fc, 0, sizeof(*fc)); + while ((next = split_tokens(color_val, next, "/", sizeof(color_val)))) { + cfg_val = color_val; + if (!cfg_color(name, c+i)) break; + i++; + if (i >= 3) break; + } + cfg_val = old_val; + return true; +} + +int get_outline_color(int text_color, int outline_color, int outline_auto) +{ + unsigned color, alpha; + if (!outline_color) return 0; + + if (outline_auto) { + // only alpha specified + int avg; + avg = ( ((text_color>>8) & 0xFF) + + ((text_color>>16) & 0xFF) + + ((text_color>>24) & 0xFF) ) / 3; + if (avg > 0x80) { + color = 0x00000000; + } else { + color = 0xFFFFFF00; + } + } else { + // full color specified + color = outline_color; + } + // apply text alpha to outline alpha + alpha = (outline_color & 0xFF) * (text_color & 0xFF) / 0xFF; + color = (color & 0xFFFFFF00) | alpha; + return color; +} + +void expand_font_color_cfg(FontColor *fc, FontColor_CFG *fcc) +{ + fc->color = fcc->color; + fc->outline = get_outline_color( fcc->color, fcc->outline, fcc->outline_auto); + fc->shadow = get_outline_color( fcc->color, fcc->shadow, fcc->shadow_auto); +} + + +int cfg_pos_xy(char *name, struct PosCoords *pos) +{ + if (strcmp(name, cfg_name)) return 0; + int i, x, y; + pos->x = -1; + pos->y = -1; + i = sscanf(cfg_val, "%d,%d", &x, &y); + if (i == 2) { + // check for valid range + if (x >= 0 && y >= 0 && x < 640 && y < 480) { + pos->x = x; + pos->y = y; + return 1; + } + } + return -1; +} + +int cfg_pos_area(char *name, struct RectCoords *pos, int min_w, int min_h) +{ + if (strcmp(name, cfg_name)) return 0; + int i, x, y, w, h; + memset(pos, 0, sizeof(*pos)); + i = sscanf(cfg_val, "%d,%d,%d,%d", &x, &y, &w, &h); + if (i == 4) { + // check for valid range + // min w * h : 480 * 320 + if (x >= 0 && y >= 0 && w >= min_w && h >= min_h && x+w <= 640 && y+h <= 480) { + pos->x = x; + pos->y = y; + pos->w = w / 2 * 2; + pos->h = h / 2 * 2; + return 1; + } + } + return -1; +} + +char *custom_button_name(int i); + +char *strtolower(char *s) +{ + char *ss = s; + int c; + while (*ss) { + c = (unsigned char)*ss; + *ss = tolower(c); + ss++; + } + return s; +} + +// n starts at 0 +char* get_token_n(char *dest, int size, char *src, char *delim, int n) +{ + int i; + char *next = src; + *dest = 0; + for (i=0; i<=n; i++) { + next = split_tokens(dest, next, delim, size); + if (!next) break; + } + //dbg_printf("==== tok %d : %s.\n", n, dest); + return next; +} + +bool cfg_gui_button(int i) +{ + int ret; + char token[64]; + char *save_val = cfg_val; + struct CfgButton *bb = &CFG.gui_button[i]; + memset(bb, 0, sizeof(*bb)); + ret = cfg_pos_area(cfg_name, &bb->pos, 4, 4); + if (ret <= 0) return false; + bb->enabled = 1; + // defaults: + CFG.gui_tc[GUI_TC_CBUTTON + i] = wgui_fc; + bb->hover_zoom = 10; + if (get_token_n(token, sizeof(token), cfg_val, ",", 4)) { + cfg_val = token; + font_color_set(cfg_name, &CFG.gui_tc[GUI_TC_CBUTTON + i]); + cfg_val = save_val; + } + if (get_token_n(token, sizeof(token), cfg_val, ",", 5)) { + STRCOPY(bb->image, token); + } + if (get_token_n(token, sizeof(token), cfg_val, ",", 6)) { + cfg_val = token; + cfg_map(cfg_name, "button", &bb->type, 0); + cfg_map(cfg_name, "icon", &bb->type, 1); + cfg_val = save_val; + } + if (get_token_n(token, sizeof(token), cfg_val, ",", 7)) { + cfg_val = token; + cfg_int_max(cfg_name, &bb->hover_zoom, 50); + cfg_val = save_val; + } + return true; +} + +bool cfg_gui_custom_buttons() +{ + int i; + // gui_button_main + // gui_button_quit ... + char *opt = "gui_button_"; + int len = strlen(opt); + char *name = cfg_name + len; + char bname[32]; + if (strncmp(cfg_name, opt, len) == 0) { + for (i=0; i 0) { + if (i == 1) bot = top; + for (i=0; i CFG_IOS_MAX) return false; + if (i != id) return false; // safety check for correct defines + *idx = i; + return true; +} + +void cfg_ios(char *name, char *val) +{ + int i; + if (!cfg_ios_idx(name, val, &i)) return; + cfg_ios_set_idx(i); + /* + int ios; + if (sscanf(val, "%d", &ios) != 1) return; + CFG.game.ios_idx = i; + CFG.ios = ios; + CFG.ios_yal = 0; + CFG.ios_mload = 0; + if (strstr(val, "-yal")) { + CFG.ios_yal = 1; + } + if (strstr(val, "-mload")) { + CFG.ios_yal = 1; + CFG.ios_mload = 1; + } + */ +} + +bool is_ios_idx_mload(int ios_idx) +{ + switch (ios_idx) { + case CFG_IOS_222_MLOAD: + case CFG_IOS_223_MLOAD: + case CFG_IOS_224_MLOAD: + return true; + } + return false; +} + +int get_ios_idx_type(int ios_idx) +{ + switch (ios_idx) { + case CFG_IOS_245: + case CFG_IOS_246: + case CFG_IOS_247: + case CFG_IOS_248: + case CFG_IOS_249: + case CFG_IOS_250: + case CFG_IOS_251: + return IOS_TYPE_WANIN; + case CFG_IOS_222_MLOAD: + case CFG_IOS_223_MLOAD: + case CFG_IOS_224_MLOAD: + return IOS_TYPE_HERMES; + case CFG_IOS_222_YAL: + case CFG_IOS_223_YAL: + return IOS_TYPE_KWIIRK; + } + return IOS_TYPE_UNK; +} + + + +void cfg_set_game(char *name, char *val, struct Game_CFG *game_cfg) +{ + cfg_name = name; + cfg_val = val; + + cfg_map_auto("language", map_language, &game_cfg->language); + + cfg_map_auto("video", map_video, &game_cfg->video); + if (strcmp("video", name) == 0 && strcmp("vidtv", val) == 0) + { + game_cfg->video = CFG_VIDEO_AUTO; + game_cfg->vidtv = 1; + } + if (strcmp("video", name) == 0 && strcmp("patch", val) == 0) + { + game_cfg->video = CFG_VIDEO_AUTO; + game_cfg->video_patch = 1; + } + + cfg_map_auto("video_patch", map_video_patch, &game_cfg->video_patch); + cfg_map_auto("nand_emu", map_nand_emu, &game_cfg->nand_emu); + cfg_map_auto("channel_boot", map_boot_method, &game_cfg->channel_boot); + + cfg_bool("vidtv", &game_cfg->vidtv); + cfg_bool("country_patch", &game_cfg->country_patch); + cfg_bool("clear_patches", &game_cfg->clean); + cfg_map("clear_patches", "all", &game_cfg->clean, CFG_CLEAN_ALL); + cfg_bool("fix_002", &game_cfg->fix_002); + cfg_bool("wide_screen", &game_cfg->wide_screen); + cfg_bool("ntsc_j_patch", &game_cfg->ntsc_j_patch); + cfg_bool("nodisc", &game_cfg->nodisc); + cfg_bool("screenshot", &game_cfg->screenshot); + cfg_ios_idx(name, val, &game_cfg->ios_idx); + cfg_bool("block_ios_reload", &game_cfg->block_ios_reload); + cfg_map("block_ios_reload", "auto", &game_cfg->block_ios_reload, 2); + cfg_bool("alt_controller_cfg", &game_cfg->alt_controller_cfg); + if (strcmp("alt_dol", name) == 0) { + int set = 0; + if (cfg_bool("alt_dol", &game_cfg->alt_dol)) set = 1; + if (cfg_map ("alt_dol", "sd", &game_cfg->alt_dol, 1)) set = 1; + if (cfg_map ("alt_dol", "disc", &game_cfg->alt_dol, 2)) set = 1; + if (cfg_int_max("alt_dol", &game_cfg->alt_dol, 100)) set = 1; + if (!set) { + // name specified + game_cfg->alt_dol = 2; + STRCOPY(game_cfg->dol_name, val); + } + } + if (strcmp("dol_name", name) == 0 && *val) { + STRCOPY(game_cfg->dol_name, val); + } + cfg_bool("ocarina", &game_cfg->ocarina); + cfg_map_auto("hooktype", map_hook, &game_cfg->hooktype); + cfg_int_max("write_playlog", &game_cfg->write_playlog, 3); + +} + +bool cfg_set_gbl(char *name, char *val) +{ + if (cfg_map("device", "ask", &CFG.device, CFG_DEV_ASK)) return true; + if (cfg_map("device", "usb", &CFG.device, CFG_DEV_USB)) return true; + if (cfg_map("device", "sdhc", &CFG.device, CFG_DEV_SDHC)) return true; + + CFG_STR("partition", CFG.partition); + + if (cfg_map_auto("gui_style", map_gui_style, &CFG.gui_style)) return true; + //if (cfg_int_max("dml", &CFG.dml, 6)) return true; + cfg_int_max("devo", &CFG.default_gc_loader,2); + cfg_bool("old_button_color", &CFG.old_button_color); + + int rows = 0; + if (cfg_int_max("gui_rows", &rows, 4)) { + if (rows > 0) { + CFG.gui_rows = rows; + return true; + } + } + CFG_STR("profile", CFG.current_profile_name); + + // theme must be last, because it changes cfg_name, cfg_val + if (strcmp(name, "theme")==0) { + if (*val) { + load_theme(val); + int i; + for (i=0; i 1) CFG.gui_compress_covers = 1; + cfg_bool("gui_pointer_scroll", &CFG.gui_pointer_scroll); + + if (cfg_set_gbl(name, val)) { + return; + } + + cfg_set_game(name, val, &CFG.game); + if (!CFG.direct_launch) { + *CFG.game.dol_name = 0; + } + + // covers path + if (strcmp(name, "covers_path")==0) { + COPY_PATH(CFG.covers_path, val); + cfg_set_covers_path(); + } + if (strcmp(name, "covers_path_2d")==0) { + COPY_PATH(CFG.covers_path_2d, val); + CFG.covers_path_2d_set = 1; + } + if (strcmp(name, "covers_path_3d")==0) { + COPY_PATH(CFG.covers_path_3d, val); + } + if (strcmp(name, "covers_path_disc")==0) { + COPY_PATH(CFG.covers_path_disc, val); + } + if (strcmp(name, "covers_path_full")==0) { + COPY_PATH(CFG.covers_path_full, val); + } + + CFG_STR_LIST("gamercard_url", CFG.gamercard_url); + CFG_STR_LIST("gamercard_key", CFG.gamercard_key); + + // urls + CFG_STR("titles_url", CFG.titles_url); + CFG_STR("nand_emu_path", CFG.nand_emu_path); + CFG_STR("wbfs_fat_dir", CFG.wbfs_fat_dir); + CFG_STR_LIST("cover_url", CFG.cover_url_2d); + CFG_STR_LIST("cover_url_3d", CFG.cover_url_3d); + CFG_STR_LIST("cover_url_disc", CFG.cover_url_disc); + CFG_STR_LIST("cover_url_full", CFG.cover_url_full); + CFG_STR_LIST("cover_url_hq", CFG.cover_url_hq); + // download options + cfg_bool("download_all_styles", &CFG.download_all); + cfg_map("download_id_len", "4", &CFG.download_id_len, 4); + cfg_map("download_id_len", "6", &CFG.download_id_len, 6); + if (strcmp("download_cc_pal", name) == 0) { + if (strcmp(val, "auto") == 0 || strcmp(val, "AUTO") == 0) { + *CFG.download_cc_pal = 0; + } else if (strlen(val) == 2) { + strcpy(CFG.download_cc_pal, val); + } + } + + // gui + if (cfg_int_max("gui", &CFG.gui, CFG_GUI_START_WGUI) || + cfg_map("gui", "start", &CFG.gui, CFG_GUI_START_WGUI)) + { + if (CFG.gui == CFG_GUI_START || CFG.gui == CFG_GUI_START_WGUI) { + CFG.gui_start = 1; + } else { + CFG.gui_start = 0; + } + if (CFG.gui > CFG_GUI_START) { + CFG.gui_menu = 1; + } else { + CFG.gui_menu = 0; + } + } + + cfg_map("gui_transition", "scroll", &CFG.gui_transit, 0); + cfg_map("gui_transition", "fade", &CFG.gui_transit, 1); + CFG_STR("gui_font", CFG.gui_font); + cfg_bool("gui_lock", &CFG.gui_lock); + + // simple changes dependant options + int simpl; + if (cfg_bool("simple", &simpl)) { + if (simpl == 1) { + CFG.confirm_start = 0; + //if (CFG_HIDE_HDDINFO == 0) { + // // normal version affects hddinfo + // // fat version does not change it. + // CFG.hide_hddinfo = 1; + //} + CFG.hide_footer = 1; + CFG.disable_remove = 1; + CFG.disable_install = 1; + CFG.disable_options = 1; + CFG.disable_format = 1; + } else { // simple == 0 + CFG.confirm_start = 1; + //if (CFG_HIDE_HDDINFO == 0) { + // // normal version affects hddinfo + // // fat version does not change it. + // CFG.hide_hddinfo = 0; + //} + CFG.hide_footer = 0; + CFG.disable_remove = 0; + CFG.disable_install = 0; + CFG.disable_options = 0; + CFG.disable_format = 0; + } + } + cfg_map("install_partitions", "only_game", + &CFG.install_partitions, CFG_INSTALL_GAME); + cfg_map("install_partitions", "all", + &CFG.install_partitions, CFG_INSTALL_ALL); + cfg_map("install_partitions", "1:1", + &CFG.install_partitions, CFG_INSTALL_1_1); + cfg_map("install_partitions", "iso", + &CFG.install_partitions, CFG_INSTALL_ISO); + + cfg_int_max("fat_split_size", &CFG.fat_split_size, 4); + + //cfg_bool("write_playlog", &CFG.write_playlog); + + cfg_int_max("fat_install_dir", &CFG.fat_install_dir, 3); + cfg_int_max("fs_install_layout", &CFG.fat_install_dir, 3); + + cfg_bool("ntfs_write", &CFG.ntfs_write); + cfg_map("ntfs_write", "norecover", &CFG.ntfs_write, 2); + + cfg_bool("disable_nsmb_patch", &CFG.disable_nsmb_patch); + cfg_bool("disable_pop_patch", &CFG.disable_pop_patch); + cfg_bool("disable_dvd_patch", &CFG.disable_dvd_patch); + cfg_bool("disable_wip", &CFG.disable_wip); + cfg_bool("disable_bca", &CFG.disable_bca); + + //cfg_int_max("dml", &CFG.dml, 6); + cfg_int_max("devo", &CFG.default_gc_loader,2); + cfg_bool("old_button_color", &CFG.old_button_color); + + cfg_id_list("hide_game", CFG.hide_game, &CFG.num_hide_game, MAX_HIDE_GAME); + cfg_id_list("pref_game", CFG.pref_game, &CFG.num_pref_game, MAX_PREF_GAME); + cfg_id_list("favorite_game", CFG.favorite_game, + &CFG.num_favorite_game, MAX_FAVORITE_GAME); + CFG_STR("sort_ignore", CFG.sort_ignore); + + cfg_map("clock_style", "0", &CFG.clock_style, 0); + cfg_map("clock_style", "24", &CFG.clock_style, 24); + cfg_map("clock_style", "12", &CFG.clock_style, 12); + cfg_map("clock_style", "12am", &CFG.clock_style, 13); + + if (strcmp(name, "music")==0) { + if (cfg_bool("music", &CFG.music)) { + *CFG.music_file = 0; + } else { + COPY_PATH(CFG.music_file, val); + } + } + + cfg_bool("delay_patch", &CFG.delay_patch); + + if (strncmp(name, "title:", 6)==0) { + char id[8]; + trimcopy(id, name+6, sizeof(id)); + title_set(id, val); + } + if (strncmp(name, "game:", 5)==0) { + game_set(name, val); + } + + if (strcmp(name, "profile_names")==0) { + char *next; + next = val; + CFG.num_profiles = 0; + while ((next = split_tokens( + CFG.profile_names[CFG.num_profiles], + next, ",", sizeof(CFG.profile_names[0])) + )) + { + if (*CFG.profile_names[CFG.num_profiles] == 0) break; + CFG.num_profiles++; + if (CFG.num_profiles >= MAX_PROFILES) break; + } + if (CFG.num_profiles == 0) { + CFG.num_profiles = 1; + STRCOPY(CFG.profile_names[0], "default"); + } + } + + if (strcmp(name, "profile_start_favorites")==0) { + int i; + char token[25]; + for (i=0; i= 0) { + // alerady on the list + if (fav) return true; + // remove + remove_from_list(i, CFG.favorite_game, &CFG.num_favorite_game); + return CFG_Save_Settings(0); + } + // not on list + if (!fav) return true; + // add + ret = add_to_list(id, CFG.favorite_game, &CFG.num_favorite_game, MAX_FAVORITE_GAME); + if (!ret) return ret; + return CFG_Save_Settings(0); +} + +int CFG_filter_favorite(struct discHdr *list, int cnt) +{ + int i; + int kept_cnt = 0; + //printf("f filter %p %d\n", list, cnt); sleep(2); + for (i=0; i= 0) { + // already in the list + if (hide) return true; + // remove + remove_from_list(i, CFG.hide_game, &CFG.num_hide_game); + return CFG_Save_Settings(0); + } + // not on list + if (!hide) return true; + // add + ret = add_to_list(id, CFG.hide_game, &CFG.num_hide_game, MAX_HIDE_GAME); + if (!ret) return ret; + return CFG_Save_Settings(0); +} + +void cfg_parsearg(int argc, char **argv) +{ + int i; + char pathname[200]; + bool is_opt; + for (i=1; i= 0 && CFG.saved_device <= 2) { + SAVE_OPT("device = %s\n", dev_str[CFG.saved_device]); + } + SAVE_OPT("partition = %s\n", CFG.saved_partition); + char *s = map_get_name(map_gui_style, CFG.saved_gui_style); + if (s) { + SAVE_OPT("gui_style = %s\n", s); + } + if (CFG.saved_gui_rows > 0 && CFG.saved_gui_rows <= 4 ) { + SAVE_OPT("gui_rows = %d\n", CFG.saved_gui_rows); + } + //SAVE_OPT("dml = %d\n", CFG.dml); + SAVE_OPT("devo = %d\n", CFG.default_gc_loader); + SAVE_OPT("old_button_color = %d", CFG.old_button_color); + } + + fprintf(f, "\n# Profiles: %d\n", CFG.num_profiles); + int save_prof = CFG.current_profile; + for (j=0; j

    kkzWYN*s|dQhW&lkDzb}n@JaLKDT9qey{f>)q^pj$8e2tv6 zkcjc&Swoamm-_R=?xaE#1L((9i@zVb+AoybTX}%bU;O8H?BSwY&I3mUzXh-1(;<3& z!S^llB_9FfAw7jvQxtNKffXa_ZW(`bzdR98t!Ir8cMYT8UgMTezIOAhr3@ma(R9B- z-7?E=!!QGV546qa?6jZKhs9;q)9(#bER>(-PzrH zzjbd+8O`E?%S-+aGrMicex%N`#j5i!(Yj=CtIbTJSi-(^f2KMbPdj*qs%N)1X}|yC zcC7aWsg7n9Ihb?APC2CL-0%B)(<~BC$6+ia7~BfYcP`Q{HntCP?1;ly5-E$=vh&Vb z_wV-?jab8JBS3pri|Vs0B~U`^q%nxQ?tLv6Wk#ftg~Fhrks2?=RThWcZ1}^TamV@( zL%iIi9FfMG>z%w>{aMdh*g%ZJ{QyjjuNmwrd45_l3cu=+hx~RWoh65p6i0knwWLyu zOtPouAGT5m|5NS>z4$~)rTH}A?%Y3*9@6KcfZB);yb22$HVW7~=XYqh3%q%9;x3M5 zrF7JEFSjDkBCZ=%;eD>qw9jw~3Z8ONVPmX;zIRvwA=H$WA~5;4C=NtGYtX6WHG80e znT&M8 zRGPoLPWpq!)WP#8(v!z;l&5P8FYEg<+&rEPQz4eo;;~8fl>sYgCF)U{U8f zyl@{bVW-pQ6RwlR!`%Zm?HYXIuyl4O`uNC?_;4MRoV9wE=@!f5@*F$iu!xi*h)&N# zY%s4Z7V=86t_|W(+0&*`TYgigVS6P&hBE*o3@tOMmtyfYB)VipSf zZQlv6UCR9va-$uVi8JyrnF~_7$PHea!uuM*26{`q=i94vF8F!C*tCIhgl%VWYA1h$ z1*B5XFHbCuxzXFBsGJ(#(}P;@O^>;F_4gY@8HW94Z75?lV}&=GPMLG`{Gw?iVW7w@vHQVZrQZ&b|RBnO{kYT?gqF1bP84fFpZ)2bh z4S$CGWAAIc(x}jLiIT~O>jSRXYThWnXR+lg-A7*BU!5uD>8Y>v(Lg_Hi8W9=Mch!g?^fzWj!;(k zTpA#tQ%}fW`wBIRVH?KytINy0#4df8oI%=2%ekj^uetk=?MCxh$*ZZ4y@?z3`IKer zb)Q;S05uJ2H(iDQ7dQgP{nNzsw|3Og&IuUF-mEwmL~1UBIQJ%TUu&t=xouW(6Npsj z{7!*LTsrT@1qOtWYt031(mP+n`&Qyj8K|wq(Be+D@>;?n+cdgO4av4{$JPar z?Pb`uf~Ym+{&jjV=5wJ7G!}f{)YAm#f@XeF>M?z+Zkk5X`V&GX;7u8*&fP_kzD=HJ zk4!O*cWf4Ax7db^f-~pDxhNvJ47KJEm+wcBsV^lvADj;u066m?%2#xzor772ZT4cP zst|G?{Nu-uAEUNBa=7x{7Em6Ve|z~H%MEFOh^wWf+--7o-j65xZme4>rPP`+Z6t(d zJ9>Op>VMR__0~C(X(WWaOG@dFLst8EUjE1na+vK@j_nFzxul#+&ckMLm~C4PjN^MB z#MQEy9BB6N>96PPDnf|i51xWci-40Dmnh_~wU&~+kIqTXS)6zmoRdh%Dlqyk4|O|q%!OYFAryc+#CtCS`|hF{6ZWCu$svTy z%|BC0y2KSBcf)G6y24!%awWj>{{!uRS>i_S?B+(D==nGHgu5c-+MbdB53^c>m8!ZM Qv;Y7A07*qoM6N<$f@Bwd0ssI2 literal 0 HcmV?d00001 diff --git a/data/odip_frag_bin b/data/odip_frag_bin new file mode 100644 index 0000000000000000000000000000000000000000..f7955ef89f0243114ef27bfbba06eea652e0b603 GIT binary patch literal 9120 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 literal 0 HcmV?d00001 diff --git a/data/pointer b/data/pointer new file mode 100644 index 0000000000000000000000000000000000000000..eb79fbd4202cb14c5a95ed1f96851338d006361c GIT binary patch literal 2200 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 literal 0 HcmV?d00001 diff --git a/data/star b/data/star new file mode 100644 index 0000000000000000000000000000000000000000..f4544178f27ab098d493d2dada5f479b9d01469e GIT binary patch literal 4805 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 literal 0 HcmV?d00001 diff --git a/data/window.png b/data/window.png new file mode 100644 index 0000000000000000000000000000000000000000..1eabf94c8750f0ffbee1e85623c9ea09fff690a8 GIT binary patch literal 3744 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 WQ)nh&%7{B + +#include + +// User should not directly modify these +Mtx _GRR_view; // Should be static as soon as all light functions needing this var will be in this file ;) +static guVector _GRR_cam = {0.0F, 0.0F, 0.0F}, + _GRR_up = {0.0F, 1.0F, 0.0F}, + _GRR_look = {0.0F, 0.0F, -100.0F}; +static guVector _GRRaxisx = (guVector){1, 0, 0}; // DO NOT MODIFY!!! +static guVector _GRRaxisy = (guVector){0, 1, 0}; // Even at runtime +static guVector _GRRaxisz = (guVector){0, 0, 1}; // NOT ever! +static Mtx _ObjTransformationMtx; +/** + * Set the background parameter when screen is cleared. + * @param r Red component. + * @param g Green component. + * @param b Blue component. + * @param a Alpha component. + */ +void GRRLIB_SetBackgroundColour(u8 r, u8 g, u8 b, u8 a) { + GX_SetCopyClear((GXColor){ r, g, b, a }, GX_MAX_Z24); +} + +/** + * Set the camera parameter (contributed my chris_c aka DaShAmAn). + * @param posx x position of the camera. + * @param posy y position of the camera. + * @param posz z position of the camera. + * @param upx Alpha component. + * @param upy Alpha component. + * @param upz Alpha component. + * @param lookx x up position of the camera. + * @param looky y up position of the camera. + * @param lookz z up position of the camera. + */ +void GRRLIB_Camera3dSettings(f32 posx, f32 posy, f32 posz, + f32 upx, f32 upy, f32 upz, + f32 lookx, f32 looky, f32 lookz) { + + _GRR_cam.x=posx; + _GRR_cam.y=posy; + _GRR_cam.z=posz; + + _GRR_up.x=upx; + _GRR_up.y=upy; + _GRR_up.z=upz; + + _GRR_look.x=lookx; + _GRR_look.y=looky; + _GRR_look.z=lookz; +} + +/** + * Set up the position matrix (contributed by chris_c aka DaShAmAn). + * @param minDist Minimal distance for the camera. + * @param maxDist Maximal distance for the camera. + * @param fov Field of view for the camera. + * @param texturemode False, GX won't need texture coordinate, True, GX will need texture coordinate. + * @param normalmode False, GX won't need normal coordinate, True, GX will need normal coordinate. + */ +void GRRLIB_3dMode(f32 minDist, f32 maxDist, f32 fov, bool texturemode, bool normalmode) { + Mtx m; + + guLookAt(_GRR_view, &_GRR_cam, &_GRR_up, &_GRR_look); + guPerspective(m, fov, (f32)rmode->fbWidth/rmode->efbHeight, minDist, maxDist); + GX_LoadProjectionMtx(m, GX_PERSPECTIVE); + GX_SetZMode (GX_TRUE, GX_LEQUAL, GX_TRUE); + + GX_SetCullMode(GX_CULL_NONE); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + if(normalmode) GX_SetVtxDesc(GX_VA_NRM, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + if(texturemode) GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + if(normalmode) GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + if(texturemode) GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + if(texturemode) GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + else GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); +} + +/** + * Go back to 2D mode (contributed by chris_c aka DaShAmAn). + */ +void GRRLIB_2dMode() { + Mtx view, m; + + GX_SetZMode(GX_FALSE, GX_LEQUAL, GX_TRUE); + + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + + guOrtho(m, 0, rmode->efbHeight, 0, rmode->fbWidth, 0, 1000.0f); + GX_LoadProjectionMtx(m, GX_ORTHOGRAPHIC); + + guMtxIdentity(view); + guMtxTransApply(view, view, 0, 0, -100.0F); + GX_LoadPosMtxImm(view, GX_PNMTX0); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + GX_SetNumTexGens(1); // One texture exists + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + GX_SetNumTevStages(1); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + + GX_SetNumChans(1); + GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + + GRRLIB_Settings.lights = 0; +} + +/** + * Init the object matrix to draw object. + */ +void GRRLIB_ObjectViewBegin(void) { + guMtxIdentity(_ObjTransformationMtx); +} + +/** + * Scale the object matrix to draw object. + * @param scalx x scale of the object. + * @param scaly y scale of the object. + * @param scalz z scale of the object. + */ +void GRRLIB_ObjectViewScale(f32 scalx, f32 scaly, f32 scalz) { + Mtx m; + + guMtxIdentity(m); + guMtxScaleApply(m, m, scalx, scaly, scalz); + + guMtxConcat(m, _ObjTransformationMtx, _ObjTransformationMtx); +} + +/** + * Rotate the object matrix to draw object . + * @param angx x rotation angle of the object. + * @param angy y rotation angle of the object. + * @param angz z rotation angle of the object. + */ +void GRRLIB_ObjectViewRotate(f32 angx, f32 angy, f32 angz) { + Mtx m, rx,ry,rz; + + guMtxIdentity(m); + guMtxRotAxisDeg(rx, &_GRRaxisx, angx); + guMtxRotAxisDeg(ry, &_GRRaxisy, angy); + guMtxRotAxisDeg(rz, &_GRRaxisz, angz); + guMtxConcat(ry, rx, m); + guMtxConcat(m, rz, m); + + guMtxConcat(m, _ObjTransformationMtx, _ObjTransformationMtx); +} + +/** + * Translate the object matrix to draw object. + * @param posx x position of the object. + * @param posy y position of the object. + * @param posz z position of the object. + */ +void GRRLIB_ObjectViewTrans(f32 posx, f32 posy, f32 posz) { + Mtx m; + + guMtxIdentity(m); + guMtxTransApply(m, m, posx, posy, posz); + + guMtxConcat(m, _ObjTransformationMtx, _ObjTransformationMtx); +} + +/** + * Concat the object and the view matrix and calculate the inverse normal matrix. + */ +void GRRLIB_ObjectViewEnd(void) { + Mtx mv, mvi; + + guMtxConcat(_GRR_view, _ObjTransformationMtx, mv); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + + guMtxInverse(mv, mvi); + guMtxTranspose(mvi, mv); + GX_LoadNrmMtxImm(mv, GX_PNMTX0); +} + +/** + * Set the view matrix to draw object (in this order scale, rotate AND trans). + * @param posx x position of the object. + * @param posy y position of the object. + * @param posz z position of the object. + * @param angx x rotation angle of the object. + * @param angy y rotation angle of the object. + * @param angz z rotation angle of the object. + * @param scalx x scale of the object. + * @param scaly y scale of the object. + * @param scalz z scale of the object. + */ +void GRRLIB_ObjectView(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz) { + Mtx ObjTransformationMtx; + Mtx m, rx,ry,rz; + Mtx mv, mvi; + + guMtxIdentity(ObjTransformationMtx); + + if((scalx !=1.0f) || (scaly !=1.0f) || (scalz !=1.0f)) { + guMtxIdentity(m); + guMtxScaleApply(m, m, scalx, scaly, scalz); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + if((angx !=0.0f) || (angy !=0.0f) || (angz !=0.0f)) { + guMtxIdentity(m); + guMtxRotAxisDeg(rx, &_GRRaxisx, angx); + guMtxRotAxisDeg(ry, &_GRRaxisy, angy); + guMtxRotAxisDeg(rz, &_GRRaxisz, angz); + guMtxConcat(ry, rx, m); + guMtxConcat(m, rz, m); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + if((posx !=0.0f) || (posy !=0.0f) || (posz !=0.0f)) { + guMtxIdentity(m); + guMtxTransApply(m, m, posx, posy, posz); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + guMtxConcat(_GRR_view, ObjTransformationMtx, mv); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + + guMtxInverse(mv, mvi); + guMtxTranspose(mvi, mv); + GX_LoadNrmMtxImm(mv, GX_PNMTX0); +} + +/** + * Set the view matrix to draw object (in this order scale, trans AND rotate). + * @param posx x position of the object. + * @param posy y position of the object. + * @param posz z position of the object. + * @param angx x rotation angle of the object. + * @param angy y rotation angle of the object. + * @param angz z rotation angle of the object. + * @param scalx x scale of the object. + * @param scaly y scale of the object. + * @param scalz z scale of the object. + */ +void GRRLIB_ObjectViewInv(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz) { + Mtx ObjTransformationMtx; + Mtx m, rx,ry,rz; + Mtx mv, mvi; + + guMtxIdentity(ObjTransformationMtx); + + if((scalx !=1.0f) || (scaly !=1.0f) || (scalz !=1.0f)) { + guMtxIdentity(m); + guMtxScaleApply(m, m, scalx, scaly, scalz); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + if((posx !=0.0f) || (posy !=0.0f) || (posz !=0.0f)) { + guMtxIdentity(m); + guMtxTransApply(m, m, posx, posy, posz); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + if((angx !=0.0f) || (angy !=0.0f) || (angz !=0.0f)) { + guMtxIdentity(m); + guMtxRotAxisDeg(rx, &_GRRaxisx, angx); + guMtxRotAxisDeg(ry, &_GRRaxisy, angy); + guMtxRotAxisDeg(rz, &_GRRaxisz, angz); + guMtxConcat(ry, rx, m); + guMtxConcat(m, rz, m); + + guMtxConcat(m, ObjTransformationMtx, ObjTransformationMtx); + } + + guMtxConcat(_GRR_view, ObjTransformationMtx, mv); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + + guMtxInverse(mv, mvi); + guMtxTranspose(mvi, mv); + GX_LoadNrmMtxImm(mv, GX_PNMTX0); +} + +/** + * Set the texture to an object (contributed by chris_c aka DaShAmAn). + * @param tex Pointer to an image texture (GRRLIB_texImg format). + * @param rep Texture Repeat Mode, True will repeat it, False won't. + */ +void GRRLIB_SetTexture(GRRLIB_texImg *tex, bool rep) { + GXTexObj texObj; + + if (rep) { + GX_InitTexObj(&texObj, tex->data, tex->w, tex->h, GX_TF_RGBA8, GX_REPEAT, GX_REPEAT, GX_FALSE); + } + else { + GX_InitTexObj(&texObj, tex->data, tex->w, tex->h, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + } + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); +} + +/** + * Draw a torus (with normal). + * @param r Radius of the ring. + * @param R Radius of the torus. + * @param nsides Number of faces per ring. + * @param rings Number of rings. + * @param filled Wired or not. + * @param col Color of the torus. + */ +void GRRLIB_DrawTorus(f32 r, f32 R, int nsides, int rings, bool filled, u32 col) { + int i, j; + f32 theta, phi, theta1; + f32 cosTheta, sinTheta; + f32 cosTheta1, sinTheta1; + f32 ringDelta, sideDelta; + f32 cosPhi, sinPhi, dist; + + ringDelta = 2.0 * M_PI / rings; + sideDelta = 2.0 * M_PI / nsides; + + theta = 0.0; + cosTheta = 1.0; + sinTheta = 0.0; + for (i = rings - 1; i >= 0; i--) { + theta1 = theta + ringDelta; + cosTheta1 = cos(theta1); + sinTheta1 = sin(theta1); + if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 2*(nsides+1)); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 2*(nsides+1)); + phi = 0.0; + for (j = nsides; j >= 0; j--) { + phi += sideDelta; + cosPhi = cos(phi); + sinPhi = sin(phi); + dist = R + r * cosPhi; + + GX_Position3f32(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi); + GX_Normal3f32(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi); + GX_Color1u32(col); + GX_Position3f32(cosTheta * dist, -sinTheta * dist, r * sinPhi); + GX_Normal3f32(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi); + GX_Color1u32(col); + } + GX_End(); + theta = theta1; + cosTheta = cosTheta1; + sinTheta = sinTheta1; + } +} + +/** + * Draw a sphere (with normal). + * @param r Radius of the sphere. + * @param lats Number of lattitudes. + * @param longs Number of longitutes. + * @param filled Wired or not. + * @param col Color of the sphere. + */ +void GRRLIB_DrawSphere(f32 r, int lats, int longs, bool filled, u32 col) { + int i, j; + f32 lat0, z0, zr0, + lat1, z1, zr1, + lng, x, y; + + for(i = 0; i <= lats; i++) { + lat0 = M_PI * (-0.5F + (f32) (i - 1) / lats); + z0 = sin(lat0); + zr0 = cos(lat0); + + lat1 = M_PI * (-0.5F + (f32) i / lats); + z1 = sin(lat1); + zr1 = cos(lat1); + if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 2*(longs+1)); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 2*(longs+1)); + for(j = 0; j <= longs; j++) { + lng = 2 * M_PI * (f32) (j - 1) / longs; + x = cos(lng); + y = sin(lng); + + GX_Position3f32(x * zr0 * r, y * zr0 * r, z0 * r); + GX_Normal3f32(x * zr0 * r, y * zr0 * r, z0 * r); + GX_Color1u32(col); + GX_Position3f32(x * zr1 * r, y * zr1 * r, z1 * r); + GX_Normal3f32(x * zr1 * r, y * zr1 * r, z1 * r); + GX_Color1u32(col); + } + GX_End(); + } +} + +/** + * Draw a cube (with normal). + * @param size Size of the cube edge. + * @param filled Wired or not. + * @param col Color of the cube. + */ +void GRRLIB_DrawCube(f32 size, bool filled, u32 col) { + static f32 n[6][3] = + { + {-1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {1.0, 0.0, 0.0}, + {0.0, -1.0, 0.0}, + {0.0, 0.0, 1.0}, + {0.0, 0.0, -1.0} + }; + static int faces[6][4] = + { + {0, 1, 2, 3}, + {3, 2, 6, 7}, + {7, 6, 5, 4}, + {4, 5, 1, 0}, + {5, 6, 2, 1}, + {7, 4, 0, 3} + }; + f32 v[8][3]; + int i; + + v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2; + v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2; + v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2; + v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2; + v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2; + v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2; + + for (i = 5; i >= 0; i--) { + if(filled) GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 5); + GX_Position3f32(v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2] ); + GX_Normal3f32(n[i][0], n[i][1], n[i][2]); + GX_Color1u32(col); + GX_Position3f32(v[faces[i][1]][0], v[faces[i][1]][1], v[faces[i][1]][2]); + GX_Normal3f32(n[i][0], n[i][1], n[i][2]); + GX_Color1u32(col); + GX_Position3f32(v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2]); + GX_Normal3f32(n[i][0], n[i][1], n[i][2]); + GX_Color1u32(col); + GX_Position3f32(v[faces[i][3]][0], v[faces[i][3]][1], v[faces[i][3]][2]); + GX_Normal3f32(n[i][0], n[i][1], n[i][2]); + GX_Color1u32(col); + if(!filled) { + GX_Position3f32(v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2]); + GX_Normal3f32(n[i][0], n[i][1], n[i][2]); + GX_Color1u32(col); + } + GX_End(); + } +} + +/** + * Draw a cylinder (with normal). + * @param r Radius of the cylinder. + * @param h High of the cylinder. + * @param d Dencity of slice. + * @param filled Wired or not. + * @param col Color of the cylinder. + */ +void GRRLIB_DrawCylinder(f32 r, f32 h, int d, bool filled, u32 col) { + int i; + f32 dx, dy; + + if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 2 * (d+1)); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 2 * (d+1)); + for(i = 0 ; i <= d ; i++) { + dx = cosf( M_PI * 2.0f * i / d ); + dy = sinf( M_PI * 2.0f * i / d ); + GX_Position3f32( r * dx, -0.5f * h, r * dy ); + GX_Normal3f32( dx, 0.0f, dy ); + GX_Color1u32(col); + GX_Position3f32( r * dx, 0.5f * h, r * dy ); + GX_Normal3f32( dx, 0.0f, dy ); + GX_Color1u32(col); + } + GX_End(); + + if(filled) GX_Begin(GX_TRIANGLEFAN, GX_VTXFMT0, d+2); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, d+2); + GX_Position3f32(0.0f, -0.5f * h, 0.0f); + GX_Normal3f32(0.0f, -1.0f, 0.0f); + GX_Color1u32(col); + for(i = 0 ; i <= d ; i++) { + GX_Position3f32( r * cosf( M_PI * 2.0f * i / d ), -0.5f * h, r * sinf( M_PI * 2.0f * i / d ) ); + GX_Normal3f32(0.0f, -1.0f, 0.0f); + GX_Color1u32(col); + } + GX_End(); + + if(filled) GX_Begin(GX_TRIANGLEFAN, GX_VTXFMT0, d+2); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, d+2); + GX_Position3f32(0.0f, 0.5f * h, 0.0f); + GX_Normal3f32(0.0f, 1.0f, 0.0f); + GX_Color1u32(col); + for(i = 0 ; i <= d ; i++) { + GX_Position3f32( r * cosf( M_PI * 2.0f * i / d ), 0.5f * h, r * sinf( M_PI * 2.0f * i / d ) ); + GX_Normal3f32(0.0f, 1.0f, 0.0f); + GX_Color1u32(col); + } + GX_End(); +} + +/** + * Draw a cone (with normal). + * @param r Radius of the cone. + * @param h High of the cone. + * @param d Dencity of slice. + * @param filled Wired or not. + * @param col Color of the cone. + */ +void GRRLIB_DrawCone(f32 r, f32 h, int d, bool filled, u32 col) { + int i; + f32 dx, dy; + + if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 2 * (d+1)); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 2 * (d+1)); + for(i = 0 ; i <= d ; i++) { + dx = cosf( M_PI * 2.0f * i / d ); + dy = sinf( M_PI * 2.0f * i / d ); + GX_Position3f32( 0, -0.5f * h,0); + GX_Normal3f32( dx, 0.0f, dy ); + GX_Color1u32(col); + GX_Position3f32( r * dx, 0.5f * h, r * dy ); + GX_Normal3f32( dx, 0.0f, dy ); + GX_Color1u32(col); + } + GX_End(); + + if(filled) GX_Begin(GX_TRIANGLEFAN, GX_VTXFMT0, d+2); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, d+2); + GX_Position3f32(0.0f, 0.5f * h, 0.0f); + GX_Normal3f32(0.0f, 1.0f, 0.0f); + GX_Color1u32(col); + for(i = 0 ; i <= d ; i++) { + GX_Position3f32( r * cosf( M_PI * 2.0f * i / d ), 0.5f * h, r * sinf( M_PI * 2.0f * i / d ) ); + GX_Normal3f32(0.0f, 1.0f, 0.0f); + GX_Color1u32(col); + } + GX_End(); +} + +/** + * Draw a Tesselated pannel (with normal). + * @param w Width of the panel. + * @param wstep Size of width slices. + * @param h Height of the pannel. + * @param hstep Size the de height slices. + * @param filled Wired or not. + * @param col Color in RGBA format. + */ +void GRRLIB_DrawTessPanel(f32 w, f32 wstep, f32 h, f32 hstep, bool filled, u32 col) { + f32 x,y,tmpx, tmpy; + int tmp; + tmpy = h/2.0f; + tmpx = w/2.0f; + tmp = ((w/wstep)*2)+2; + for ( y = -tmpy ; y <= tmpy ; y+=hstep ) + { + if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, tmp); + else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, tmp); + for ( x = -tmpx ; x <= tmpx ; x+=wstep ) + { + GX_Position3f32( x, y, 0.0f ); + GX_Normal3f32( 0.0f, 0.0f, 1.0f); + GX_Color1u32(col); + GX_Position3f32( x, y+hstep, 0.0f ); + GX_Normal3f32( 0.0f, 0.0f, 1.0f); + GX_Color1u32(col); + } + GX_End(); + } +} + +/** + * Set ambient color. + * When no diffuse ligth is shinig on a object, the color is equal to ambient color. + * @param ambientcolor Ambient color in RGBA format. + */ +void GRRLIB_SetLightAmbient(u32 ambientcolor) { + GX_SetChanAmbColor(GX_COLOR0A0, (GXColor) { R(ambientcolor), G(ambientcolor), B(ambientcolor), 0xFF}); +} + +/** + * Set diffuse light parameters. + * @param num Number of the light. It's a number from 0 to 7. + * @param pos Position of the diffuse light (x/y/z). + * @param distattn Distance attenuation. + * @param brightness Brightness of the light. The value should be between 0 and 1. + * @param lightcolor Color of the light in RGBA format. + */ +void GRRLIB_SetLightDiff(u8 num, guVector pos, f32 distattn, f32 brightness, u32 lightcolor) { + GXLightObj MyLight; + guVector lpos = {pos.x, pos.y, pos.z}; + + GRRLIB_Settings.lights |= (1< +#include + +#include + +/** + * Load a ByteMap font structure from a buffer. + * File format version 1.1 is used, more information could be found at http://bmf.wz.cz/bmf-format.htm + * @param my_bmf The ByteMap font buffer to load. + * @return A GRRLIB_bytemapFont structure filled with BMF information. + * @see GRRLIB_FreeBMF + */ +GRRLIB_bytemapFont* GRRLIB_LoadBMF (const u8 my_bmf[] ) { + GRRLIB_bytemapFont *fontArray = (struct GRRLIB_bytemapFont *)malloc(sizeof(GRRLIB_bytemapFont)); + u32 i, j = 1; + u8 lineheight, usedcolors, highestcolor, nbPalette, c; + short int sizeover, sizeunder, sizeinner, numcolpal; + u16 nbPixels; + + if (fontArray != NULL && my_bmf[0]==0xE1 && my_bmf[1]==0xE6 && my_bmf[2]==0xD5 && my_bmf[3]==0x1A) { + fontArray->version = my_bmf[4]; + lineheight = my_bmf[5]; + sizeover = my_bmf[6]; + sizeunder = my_bmf[7]; + fontArray->tracking = my_bmf[8]; + sizeinner = my_bmf[9]; + usedcolors = my_bmf[10]; + highestcolor = my_bmf[11]; + nbPalette = my_bmf[16]; + numcolpal = 3 * nbPalette; + fontArray->palette = (u32 *)calloc(nbPalette + 1, sizeof(u32)); + for (i=0; i < numcolpal; i+=3) { + fontArray->palette[j++] = ((((my_bmf[i+17]<<2)+3)<<24) | (((my_bmf[i+18]<<2)+3)<<16) | (((my_bmf[i+19]<<2)+3)<<8) | 0xFF); + } + j = my_bmf[17 + numcolpal]; + fontArray->name = (char *)calloc(j + 1, sizeof(char)); + memcpy(fontArray->name, &my_bmf[18 + numcolpal], j); + j = 18 + numcolpal + j; + fontArray->nbChar = (my_bmf[j] | my_bmf[j+1]<<8); + memset(fontArray->charDef, 0, 256 * sizeof(GRRLIB_bytemapChar)); + j++; + for (i=0; i < fontArray->nbChar; i++) { + c = my_bmf[++j]; + fontArray->charDef[c].width = my_bmf[++j]; + fontArray->charDef[c].height = my_bmf[++j]; + fontArray->charDef[c].relx = my_bmf[++j]; + fontArray->charDef[c].rely = my_bmf[++j]; + fontArray->charDef[c].kerning = my_bmf[++j]; + nbPixels = fontArray->charDef[c].width * fontArray->charDef[c].height; + fontArray->charDef[c].data = (u8 *)malloc(nbPixels); + if (nbPixels && fontArray->charDef[c].data) { + memcpy(fontArray->charDef[c].data, &my_bmf[++j], nbPixels); + j += (nbPixels - 1); + } + } + } + return fontArray; +} + +/** + * Free memory allocated by ByteMap fonts. + * @param bmf A GRRLIB_bytemapFont structure. + */ +void GRRLIB_FreeBMF (GRRLIB_bytemapFont *bmf) { + u16 i; + + for (i=0; i<256; i++) { + if(bmf->charDef[i].data) { + free(bmf->charDef[i].data); + } + } + free(bmf->palette); + free(bmf->name); + free(bmf); + bmf = NULL; +} + +/** + * Initialize a tile set. + * @param tex The texture to initialize. + * @param tilew Width of the tile. + * @param tileh Height of the tile. + * @param tilestart Offset for starting position (Used in fonts). + */ +void GRRLIB_InitTileSet (GRRLIB_texImg *tex, + const uint tilew, const uint tileh, + const uint tilestart) { + tex->tilew = tilew; + tex->tileh = tileh; + if (tilew) // Avoid division by zero + tex->nbtilew = tex->w / tilew; + if (tileh) // Avoid division by zero + tex->nbtileh = tex->h / tileh; + tex->tilestart = tilestart; + tex->tiledtex = true; + tex->ofnormaltexx = 1.0F / tex->nbtilew; + tex->ofnormaltexy = 1.0F / tex->nbtileh; + GRRLIB_SetHandle( tex, 0, 0 ); +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_bmfx.c b/lib/grrlib/GRRLIB/GRRLIB_bmfx.c new file mode 100644 index 0000000..cef02f9 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_bmfx.c @@ -0,0 +1,242 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include + +#include + +/** + * Flip texture horizontal. + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + */ +void GRRLIB_BMFX_FlipH (const GRRLIB_texImg *texsrc, GRRLIB_texImg *texdest) { + unsigned int x, y, txtWidth = texsrc->w - 1; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + GRRLIB_SetPixelTotexImg(txtWidth - x, y, texdest, + GRRLIB_GetPixelFromtexImg(x, y, texsrc)); + } + } +} + +/** + * Flip texture vertical. + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + */ +void GRRLIB_BMFX_FlipV (const GRRLIB_texImg *texsrc, GRRLIB_texImg *texdest) { + unsigned int x, y, texHeight = texsrc->h - 1; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + GRRLIB_SetPixelTotexImg(x, texHeight - y, texdest, + GRRLIB_GetPixelFromtexImg(x, y, texsrc)); + } + } +} + +/** + * Change a texture to gray scale. + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture grayscaled destination. + */ +void GRRLIB_BMFX_Grayscale (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest) { + unsigned int x, y; + u8 gray; + u32 color; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + color = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + + gray = ((R(color)* 77 + + G(color)*150 + + B(color)* 28 ) / 255); + + GRRLIB_SetPixelTotexImg(x, y, texdest, + ((gray << 24) | (gray << 16) | (gray << 8) | A(color))); + } + } + GRRLIB_SetHandle(texdest, 0, 0); +} + +/** + * Change a texture to sepia (old photo style). + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + * @author elisherer + */ +void GRRLIB_BMFX_Sepia (const GRRLIB_texImg *texsrc, GRRLIB_texImg *texdest) { + unsigned int x, y; + u16 sr, sg, sb; + u32 color; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + color = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + sr = R(color)*0.393 + G(color)*0.769 + B(color)*0.189; + sg = R(color)*0.349 + G(color)*0.686 + B(color)*0.168; + sb = R(color)*0.272 + G(color)*0.534 + B(color)*0.131; + if (sr>255) sr=255; if (sg>255) sg=255; if (sb>255) sb=255; + GRRLIB_SetPixelTotexImg(x, y, texdest, + RGBA(sr,sg,sb,A(color))); + } + } + GRRLIB_SetHandle(texdest, 0, 0); +} +/** + * Invert colors of the texture. + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + */ +void GRRLIB_BMFX_Invert (const GRRLIB_texImg *texsrc, GRRLIB_texImg *texdest) { + unsigned int x, y; + u32 color; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + color = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + GRRLIB_SetPixelTotexImg(x, y, texdest, + ((0xFFFFFF - (color >> 8 & 0xFFFFFF)) << 8) | (color & 0xFF)); + } + } +} + +/** + * A texture effect (Blur). + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + * @param factor The blur factor. + */ +void GRRLIB_BMFX_Blur (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor) { + int numba = (1+(factor<<1))*(1+(factor<<1)); + u32 x, y; + s32 k, l; + int tmp; + int newr, newg, newb, newa; + u32 colours[numba]; + u32 thiscol; + + for (x = 0; x < texsrc->w; x++) { + for (y = 0; y < texsrc->h; y++) { + newr = 0; + newg = 0; + newb = 0; + newa = 0; + + tmp = 0; + thiscol = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + + for (k = x - factor; k <= x + factor; k++) { + for (l = y - factor; l <= y + factor; l++) { + if (k < 0 || k >= texsrc->w || l < 0 || l >= texsrc->h) { + colours[tmp] = thiscol; + } + else { + colours[tmp] = GRRLIB_GetPixelFromtexImg(k, l, texsrc); + } + tmp++; + } + } + + for (tmp = 0; tmp < numba; tmp++) { + newr += (colours[tmp] >> 24) & 0xFF; + newg += (colours[tmp] >> 16) & 0xFF; + newb += (colours[tmp] >> 8) & 0xFF; + newa += colours[tmp] & 0xFF; + } + + newr /= numba; + newg /= numba; + newb /= numba; + newa /= numba; + + GRRLIB_SetPixelTotexImg(x, y, texdest, (newr<<24) | (newg<<16) | (newb<<8) | newa); + } + } +} + +/** + * A texture effect (Scatter). + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + * @param factor The factor level of the effect. + */ +void GRRLIB_BMFX_Scatter (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor) { + unsigned int x, y; + u32 val1, val2; + u32 val3, val4; + int factorx2 = factor*2; + + for (y = 0; y < texsrc->h; y++) { + for (x = 0; x < texsrc->w; x++) { + val1 = x + (int) (factorx2 * (rand() / (RAND_MAX + 1.0))) - factor; + val2 = y + (int) (factorx2 * (rand() / (RAND_MAX + 1.0))) - factor; + + if ((val1 >= texsrc->w) || (val2 >= texsrc->h)) { + } + else { + val3 = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + val4 = GRRLIB_GetPixelFromtexImg(val1, val2, texsrc); + GRRLIB_SetPixelTotexImg(x, y, texdest, val4); + GRRLIB_SetPixelTotexImg(val1, val2, texdest, val3); + } + } + } +} + +/** + * A texture effect (Pixelate). + * @see GRRLIB_FlushTex + * @param texsrc The texture source. + * @param texdest The texture destination. + * @param factor The factor level of the effect. + */ +void GRRLIB_BMFX_Pixelate (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor) { + unsigned int x, y; + unsigned int xx, yy; + u32 rgb; + + for (x = 0; x < texsrc->w - 1 - factor; x += factor) { + for (y = 0; y < texsrc->h - 1 - factor; y +=factor) { + rgb = GRRLIB_GetPixelFromtexImg(x, y, texsrc); + for (xx = x; xx < x + factor; xx++) { + for (yy = y; yy < y + factor; yy++) { + GRRLIB_SetPixelTotexImg(xx, yy, texdest, rgb); + } + } + } + } +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_core.c b/lib/grrlib/GRRLIB/GRRLIB_core.c new file mode 100644 index 0000000..58202fc --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_core.c @@ -0,0 +1,211 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include +#include +#include +#include +//#include + +#define __GRRLIB_CORE__ +#include +#include "grrlib/GRRLIB_private.h" + +#define DEFAULT_FIFO_SIZE (256 * 1024) /**< GX fifo buffer size. */ + +GRRLIB_drawSettings GRRLIB_Settings; +Mtx GXmodelView2D; + +#if 0 + +static void *gp_fifo = NULL; + +static bool is_setup = false; // To control entry and exit + +/** + * Initialize GRRLIB. Call this once at the beginning your code. + * @return A integer representating a code: + * - 0 : The operation completed successfully. + * - -1 : Not enough memory is available to initialize GRRLIB. + * - -2 : Failed to add the fat device driver to the devoptab. + * - -3 : Failed to initialize the font engine. + * @see GRRLIB_Exit + */ +int GRRLIB_Init (void) { + f32 yscale; + u32 xfbHeight; + Mtx44 perspective; + s8 error_code = 0; + + // Ensure this function is only ever called once + if (is_setup) return 0; + + // Initialise the video subsystem + VIDEO_Init(); + VIDEO_SetBlack(true); // Disable video output during initialisation + + // Grab a pointer to the video mode attributes + if ( !(rmode = VIDEO_GetPreferredMode(NULL)) ) return -1; + + // Video Mode Correction + switch (rmode->viTVMode) { + case VI_DEBUG_PAL: // PAL 50hz 576i + //rmode = &TVPal574IntDfScale; + rmode = &TVPal528IntDf; // BC ...this is still wrong, but "less bad" for now + break; + } + + // 16:9 and 4:3 Screen Adjustment + if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) { + rmode->viWidth = 678; + rmode->viXOrigin = (VI_MAX_WIDTH_NTSC - 678)/2; // This probably needs to consider PAL + } else { // 4:3 + rmode->viWidth = 672; + rmode->viXOrigin = (VI_MAX_WIDTH_NTSC - 672)/2; + } + + // -- + VIDEO_Configure(rmode); + + // Get some memory to use for a "double buffered" frame buffer + if ( !(xfb[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode))) ) return -1; + if ( !(xfb[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode))) ) return -1; + + VIDEO_SetNextFramebuffer(xfb[fb]); // Choose a frame buffer to start with + + VIDEO_Flush(); // flush the frame to the TV + VIDEO_WaitVSync(); // Wait for the TV to finish updating + // If the TV image is interlaced it takes two passes to display the image + if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); + + // The FIFO is the buffer the CPU uses to send commands to the GPU + if ( !(gp_fifo = memalign(32, DEFAULT_FIFO_SIZE)) ) return -1; + memset(gp_fifo, 0, DEFAULT_FIFO_SIZE); + GX_Init(gp_fifo, DEFAULT_FIFO_SIZE); + + // Clear the background to opaque black and clears the z-buffer + GX_SetCopyClear((GXColor){ 0, 0, 0, 0 }, GX_MAX_Z24); + + if (rmode->aa) GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); // Set 16 bit RGB565 + else GX_SetPixelFmt(GX_PF_RGB8_Z24 , GX_ZC_LINEAR); // Set 24 bit Z24 + + // Other GX setup + yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight); + xfbHeight = GX_SetDispCopyYScale(yscale); + GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight); + GX_SetDispCopyDst(rmode->fbWidth, xfbHeight); + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE)); + + GX_SetDispCopyGamma(GX_GM_1_0); + + if(rmode->fbWidth <= 0){ printf("GRRLIB " GRRLIB_VER_STRING); } + + // Setup the vertex descriptor + GX_ClearVtxDesc(); // clear all the vertex descriptors + GX_InvVtxCache(); // Invalidate the vertex cache + GX_InvalidateTexAll(); // Invalidate all textures + + // Tells the flipper to expect direct data + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + // Colour 0 is 8bit RGBA format + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetZMode(GX_FALSE, GX_LEQUAL, GX_TRUE); + + GX_SetNumChans(1); // colour is the same as vertex colour + GX_SetNumTexGens(1); // One texture exists + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + guMtxIdentity(GXmodelView2D); + guMtxTransApply(GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -100.0F); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + guOrtho(perspective, 0, rmode->efbHeight, 0, rmode->fbWidth, 0, 1000.0f); + GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC); + + GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetAlphaUpdate(GX_TRUE); + GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0); + GX_SetColorUpdate(GX_ENABLE); + GX_SetCullMode(GX_CULL_NONE); + GRRLIB_ClipReset(); + + // Default settings + GRRLIB_Settings.antialias = true; + GRRLIB_Settings.blend = GRRLIB_BLEND_ALPHA; + GRRLIB_Settings.lights = 0; + + // Schedule cleanup for when program exits + is_setup = true; + atexit(GRRLIB_Exit); + + // Initialise the filing system + if (!fatInitDefault()) error_code = -2; + + // Initialise TTF + if (GRRLIB_InitTTF()) error_code = -3; + + VIDEO_SetBlack(false); // Enable video output + return error_code; +} + +/** + * Call this before exiting your application. + * Ensure this function is only ever called once + * and only if the setup function has been called. + */ +void GRRLIB_Exit (void) { + static bool done = false; + if (done || !is_setup) return; + else done = true; + + // Allow write access to the full screen + GX_SetClipMode( GX_CLIP_DISABLE ); + GX_SetScissor( 0, 0, rmode->fbWidth, rmode->efbHeight ); + + // We empty both frame buffers on our way out + // otherwise dead frames are sometimes seen when starting the next app + GRRLIB_FillScreen( 0x000000FF ); GRRLIB_Render(); + GRRLIB_FillScreen( 0x000000FF ); GRRLIB_Render(); + + // Shut down the GX engine + GX_DrawDone(); + GX_AbortFrame(); + + // Free up memory allocated for frame buffers & FIFOs + if (xfb[0] != NULL) { free(MEM_K1_TO_K0(xfb[0])); xfb[0] = NULL; } + if (xfb[1] != NULL) { free(MEM_K1_TO_K0(xfb[1])); xfb[1] = NULL; } + if (gp_fifo != NULL) { free(gp_fifo); gp_fifo = NULL; } + + // Done with TTF + GRRLIB_ExitTTF(); +} +#endif + diff --git a/lib/grrlib/GRRLIB/GRRLIB_fbAdvanced.c b/lib/grrlib/GRRLIB/GRRLIB_fbAdvanced.c new file mode 100644 index 0000000..cf8bace --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_fbAdvanced.c @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include + +#include + +/** + * Draw a circle. + * @author Dark_Link + * @param x Specifies the x-coordinate of the circle. + * @param y Specifies the y-coordinate of the circle. + * @param radius The radius of the circle. + * @param color The color of the circle in RGBA format. + * @param filled Set to true to fill the circle. + */ +void GRRLIB_Circle (const f32 x, const f32 y, const f32 radius, + const u32 color, const u8 filled) { + guVector v[36]; + u32 ncolor[36]; + u32 a; + f32 ra; + f32 G_DTOR = M_DTOR * 10; + + for (a = 0; a < 36; a++) { + ra = a * G_DTOR; + + v[a].x = cos(ra) * radius + x; + v[a].y = sin(ra) * radius + y; + v[a].z = 0.0f; + ncolor[a] = color; + } + + if (!filled) GRRLIB_GXEngine(v, ncolor, 36, GX_LINESTRIP ); + else GRRLIB_GXEngine(v, ncolor, 36, GX_TRIANGLEFAN); +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_fileIO.c b/lib/grrlib/GRRLIB/GRRLIB_fileIO.c new file mode 100644 index 0000000..93b7d5a --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_fileIO.c @@ -0,0 +1,114 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +/** + * Load a file to memory. + * @param filename Name of the file to be loaded. + * @param data Pointer-to-your-pointer. + * Ie. { u8 *data; GRRLIB_LoadFile("file", &data); }. + * It is your responsibility to free the memory allocated by this function. + * @return A integer representating a code: + * - 0 : EmptyFile. + * - -1 : FileNotFound. + * - -2 : OutOfMemory. + * - -3 : FileReadError. + * - >0 : FileLength. + */ +int GRRLIB_LoadFile(const char* filename, unsigned char* *data) { + int len; + FILE *fd; + + // Open the file + if ( !(fd = fopen(filename, "rb")) ) { + return -1; + } + + // Get file length + fseek(fd, 0, SEEK_END); + if ( !(len = ftell(fd)) ) { + fclose(fd); + *data = NULL; + return 0; + } + fseek(fd, 0, SEEK_SET); + + // Grab some memory in which to store the file + if ( !(*data = malloc(len)) ) { + fclose(fd); + return -2; + } + + if ( fread(*data, 1, len, fd) != len) { + fclose(fd); + free(*data); *data = NULL; + return -3; + } + + fclose(fd); + return len; +} + +/** + * Load a texture from a file. + * @param filename The JPEG, PNG or Bitmap filename to load. + * @return A GRRLIB_texImg structure filled with image information. + * If an error occurs NULL will be returned. + */ +GRRLIB_texImg* GRRLIB_LoadTextureFromFile(const char *filename) { + GRRLIB_texImg *tex; + unsigned char *data; + + // return NULL it load fails + if (GRRLIB_LoadFile(filename, &data) <= 0) return NULL; + + // Convert to texture + tex = GRRLIB_LoadTexture(data); + + // Free up the buffer + free(data); + + return tex; +} + +/** + * Make a PNG screenshot. + * It should be called after drawing stuff on the screen, but before GRRLIB_Render. + * libfat is required to use the function. + * @param filename Name of the file to write. + * @return bool true=everything worked, false=problems occurred. + */ +bool GRRLIB_ScrShot(const char* filename) { + IMGCTX pngContext; + int ret = -1; + + if ( (pngContext = PNGU_SelectImageFromDevice(filename)) ) { + ret = PNGU_EncodeFromEFB( pngContext, + rmode->fbWidth, rmode->efbHeight, + 0 ); + PNGU_ReleaseImageContext(pngContext); + } + return !ret; +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_gecko.c b/lib/grrlib/GRRLIB/GRRLIB_gecko.c new file mode 100644 index 0000000..6932a18 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_gecko.c @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include +#include +#include + +static bool geckoinit = false; + +/** + * Initialize USB Gecko. + */ +bool GRRLIB_GeckoInit() { + u32 geckoattached = usb_isgeckoalive(EXI_CHANNEL_1); + if (geckoattached) { + usb_flush(EXI_CHANNEL_1); + geckoinit = true; + return true; + } + else return false; +} + +/** + * Print Gecko. + * @param text Text to print. + * @param ... Optional arguments. + */ +void GRRLIB_GeckoPrintf (const char *text, ...) { + int size; + char tmp[1024]; + + if (!geckoinit) return; + + va_list argp; + va_start(argp, text); + size = vsprintf(tmp, text, argp); + va_end(argp); + + usb_sendbuffer_safe(1, tmp, size); +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_print.c b/lib/grrlib/GRRLIB/GRRLIB_print.c new file mode 100644 index 0000000..1a22df1 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_print.c @@ -0,0 +1,100 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include +#include +#include + +#include + +/** + * Print formatted output. + * @param xpos Specifies the x-coordinate of the upper-left corner of the text. + * @param ypos Specifies the y-coordinate of the upper-left corner of the text. + * @param tex The texture containing the character set. + * @param color Text color in RGBA format. The alpha channel is used to change the opacity of the text. + * @param zoom This is a factor by which the text size will be increase or decrease. + * @param text Text to draw. + * @param ... Optional arguments. + */ +void GRRLIB_Printf (const f32 xpos, const f32 ypos, + const GRRLIB_texImg *tex, const u32 color, + const f32 zoom, const char *text, ...) { + if (tex == NULL || tex->data == NULL) { + return; + } + + int i, size; + char tmp[1024]; + f32 offset = tex->tilew * zoom; + + va_list argp; + va_start(argp, text); + size = vsnprintf(tmp, sizeof(tmp), text, argp); + va_end(argp); + + for (i = 0; i < size; i++) { + GRRLIB_DrawTile(xpos+i*offset, ypos, tex, 0, zoom, zoom, color, + tmp[i] - tex->tilestart); + } +} + +/** + * Print formatted output with a ByteMap font. + * This function could be slow, it should be used with GRRLIB_CompoStart and GRRLIB_CompoEnd. + * @param xpos Specifies the x-coordinate of the upper-left corner of the text. + * @param ypos Specifies the y-coordinate of the upper-left corner of the text. + * @param bmf The ByteMap font to use. + * @param text Text to draw. + * @param ... Optional arguments. + */ +void GRRLIB_PrintBMF (const f32 xpos, const f32 ypos, + const GRRLIB_bytemapFont *bmf, + const char *text, ...) { + uint i, size; + u8 *pdata; + u8 x, y; + char tmp[1024]; + f32 xoff = xpos; + const GRRLIB_bytemapChar *pchar; + + va_list argp; + va_start(argp, text); + size = vsprintf(tmp, text, argp); + va_end(argp); + + for (i=0; icharDef[(u8)tmp[i]]; + pdata = pchar->data; + for (y=0; yheight; y++) { + for (x=0; xwidth; x++) { + if (*pdata) { + GRRLIB_Plot(xoff + x + pchar->relx, + ypos + y + pchar->rely, + bmf->palette[*pdata]); + } + pdata++; + } + } + xoff += pchar->kerning + bmf->tracking; + } +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_render.c b/lib/grrlib/GRRLIB/GRRLIB_render.c new file mode 100644 index 0000000..ff33c96 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_render.c @@ -0,0 +1,423 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include + +#include + +extern GRRLIB_drawSettings GRRLIB_Settings; +extern Mtx GXmodelView2D; + +static guVector axis = (guVector){0, 0, 1}; + +/** + * Draw a texture. + * @param xpos Specifies the x-coordinate of the upper-left corner. + * @param ypos Specifies the y-coordinate of the upper-left corner. + * @param tex The texture to draw. + * @param degrees Angle of rotation. + * @param scaleX Specifies the x-coordinate scale. -1 could be used for flipping the texture horizontally. + * @param scaleY Specifies the y-coordinate scale. -1 could be used for flipping the texture vertically. + * @param color Color in RGBA format. + */ +void GRRLIB_DrawImg (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, const f32 degrees, const f32 scaleX, const f32 scaleY, const u32 color) { + GXTexObj texObj; + u16 width, height; + Mtx m, m1, m2, mv; + + if (tex == NULL || tex->data == NULL) return; + + GX_InitTexObj(&texObj, tex->data, tex->w, tex->h, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0); + guMtxRotAxisDeg(m2, &axis, degrees); + guMtxConcat (m2, m1, m); + + width = tex->w * 0.5; + height = tex->h * 0.5; + + guMtxTransApply(m, m, + xpos +width +tex->handlex + -tex->offsetx +( scaleX *(-tex->handley *sin(-DegToRad(degrees)) + -tex->handlex *cos(-DegToRad(degrees))) ), + ypos +height +tex->handley + -tex->offsety +( scaleY *(-tex->handley *cos(-DegToRad(degrees)) + +tex->handlex *sin(-DegToRad(degrees))) ), + 0); + guMtxConcat(GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(0, 0); + + GX_Position3f32(width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(1, 0); + + GX_Position3f32(width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(1, 1); + + GX_Position3f32(-width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(0, 1); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +/** + * Draw a textured quad. + * @param pos Vector array of the 4 points. + * @param tex The texture to draw. + * @param color Color in RGBA format. + */ +void GRRLIB_DrawImgQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color) { + GXTexObj texObj; + Mtx m, m1, m2, mv; + + if (tex == NULL || tex->data == NULL) return; + + GX_InitTexObj(&texObj, tex->data, tex->w, tex->h, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, 1, 1, 1.0); + guMtxRotAxisDeg(m2, &axis, 0); + guMtxConcat (m2, m1, m); + guMtxConcat (GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(pos[0].x, pos[0].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(0, 0); + + GX_Position3f32(pos[1].x, pos[1].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(1, 0); + + GX_Position3f32(pos[2].x, pos[2].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(1, 1); + + GX_Position3f32(pos[3].x, pos[3].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(0, 1); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +/** + * Draw a tile. + * @param xpos Specifies the x-coordinate of the upper-left corner. + * @param ypos Specifies the y-coordinate of the upper-left corner. + * @param tex The texture containing the tile to draw. + * @param degrees Angle of rotation. + * @param scaleX Specifies the x-coordinate scale. -1 could be used for flipping the texture horizontally. + * @param scaleY Specifies the y-coordinate scale. -1 could be used for flipping the texture vertically. + * @param color Color in RGBA format. + * @param frame Specifies the frame to draw. + */ +void GRRLIB_DrawTile (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, const f32 degrees, const f32 scaleX, const f32 scaleY, const u32 color, const int frame) { + GXTexObj texObj; + f32 width, height; + Mtx m, m1, m2, mv; + f32 s1, s2, t1, t2; + + if (tex == NULL || tex->data == NULL) return; + + // The 0.001f/x is the frame correction formula by spiffen + s1 = (frame % tex->nbtilew) * tex->ofnormaltexx; + s2 = s1 + tex->ofnormaltexx; + t1 = (int)(frame/tex->nbtilew) * tex->ofnormaltexy; + t2 = t1 + tex->ofnormaltexy; + + GX_InitTexObj(&texObj, tex->data, + tex->tilew * tex->nbtilew, tex->tileh * tex->nbtileh, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + width = tex->tilew * 0.5f; + height = tex->tileh * 0.5f; + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0f); + guMtxRotAxisDeg(m2, &axis, degrees); + guMtxConcat (m2, m1, m); + + guMtxTransApply(m, m, + xpos +width +tex->handlex + -tex->offsetx +( scaleX *(-tex->handley *sin(-DegToRad(degrees)) + -tex->handlex *cos(-DegToRad(degrees))) ), + ypos +height +tex->handley + -tex->offsety +( scaleY *(-tex->handley *cos(-DegToRad(degrees)) + +tex->handlex *sin(-DegToRad(degrees))) ), + 0); + + guMtxConcat(GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(-width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +/** + * Draw a part of a texture. + * @param xpos Specifies the x-coordinate of the upper-left corner. + * @param ypos Specifies the y-coordinate of the upper-left corner. + * @param partx Specifies the x-coordinate of the upper-left corner in the texture. + * @param party Specifies the y-coordinate of the upper-left corner in the texture. + * @param partw Specifies the width in the texture. + * @param parth Specifies the height in the texture. + * @param tex The texture containing the tile to draw. + * @param degrees Angle of rotation. + * @param scaleX Specifies the x-coordinate scale. -1 could be used for flipping the texture horizontally. + * @param scaleY Specifies the y-coordinate scale. -1 could be used for flipping the texture vertically. + * @param color Color in RGBA format. + */ +void GRRLIB_DrawPart (const f32 xpos, const f32 ypos, const f32 partx, const f32 party, const f32 partw, const f32 parth, const GRRLIB_texImg *tex, const f32 degrees, const f32 scaleX, const f32 scaleY, const u32 color) { + GXTexObj texObj; + f32 width, height; + Mtx m, m1, m2, mv; + f32 s1, s2, t1, t2; + + if (tex == NULL || tex->data == NULL) return; + + // The 0.001f/x is the frame correction formula by spiffen + s1 = (partx /tex->w) +(0.001f /tex->w); + s2 = ((partx + partw)/tex->w) -(0.001f /tex->w); + t1 = (party /tex->h) +(0.001f /tex->h); + t2 = ((party + parth)/tex->h) -(0.001f /tex->h); + + GX_InitTexObj(&texObj, tex->data, + tex->w, tex->h, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + width = partw * 0.5f; + height = parth * 0.5f; + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, scaleX, scaleY, 1.0f); + guMtxRotAxisDeg(m2, &axis, degrees); + guMtxConcat (m2, m1, m); + + guMtxTransApply(m, m, + xpos +width +tex->handlex + -tex->offsetx +( scaleX *(-tex->handley *sin(-DegToRad(degrees)) + -tex->handlex *cos(-DegToRad(degrees))) ), + ypos +height +tex->handley + -tex->offsety +( scaleY *(-tex->handley *cos(-DegToRad(degrees)) + +tex->handlex *sin(-DegToRad(degrees))) ), + 0); + + guMtxConcat(GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(-width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(width, -height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(-width, height, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +/** + * Draw a tile in a quad. + * @param pos Vector array of the 4 points. + * @param tex The texture to draw. + * @param color Color in RGBA format. + * @param frame Specifies the frame to draw. + */ +void GRRLIB_DrawTileQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color, const int frame) { + GXTexObj texObj; + Mtx m, m1, m2, mv; + f32 s1, s2, t1, t2; + + if (tex == NULL || tex->data == NULL) return; + + // The 0.001f/x is the frame correction formula by spiffen + s1 = (( (frame %tex->nbtilew) ) /(f32)tex->nbtilew) +(0.001f /tex->w); + s2 = (( (frame %tex->nbtilew) +1) /(f32)tex->nbtilew) -(0.001f /tex->w); + t1 = (((int)(frame /tex->nbtilew) ) /(f32)tex->nbtileh) +(0.001f /tex->h); + t2 = (((int)(frame /tex->nbtilew) +1) /(f32)tex->nbtileh) -(0.001f /tex->h); + + GX_InitTexObj(&texObj, tex->data, + tex->tilew * tex->nbtilew, tex->tileh * tex->nbtileh, + GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); + + if (GRRLIB_Settings.antialias == false) { + GX_InitTexObjLOD(&texObj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + GX_SetCopyFilter(GX_FALSE, rmode->sample_pattern, GX_FALSE, rmode->vfilter); + } + else { + GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter); + } + + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + guMtxIdentity (m1); + guMtxScaleApply(m1, m1, 1, 1, 1.0f); + guMtxRotAxisDeg(m2, &axis, 0); + guMtxConcat (m2, m1, m); + guMtxConcat (GXmodelView2D, m, mv); + + GX_LoadPosMtxImm(mv, GX_PNMTX0); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(pos[0].x, pos[0].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t1); + + GX_Position3f32(pos[1].x, pos[1].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t1); + + GX_Position3f32(pos[2].x, pos[2].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s2, t2); + + GX_Position3f32(pos[3].x, pos[3].y, 0); + GX_Color1u32 (color); + GX_TexCoord2f32(s1, t2); + GX_End(); + GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); +} + +/** + * Call this function after drawing. + */ +void GRRLIB_Render (void) { + GX_DrawDone(); // Tell the GX engine we are done drawing + GX_InvalidateTexAll(); + + fb ^= 1; // Toggle framebuffer index + + GX_SetZMode (GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp (xfb[fb], GX_TRUE); + + VIDEO_SetNextFramebuffer(xfb[fb]); // Select eXternal Frame Buffer + VIDEO_Flush(); // Flush video buffer to screen + VIDEO_WaitVSync(); // Wait for screen to update + // Interlaced screens require two frames to update + if (rmode->viTVMode &VI_NON_INTERLACE) VIDEO_WaitVSync(); +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_snapshot.c b/lib/grrlib/GRRLIB/GRRLIB_snapshot.c new file mode 100644 index 0000000..8931ec4 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_snapshot.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include + +/** + * Make a snapshot of the screen in a texture WITHOUT ALPHA LAYER. + * @param posx top left corner of the grabbed part. + * @param posy top left corner of the grabbed part. + * @param tex A pointer to a texture representing the screen or NULL if an error occurs. + * @param clear When this flag is set to true, the screen is cleared after copy. + */ +void GRRLIB_Screen2Texture (int posx, int posy, GRRLIB_texImg *tex, bool clear) { + if(tex->data != NULL) { + GX_SetTexCopySrc(posx, posy, tex->w, tex->h); + GX_SetTexCopyDst(tex->w, tex->h, GX_TF_RGBA8, GX_FALSE); + GX_CopyTex(tex->data, GX_FALSE); + GX_PixModeSync(); + GRRLIB_FlushTex(tex); + if(clear) + GX_CopyDisp(xfb[!fb], GX_TRUE); + } +} + +/** + * Start GX compositing process. + * @see GRRLIB_CompoEnd + */ +void GRRLIB_CompoStart (void) { + GX_SetPixelFmt(GX_PF_RGBA6_Z24, GX_ZC_LINEAR); + GX_PokeAlphaRead(GX_READ_NONE); +} + +/** + * End GX compositing process (Make a snapshot of the screen in a texture WITH ALPHA LAYER). + * EFB is cleared after this function. + * @see GRRLIB_CompoStart + * @param posx top left corner of the grabbed part. + * @param posy top left corner of the grabbed part. + * @param tex A pointer to a texture representing the screen or NULL if an error occurs. + */ +void GRRLIB_CompoEnd(int posx, int posy, GRRLIB_texImg *tex) { + GRRLIB_Screen2Texture(posx, posy, tex, GX_TRUE); + + if (rmode->aa) GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); + else GX_SetPixelFmt(GX_PF_RGB8_Z24 , GX_ZC_LINEAR); +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_texEdit.c b/lib/grrlib/GRRLIB/GRRLIB_texEdit.c new file mode 100644 index 0000000..622dfd2 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_texEdit.c @@ -0,0 +1,388 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include + +/** + * This structure contains information about the type, size, and layout of a file that containing a device-independent bitmap (DIB). + */ +typedef struct tagBITMAPFILEHEADER { + u16 bfType; /**< Specifies the file type. It must be set to the signature word BM (0x4D42) to indicate bitmap. */ + u32 bfSize; /**< Specifies the size, in bytes, of the bitmap file. */ + u16 bfReserved1; /**< Reserved; set to zero. */ + u16 bfReserved2; /**< Reserved; set to zero. */ + u32 bfOffBits; /**< Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits. */ +} BITMAPFILEHEADER; +/** + * This structure contains information about the dimensions and color format of a device-independent bitmap (DIB). + */ +typedef struct tagBITMAPINFOHEADER { + u32 biSize; /**< Specifies the size of the structure, in bytes. */ + u32 biWidth; /**< Specifies the width of the bitmap, in pixels. */ + u32 biHeight; /**< Specifies the height of the bitmap, in pixels. */ + u16 biPlanes; /**< Specifies the number of planes for the target device. */ + u16 biBitCount; /**< Specifies the number of bits per pixel. */ + u32 biCompression; /**< Specifies the type of compression for a compressed bottom-up bitmap.*/ + u32 biSizeImage; /**< Specifies the size, in bytes, of the image. */ + u32 biXPelsPerMeter; /**< Specifies the horizontal resolution, in pixels per meter, of the target device for the bitmap. */ + u32 biYPelsPerMeter; /**< Specifies the vertical resolution, in pixels per meter, of the target device for the bitmap. */ + u32 biClrUsed; /**< Specifies the number of color indexes in the color table that are actually used by the bitmap. */ + u32 biClrImportant; /**< Specifies the number of color indexes required for displaying the bitmap. */ +} BITMAPINFOHEADER; +/** + * The RGBQUAD structure describes a color consisting of relative intensities of + * red, green, and blue. The bmiColors member of the BITMAPINFO structure + * consists of an array of RGBQUAD structures. + */ +typedef struct tagRGBQUAD { + u8 rgbBlue; /**< Specifies the intensity of blue in the color. */ + u8 rgbGreen; /**< Specifies the intensity of green in the color. */ + u8 rgbRed; /**< Specifies the intensity of red in the color. */ + u8 rgbReserved; /**< Not used; must be set to zero. */ +} RGBQUAD; + +/** + * Convert a raw BMP (RGB, no alpha) to 4x4RGBA. + * @author DragonMinded + * @param src Source. + * @param dst Destination. + * @param width Width. + * @param height Height. +*/ +static +void RawTo4x4RGBA (const u8 *src, void *dst, + const uint width, const uint height) { + uint block; + uint i; + u8 c; + u8 argb; + + u8 *p = (u8*)dst; + + for (block = 0; block < height; block += 4) { + for (i = 0; i < width; i += 4) { + // Alpha and Red + for (c = 0; c < 4; ++c) { + for (argb = 0; argb < 4; ++argb) { + // Alpha pixels + *p++ = 255; + // Red pixels + *p++ = src[((i + argb) + ((block + c) * width)) * 3]; + } + } + + // Green and Blue + for (c = 0; c < 4; ++c) { + for (argb = 0; argb < 4; ++argb) { + // Green pixels + *p++ = src[(((i + argb) + ((block + c) * width)) * 3) + 1]; + // Blue pixels + *p++ = src[(((i + argb) + ((block + c) * width)) * 3) + 2]; + } + } + } + } +} + +/** + * Load a texture from a buffer. + * @param my_img The JPEG, PNG or Bitmap buffer to load. + * @return A GRRLIB_texImg structure filled with image information. + */ +GRRLIB_texImg* GRRLIB_LoadTexture (const u8 *my_img) { + if (my_img[0]==0xFF && my_img[1]==0xD8 && my_img[2]==0xFF) + return (GRRLIB_LoadTextureJPG(my_img)); + else if (my_img[0]=='B' && my_img[1]=='M') + return (GRRLIB_LoadTextureBMP(my_img)); + else + return (GRRLIB_LoadTexturePNG(my_img)); +} + +/** + * Load a texture from a buffer. + * @param my_png the PNG buffer to load. + * @return A GRRLIB_texImg structure filled with image information. + * If image size is not correct, the texture will be completely transparent. + */ +GRRLIB_texImg* GRRLIB_LoadTexturePNG (const u8 *my_png) { + int width = 0, height = 0; + PNGUPROP imgProp; + IMGCTX ctx; + GRRLIB_texImg *my_texture = calloc(1, sizeof(GRRLIB_texImg)); + + if(my_texture != NULL) { + ctx = PNGU_SelectImageFromBuffer(my_png); + PNGU_GetImageProperties(ctx, &imgProp); + my_texture->data = PNGU_DecodeTo4x4RGBA8_EX(ctx, imgProp.imgWidth, imgProp.imgHeight, &width, &height, NULL); + if(my_texture->data != NULL) { + my_texture->w = width; + my_texture->h = height; + GRRLIB_SetHandle( my_texture, 0, 0 ); + if(imgProp.imgWidth != width || imgProp.imgHeight != height) { + // PNGU has resized the texture + memset(my_texture->data, 0, (my_texture->h * my_texture->w) << 2); + } + GRRLIB_FlushTex( my_texture ); + } + PNGU_ReleaseImageContext(ctx); + } + return my_texture; +} + +/** + * Create an array of palette. + * @param my_bmp Bitmap buffer to parse. + * @param Size The number of palette to add. + * @return An array of palette. Memory must be deleted. + */ +static RGBQUAD* GRRLIB_CreatePalette (const u8 *my_bmp, u32 Size) { + u32 n, i = 0; + RGBQUAD *Palette = calloc(Size, sizeof(RGBQUAD)); + for(n=0; ndata = memalign(32, MyBitmapHeader.biWidth * MyBitmapHeader.biHeight * 4); + if(my_texture->data != NULL && MyBitmapFileHeader.bfType == 0x4D42) { + my_texture->w = MyBitmapHeader.biWidth; + my_texture->h = MyBitmapHeader.biHeight; + switch(MyBitmapHeader.biBitCount) { + case 32: // RGBA images + i = 54; + for(y=MyBitmapHeader.biHeight-1; y>=0; y--) { + for(x=0; x=0; y--) { + for(x=0; x=0; y--) { + for(x=0; x=0; y--) { + for(x=0; x> ((x % 2) ? 0 : 4)) & 0x0F; + GRRLIB_SetPixelTotexImg(x, y, my_texture, + RGBA(Palette[pal_ref].rgbRed, + Palette[pal_ref].rgbGreen, + Palette[pal_ref].rgbBlue, + 0xFF)); + } + i += BufferSize; // Padding + } + free(Palette); + break; + case 1: // black & white images + BufferSize = (int)(MyBitmapHeader.biWidth / 8.0); + while(8*BufferSize < MyBitmapHeader.biWidth) { + BufferSize++; + } + while(BufferSize % 4) { + BufferSize++; + } + Palette = GRRLIB_CreatePalette(&my_bmp[54], 2); + i = 62; // 54 + (MyBitmapHeader.biBitCount * 4) + for(y=MyBitmapHeader.biHeight-1; y>=0; y--) { + for(x=0; x> (-x%8+7)) & 0x01; + GRRLIB_SetPixelTotexImg(x, y, my_texture, + RGBA(Palette[pal_ref].rgbRed, + Palette[pal_ref].rgbGreen, + Palette[pal_ref].rgbBlue, + 0xFF)); + } + i += BufferSize; // Padding + } + free(Palette); + break; + default: + GRRLIB_ClearTex(my_texture); + } + GRRLIB_SetHandle( my_texture, 0, 0 ); + GRRLIB_FlushTex( my_texture ); + } + } + return my_texture; +} + +/** + * Load a texture from a buffer. + * Take care to have the JPG finish with 0xFF 0xD9! + * @param my_jpg The JPEG buffer to load. + * @return A GRRLIB_texImg structure filled with image information. + */ +GRRLIB_texImg* GRRLIB_LoadTextureJPG (const u8 *my_jpg) { + int n = 0; + + if ((my_jpg[0]==0xFF) && (my_jpg[1]==0xD8) && (my_jpg[2]==0xFF)) { + while(true) { + if ((my_jpg[n]==0xFF) && (my_jpg[n+1]==0xD9)) + break; + n++; + } + n+=2; + } + + return GRRLIB_LoadTextureJPGEx(my_jpg, n); +} + +/** + * Load a texture from a buffer. + * @author DrTwox + * @param my_jpg The JPEG buffer to load. + * @param my_size Size of the JPEG buffer to load. + * @return A GRRLIB_texImg structure filled with image information. + */ +GRRLIB_texImg* GRRLIB_LoadTextureJPGEx (const u8 *my_jpg, const int my_size) { + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + GRRLIB_texImg *my_texture = calloc(1, sizeof(GRRLIB_texImg)); + unsigned int i; + + if(my_texture == NULL) + return NULL; + + jpeg_create_decompress(&cinfo); + cinfo.err = jpeg_std_error(&jerr); + cinfo.progress = NULL; + jpeg_mem_src(&cinfo, (unsigned char *)my_jpg, my_size); + jpeg_read_header(&cinfo, TRUE); + if(cinfo.jpeg_color_space == JCS_GRAYSCALE) { + cinfo.out_color_space = JCS_RGB; + } + jpeg_start_decompress(&cinfo); + unsigned char *tempBuffer = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); + JSAMPROW row_pointer[1]; + row_pointer[0] = malloc(cinfo.output_width * cinfo.output_components); + size_t location = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, row_pointer, 1); + for (i = 0; i < cinfo.image_width * cinfo.output_components; i++) { + /* Put the decoded scanline into the tempBuffer */ + tempBuffer[ location++ ] = row_pointer[0][i]; + } + } + + /* Create a buffer to hold the final texture */ + my_texture->data = memalign(32, cinfo.output_width * cinfo.output_height * 4); + RawTo4x4RGBA(tempBuffer, my_texture->data, cinfo.output_width, cinfo.output_height); + + /* Done - Do cleanup and release allocated memory */ + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + free(row_pointer[0]); + free(tempBuffer); + + my_texture->w = cinfo.output_width; + my_texture->h = cinfo.output_height; + GRRLIB_SetHandle( my_texture, 0, 0 ); + GRRLIB_FlushTex( my_texture ); + return my_texture; +} diff --git a/lib/grrlib/GRRLIB/GRRLIB_ttf.c b/lib/grrlib/GRRLIB/GRRLIB_ttf.c new file mode 100644 index 0000000..c0556c3 --- /dev/null +++ b/lib/grrlib/GRRLIB/GRRLIB_ttf.c @@ -0,0 +1,252 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +#if 0 + +#include "grrlib/GRRLIB_private.h" +#include +#include +#include +#include FT_FREETYPE_H + +static FT_Library ftLibrary; /**< A handle to a FreeType library instance. */ + +// Static function prototypes +static void DrawBitmap(FT_Bitmap *bitmap, int offset, int top, const u8 cR, const u8 cG, const u8 cB); + + +/** + * Initialize FreeType library. + * @return int 0=OK; -1=Failed + */ +int GRRLIB_InitTTF () { + if (FT_Init_FreeType(&ftLibrary)) { + return -1; + } + return 0; +} + +/** + * Call this when your done with FreeType. + */ +void GRRLIB_ExitTTF (void) { + FT_Done_FreeType(ftLibrary); +} + +/** + * Load a TTF from a buffer. + * @param file_base Buffer with TTF data. You must not deallocate the memory before calling GRRLIB_FreeTTF. + * @param file_size Size of the TTF buffer. + * @return A handle to a given TTF font object. + * @see GRRLIB_FreeTTF + */ +GRRLIB_ttfFont* GRRLIB_LoadTTF (const u8* file_base, s32 file_size) { + FT_Face Face; + GRRLIB_ttfFont* myFont = (GRRLIB_ttfFont*)malloc(sizeof(GRRLIB_ttfFont)); + FT_New_Memory_Face(ftLibrary, file_base, file_size, 0, &Face); + myFont->kerning = FT_HAS_KERNING(Face); +/* + if (FT_Set_Pixel_Sizes(Face, 0, fontSize)) { + FT_Set_Pixel_Sizes(Face, 0, 12); + } +*/ + myFont->face = Face; + return myFont; +} + +/** + * Free memory allocated by TTF fonts. + * @param myFont A TTF. + */ +void GRRLIB_FreeTTF (GRRLIB_ttfFont *myFont) { + if(myFont) { + FT_Done_Face(myFont->face); + free(myFont); + myFont = NULL; + } +} + +/** + * Print function for TTF font. + * @param x Specifies the x-coordinate of the upper-left corner of the text. + * @param y Specifies the y-coordinate of the upper-left corner of the text. + * @param myFont A TTF. + * @param string Text to draw. + * @param fontSize Size of the font. + * @param color Text color in RGBA format. + */ +void GRRLIB_PrintfTTF(int x, int y, GRRLIB_ttfFont *myFont, const char *string, unsigned int fontSize, const u32 color) { + if(myFont == NULL || string == NULL) + return; + + size_t length = strlen(string) + 1; + wchar_t *utf32 = (wchar_t*)malloc(length * sizeof(wchar_t)); + if(utf32) { + length = mbstowcs(utf32, string, length); + if(length > 0) { + utf32[length] = L'\0'; + GRRLIB_PrintfTTFW(x, y, myFont, utf32, fontSize, color); + } + free(utf32); + } +} + +/** + * Print function for TTF font. + * @author wplaat and DrTwox + * @param x Specifies the x-coordinate of the upper-left corner of the text. + * @param y Specifies the y-coordinate of the upper-left corner of the text. + * @param myFont A TTF. + * @param utf32 Text to draw. + * @param fontSize Size of the font. + * @param color Text color in RGBA format. + */ +void GRRLIB_PrintfTTFW(int x, int y, GRRLIB_ttfFont *myFont, const wchar_t *utf32, unsigned int fontSize, const u32 color) { + if(myFont == NULL || utf32 == NULL) + return; + + FT_Face Face = (FT_Face)myFont->face; + int penX = 0; + int penY = fontSize; + FT_GlyphSlot slot = Face->glyph; + FT_UInt glyphIndex = 0; + FT_UInt previousGlyph = 0; + u8 cR = R(color), cG = G(color), cB = B(color); + + if (FT_Set_Pixel_Sizes(Face, 0, fontSize)) { + FT_Set_Pixel_Sizes(Face, 0, 12); + } + + /* Loop over each character, until the + * end of the string is reached, or until the pixel width is too wide */ + while(*utf32) { + glyphIndex = FT_Get_Char_Index(myFont->face, *utf32++); + + if (myFont->kerning && previousGlyph && glyphIndex) { + FT_Vector delta; + FT_Get_Kerning(myFont->face, previousGlyph, glyphIndex, FT_KERNING_DEFAULT, &delta); + penX += delta.x >> 6; + } + if (FT_Load_Glyph(myFont->face, glyphIndex, FT_LOAD_RENDER)) { + continue; + } + + DrawBitmap(&slot->bitmap, + penX + slot->bitmap_left + x, + penY - slot->bitmap_top + y, + cR, cG, cB); + penX += slot->advance.x >> 6; + previousGlyph = glyphIndex; + } +} + +/** + * Draw a character on the screen. + * @param bitmap Bitmap to draw. + * @param offset x-coordinate offset. + * @param top y-coordinate. + * @param cR Red component of the colour. + * @param cG Green component of the colour. + * @param cB Blue component of the colour. + */ +static void DrawBitmap(FT_Bitmap *bitmap, int offset, int top, const u8 cR, const u8 cG, const u8 cB) { + FT_Int i, j, p, q; + FT_Int x_max = offset + bitmap->width; + FT_Int y_max = top + bitmap->rows; + + for ( i = offset, p = 0; i < x_max; i++, p++ ) { + for ( j = top, q = 0; j < y_max; j++, q++ ) { + GX_Begin(GX_POINTS, GX_VTXFMT0, 1); + GX_Position3f32(i, j, 0); + GX_Color4u8(cR, cG, cB, + bitmap->buffer[ q * bitmap->width + p ]); + GX_End(); + } + } +} + +/** + * Get the width of a text in pixel. + * @param myFont A TTF. + * @param string The text to check. + * @param fontSize The size of the font. + * @return The width of a text in pixel. + */ +unsigned int GRRLIB_WidthTTF(GRRLIB_ttfFont *myFont, const char *string, unsigned int fontSize) { + if(myFont == NULL || string == NULL) { + return 0; + } + unsigned int penX; + size_t length = strlen(string) + 1; + wchar_t *utf32 = (wchar_t*)malloc(length * sizeof(wchar_t)); + length = mbstowcs(utf32, string, length); + utf32[length] = L'\0'; + + penX = GRRLIB_WidthTTFW(myFont, utf32, fontSize); + + free(utf32); + + return penX; +} + +/** + * Get the width of a text in pixel. + * @param myFont A TTF. + * @param utf32 The text to check. + * @param fontSize The size of the font. + * @return The width of a text in pixel. + */ +unsigned int GRRLIB_WidthTTFW(GRRLIB_ttfFont *myFont, const wchar_t *utf32, unsigned int fontSize) { + if(myFont == NULL || utf32 == NULL) { + return 0; + } + + FT_Face Face = (FT_Face)myFont->face; + unsigned int penX = 0; + FT_UInt glyphIndex; + FT_UInt previousGlyph = 0; + + if(FT_Set_Pixel_Sizes(myFont->face, 0, fontSize)) { + FT_Set_Pixel_Sizes(myFont->face, 0, 12); + } + + while(*utf32) { + glyphIndex = FT_Get_Char_Index(myFont->face, *utf32++); + + if(myFont->kerning && previousGlyph && glyphIndex) { + FT_Vector delta; + FT_Get_Kerning(Face, previousGlyph, glyphIndex, FT_KERNING_DEFAULT, &delta); + penX += delta.x >> 6; + } + if(FT_Load_Glyph(Face, glyphIndex, FT_LOAD_RENDER)) { + continue; + } + + penX += Face->glyph->advance.x >> 6; + previousGlyph = glyphIndex; + } + + return penX; +} + +#endif + diff --git a/lib/grrlib/GRRLIB/Makefile b/lib/grrlib/GRRLIB/Makefile new file mode 100644 index 0000000..7dcecd8 --- /dev/null +++ b/lib/grrlib/GRRLIB/Makefile @@ -0,0 +1,62 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +include $(DEVKITPPC)/wii_rules + +NULLSTR := +PWD := $(subst $(NULLSTR) ,\ ,$(shell pwd)) + +#PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +#CC := $(PREFIX)gcc +#AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii + +#INCLUDE := -I../lib/jpeg -I../lib/pngu -I../lib/freetype/include -I$(PWD) -I$(LIBOGC_INC) +INCLUDE := -I../../jpeg/include -I../../../source/pngu -I$(PWD) -I$(LIBOGC_INC) +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -Os -Wall $(MACHDEP) $(INCLUDE) + +LIB := grrlib +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +ARC := lib$(LIB).a +HDR := $(LIB).h +INL := $(wildcard $(LIB)/*.h) + +#all : $(OFILES) $(ARC) +# $(AR) -r $(ARC) $(OFILES) + +all : $(ARC) + +$(ARC) : $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) + +install_ogc : + mkdir -p $(LIBOGC_LIB) $(LIBOGC_INC) $(LIBOGC_INC)/grrlib + cp -f $(ARC) $(LIBOGC_LIB)/ + cp -f $(HDR) $(LIBOGC_INC)/ + cp -f $(INL) $(LIBOGC_INC)/grrlib + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../include +DEST_LIB := ../lib$(DKV) + +install : + mkdir -p $(DEST_LIB) $(DEST_INC) $(DEST_INC)/grrlib + cp -f $(ARC) $(DEST_LIB)/ + cp -f $(HDR) $(DEST_INC)/ + cp -f $(INL) $(DEST_INC)/grrlib + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/grrlib/GRRLIB/Makefile-orig b/lib/grrlib/GRRLIB/Makefile-orig new file mode 100644 index 0000000..8e619b9 --- /dev/null +++ b/lib/grrlib/GRRLIB/Makefile-orig @@ -0,0 +1,45 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +NULLSTR := +PWD := $(subst $(NULLSTR) ,\ ,$(shell pwd)) + +PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +CC := $(PREFIX)gcc +AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii + +INCLUDE := -I../lib/jpeg -I../lib/pngu -I../lib/freetype/include -I$(PWD) -I$(LIBOGC_INC) +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -O2 -Wall $(MACHDEP) $(INCLUDE) + +LIB := grrlib +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +ARC := lib$(LIB).a +HDR := $(LIB).h +INL := $(wildcard $(LIB)/*.h) + +all : $(OFILES) + $(AR) -r $(ARC) $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) + +install : + mkdir -p $(LIBOGC_LIB) $(LIBOGC_INC) $(LIBOGC_INC)/grrlib + cp -f $(ARC) $(LIBOGC_LIB)/ + cp -f $(HDR) $(LIBOGC_INC)/ + cp -f $(INL) $(LIBOGC_INC)/grrlib + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/grrlib/GRRLIB/grrlib.h b/lib/grrlib/GRRLIB/grrlib.h new file mode 100644 index 0000000..f5bce85 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib.h @@ -0,0 +1,264 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB.h + * GRRLIB user include file. + */ +/** + * @defgroup AllFunc Everything in GRRLIB + * This is the complete list of funtions, structures, defines, typedefs, enumerations and variables you may want to used to make your homebrew with GRRLIB. + * You simply need to include grrlib.h in your project to have access to all of these. + * @{ + */ + +#ifndef __GRRLIB_H__ +#define __GRRLIB_H__ + +/** + * Version information for GRRLIB. + */ +#define GRRLIB_VER_STRING "4.3.1" + +//============================================================================== +// Includes +//============================================================================== +#include +#include + +#if (_V_MAJOR_*10 + _V_MINOR_) < 18 +#define guVector Vector +#endif +//============================================================================== + +//============================================================================== +// C++ header +//============================================================================== +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +//============================================================================== +// Extra standard declarations +//============================================================================== +typedef unsigned int uint;/**< The uint keyword signifies an integral type. */ + +//============================================================================== +// Primitive colour macros +//============================================================================== +#define R(c) (((c) >>24) &0xFF) /**< Exract Red component of colour. */ +#define G(c) (((c) >>16) &0xFF) /**< Exract Green component of colour. */ +#define B(c) (((c) >> 8) &0xFF) /**< Exract Blue component of colour. */ +#define A(c) ( (c) &0xFF) /**< Exract Alpha component of colour. */ + +/** + * Build an RGB pixel from components. + * @param r Red component. + * @param g Green component. + * @param b Blue component. + * @param a Alpha component. + */ +#define RGBA(r,g,b,a) ( (u32)( ( ((u32)(r)) <<24) | \ + ((((u32)(g)) &0xFF) <<16) | \ + ((((u32)(b)) &0xFF) << 8) | \ + ( ((u32)(a)) &0xFF ) ) ) + +//============================================================================== +// typedefs, enumerators & structs +//============================================================================== +/** + * GRRLIB Blending Modes. + */ +typedef enum GRRLIB_blendMode { + GRRLIB_BLEND_ALPHA = 0, /**< Alpha Blending. */ + GRRLIB_BLEND_ADD = 1, /**< Additive Blending. */ + GRRLIB_BLEND_SCREEN = 2, /**< Alpha Light Blending. */ + GRRLIB_BLEND_MULTI = 3, /**< Multiply Blending. */ + GRRLIB_BLEND_INV = 4, /**< Invert Color Blending. */ +} GRRLIB_blendMode; + +#define GRRLIB_BLEND_NONE (GRRLIB_BLEND_ALPHA) /**< Alias for GRRLIB_BLEND_ALPHA. */ +#define GRRLIB_BLEND_LIGHT (GRRLIB_BLEND_ADD) /**< Alias for GRRLIB_BLEND_ADD. */ +#define GRRLIB_BLEND_SHADE (GRRLIB_BLEND_MULTI) /**< Alias for GRRLIB_BLEND_MULTI. */ + +//------------------------------------------------------------------------------ +/** + * Structure to hold the current drawing settings. + */ +typedef struct GRRLIB_drawSettings { + bool antialias; /**< AntiAlias is enabled when set to true. */ + GRRLIB_blendMode blend; /**< Blending Mode. */ + int lights; /**< Active lights. */ +} GRRLIB_drawSettings; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the texture information. + */ +typedef struct GRRLIB_texImg { + int tex_format; + int tex_lod; + uint w; /**< The width of the texture in pixels. */ + uint h; /**< The height of the texture in pixels. */ + int handlex; /**< Texture handle x. */ + int handley; /**< Texture handle y. */ + int offsetx; /**< Texture offset x. */ + int offsety; /**< Texture offset y. */ + + bool tiledtex; /**< Texture is tiled if set to true. */ + uint tilew; /**< The width of one tile in pixels. */ + uint tileh; /**< The height of one tile in pixels. */ + uint nbtilew; /**< Number of tiles for the x axis. */ + uint nbtileh; /**< Number of tiles for the y axis. */ + uint tilestart; /**< Offset to tile starting position. */ + f32 ofnormaltexx;/**< Offset of normalized texture on x. */ + f32 ofnormaltexy;/**< Offset of normalized texture on y. */ + + void *data; /**< Pointer to the texture data. */ +} GRRLIB_texImg; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the bytemap character information. + */ +typedef struct GRRLIB_bytemapChar { + u8 width; /**< Character width. */ + u8 height; /**< Character height. */ + s8 relx; /**< Horizontal offset relative to cursor (-128 to 127). */ + s8 rely; /**< Vertical offset relative to cursor (-128 to 127). */ + u8 kerning; /**< Kerning (Horizontal cursor shift after drawing the character). */ + u8 *data; /**< Character data (uncompressed, 8 bits per pixel). */ +} GRRLIB_bytemapChar; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the bytemap font information. + */ +typedef struct GRRLIB_bytemapFont { + char *name; /**< Font name. */ + u32 *palette; /**< Font palette. */ + u16 nbChar; /**< Number of characters in font. */ + u8 version; /**< Version. */ + s8 tracking; /**< Tracking (Add-space after each char) (-128 to 127). */ + + GRRLIB_bytemapChar charDef[256]; /**< Array of bitmap characters. */ +} GRRLIB_bytemapFont; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the TTF information. + */ +typedef struct GRRLIB_Font { + void *face; /**< A TTF face object. */ + bool kerning; /**< true whenever a face object contains kerning data that can be accessed with FT_Get_Kerning. */ +} GRRLIB_ttfFont; + +//============================================================================== +// Allow general access to screen and frame information +//============================================================================== +#if defined __GRRLIB_CORE__ +# define GRR_EXTERN +# define GRR_INIT(v) = v +# define GRR_INITS(...) = { __VA_ARGS__ } +#else +# define GRR_EXTERN extern +# define GRR_INIT(v) +# define GRR_INITS(...) +#endif + +GRR_EXTERN GXRModeObj *rmode; +GRR_EXTERN void *xfb[2] GRR_INITS(NULL, NULL); +GRR_EXTERN u32 fb GRR_INIT(0); +//============================================================================== +// procedure and function prototypes +// Inline function handling - http://www.greenend.org.uk/rjk/2003/03/inline.html +//============================================================================== +#include "grrlib/GRRLIB__lib.h" + +#if defined __GRRLIB_CORE__ +# define INLINE +#else +# if __GNUC__ && !__GNUC_STDC_INLINE__ +# define INLINE static inline +# else +# define INLINE inline +# endif +#endif +#include "grrlib/GRRLIB__inline.h" + +//============================================================================== +// C++ footer +//============================================================================== +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif // __GRRLIB_H__ +/** @} */ // end of group +/** + * @mainpage GRRLIB Documentation + * @image html grrlib_logo.png + * Welcome to the GRRLIB documentation. + * A complete list of functions is available from this \ref AllFunc "page". + * + * @section Introduction + * GRRLIB is a C/C++ 2D/3D graphics library for Wii application developers. + * It is essentially a wrapper which presents a friendly interface to the Nintendo GX core. + * + * @section Links + * Forum: http://grrlib.santo.fr/forum\n + * Code: http://code.google.com/p/grrlib\n + * IRC: ##GRRLIB on EFnet + * + * @section Credits + * Project Leader : NoNameNo\n + * Documentation : Crayon, BlueChip\n + * Lead Coder : NoNameNo\n + * Support Coders : Crayon, Xane, DragonMinded, BlueChip\n + * Advisors : RedShade, Jespa\n + * + * @section Licence + * Copyright (c) 2010 The GRRLIB Team + * + * 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. + * + * @example template/source/main.c + * This example shows the minimum code required to use GRRLIB. + * It could be used as a template to start a new project. + * More elaborate examples can be found inside the \e examples folder. + */ diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB__inline.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB__inline.h new file mode 100644 index 0000000..773720f --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB__inline.h @@ -0,0 +1,145 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB__inline.h + * GRRLIB inline function prototypes. + * Do not include GRRLIB__inline.h directly, include only GRRLIB.h. + */ +/** + * @addtogroup AllFunc + * @{ + */ + +#ifndef __GRRLIB_H__ +# error Do not include GRRLIB__inline.h directly, include only GRRLIB.h +#endif + +#ifndef __GRRLIB_FNINLINE_H__ +#define __GRRLIB_FNINLINE_H__ + +//============================================================================== +// Prototypes for inlined functions +//============================================================================== + +//------------------------------------------------------------------------------ +// GRRLIB_cExtn.h - C extensions (helper functions) +INLINE u8 GRRLIB_ClampVar8 (f32 Value); + +//------------------------------------------------------------------------------ +// GRRLIB_clipping.h - Clipping control +INLINE void GRRLIB_ClipReset (void); +INLINE void GRRLIB_ClipDrawing (const int x, const int y, + const int width, const int height); + +//------------------------------------------------------------------------------ +// GRRLIB_collision.h - Collision detection +INLINE bool GRRLIB_PtInRect (const int hotx, const int hoty, + const int hotw, const int hoth, + const int wpadx, const int wpady); + +INLINE bool GRRLIB_RectInRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h); + +INLINE bool GRRLIB_RectOnRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h); + +//------------------------------------------------------------------------------ +// GRRLIB_fbComplex.h - +INLINE void GRRLIB_NPlot (const guVector v[], const u32 color[], + const long n); +INLINE void GRRLIB_NGone (const guVector v[], const u32 color[], + const long n); +INLINE void GRRLIB_NGoneFilled (const guVector v[], const u32 color[], + const long n); + +//------------------------------------------------------------------------------ +// GRRLIB_fbGX.h - +INLINE void GRRLIB_GXEngine (const guVector v[], const u32 color[], + const long n, const u8 fmt); + +//------------------------------------------------------------------------------ +// GRRLIB_fbSimple.h - +INLINE void GRRLIB_FillScreen (const u32 color); +INLINE void GRRLIB_Plot (const f32 x, const f32 y, const u32 color); +INLINE void GRRLIB_Line (const f32 x1, const f32 y1, + const f32 x2, const f32 y2, const u32 color); +INLINE void GRRLIB_Rectangle (const f32 x, const f32 y, + const f32 width, const f32 height, + const u32 color, const u8 filled); + +//------------------------------------------------------------------------------ +// GRRLIB_handle.h - Texture handle manipulation +INLINE void GRRLIB_SetHandle (GRRLIB_texImg *tex, const int x, const int y); +INLINE void GRRLIB_SetMidHandle (GRRLIB_texImg *tex, const bool enabled); + +//------------------------------------------------------------------------------ +// GRRLIB_pixel.h - Pixel manipulation +INLINE u32 GRRLIB_GetPixelFromtexImg (const int x, const int y, + const GRRLIB_texImg *tex); + +INLINE void GRRLIB_SetPixelTotexImg (const int x, const int y, + GRRLIB_texImg *tex, const u32 color); + +INLINE u32 GRRLIB_GetPixelFromFB (int x, int y); +INLINE void GRRLIB_SetPixelToFB (int x, int y, u32 pokeColor); + +//------------------------------------------------------------------------------ +// GRRLIB_settings.h - Rendering functions +INLINE void GRRLIB_SetBlend (const GRRLIB_blendMode blendmode); +INLINE GRRLIB_blendMode GRRLIB_GetBlend (void); +INLINE void GRRLIB_SetAntiAliasing (const bool aa); +INLINE bool GRRLIB_GetAntiAliasing (void); + +//------------------------------------------------------------------------------ +// GRRLIB_texSetup.h - Create and setup textures +#if 0 +INLINE GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h); +INLINE void GRRLIB_ClearTex (GRRLIB_texImg* tex); +INLINE void GRRLIB_FlushTex (GRRLIB_texImg *tex); +INLINE void GRRLIB_FreeTexture (GRRLIB_texImg *tex); +#endif +GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h); +void GRRLIB_ClearTex (GRRLIB_texImg* tex); +void GRRLIB_FlushTex (GRRLIB_texImg *tex); +void GRRLIB_FreeTexture (GRRLIB_texImg *tex); + +//============================================================================== +// Definitions of inlined functions +//============================================================================== +#include // C extensions (helper functions) +#include // Clipping control +#include // Collision detection +#include // Render to framebuffer: Complex primitives +#include // Render to framebuffer: Simple GX wrapper +#include // Render to framebuffer: Simple primitives +#include // Texture handle manipulation +#include // Pixel manipulation +#include // GRRLIB Settings +#include // Setup for textures + +#endif // __GRRLIB_FNINLINE_H__ +/** @} */ // end of group diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB__lib.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB__lib.h new file mode 100644 index 0000000..fafc194 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB__lib.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB__lib.h + * GRRLIB library function prototypes. + * Do not include GRRLIB__lib.h directly, include only GRRLIB.h. + */ +/** + * @addtogroup AllFunc + * @{ + */ + +#ifndef __GRRLIB_H__ +# error Do not include GRRLIB__lib.h directly, include only GRRLIB.h +#endif + +#ifndef __GRRLIB_FNLIB_H__ +#define __GRRLIB_FNLIB_H__ + +//============================================================================== +// Prototypes for library contained functions +//============================================================================== + +//------------------------------------------------------------------------------ +// GRRLIB_bmf.c - BitMapFont functions +GRRLIB_bytemapFont* GRRLIB_LoadBMF (const u8 my_bmf[] ); +void GRRLIB_FreeBMF (GRRLIB_bytemapFont *bmf); + +void GRRLIB_InitTileSet (GRRLIB_texImg *tex, + const uint tilew, const uint tileh, + const uint tilestart); + +//------------------------------------------------------------------------------ +// GRRLIB_bmfx.c - Bitmap f/x +void GRRLIB_BMFX_FlipH (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_FlipV (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Grayscale (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Sepia (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Invert (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Blur (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +void GRRLIB_BMFX_Scatter (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +void GRRLIB_BMFX_Pixelate (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +//------------------------------------------------------------------------------ +// GRRLIB_core.c - GRRLIB core functions +int GRRLIB_Init (void); +void GRRLIB_Exit (void); + +//------------------------------------------------------------------------------ +// GRRLIB_fbAdvanced.c - Render to framebuffer: Advanced primitives +void GRRLIB_Circle (const f32 x, const f32 y, const f32 radius, + const u32 color, const u8 filled); + +//------------------------------------------------------------------------------ +// GRRLIB_fileIO - File I/O (SD Card) +int GRRLIB_LoadFile (const char* filename, + unsigned char* *data); +GRRLIB_texImg* GRRLIB_LoadTextureFromFile (const char* filename); +bool GRRLIB_ScrShot (const char* filename); + +//------------------------------------------------------------------------------ +// GRRLIB_print.c - Will someone please tell me what these are :) +void GRRLIB_Printf (const f32 xpos, const f32 ypos, + const GRRLIB_texImg *tex, const u32 color, + const f32 zoom, const char *text, ...); + +void GRRLIB_PrintBMF (const f32 xpos, const f32 ypos, + const GRRLIB_bytemapFont *bmf, + const char *text, ...); + +//------------------------------------------------------------------------------ +// GRRLIB_render.c - Rendering functions +void GRRLIB_DrawImg (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color); + +void GRRLIB_DrawImgQuad (const guVector pos[4], GRRLIB_texImg *tex, + const u32 color); + +void GRRLIB_DrawTile (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color, const int frame); + +void GRRLIB_DrawPart (const f32 xpos, const f32 ypos, const f32 partx, const f32 party, + const f32 partw, const f32 parth, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color); + +void GRRLIB_DrawTileQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color, const int frame); + +void GRRLIB_Render (void); + +//------------------------------------------------------------------------------ +// GRRLIB_snapshot.c - Create a texture containing a snapshot of a part of the framebuffer +void GRRLIB_Screen2Texture (int posx, int posy, GRRLIB_texImg *tex, bool clear); +void GRRLIB_CompoStart (void); +void GRRLIB_CompoEnd(int posx, int posy, GRRLIB_texImg *tex); + +//------------------------------------------------------------------------------ +// GRRLIB_texEdit.c - Modifying the content of a texture +GRRLIB_texImg* GRRLIB_LoadTexture (const u8 *my_img); +GRRLIB_texImg* GRRLIB_LoadTexturePNG (const u8 *my_png); +GRRLIB_texImg* GRRLIB_LoadTextureJPG (const u8 *my_jpg); +GRRLIB_texImg* GRRLIB_LoadTextureJPGEx (const u8 *my_jpg, const int); +GRRLIB_texImg* GRRLIB_LoadTextureBMP (const u8 *my_bmp); + +//------------------------------------------------------------------------------ +// GRRLIB_gecko.c - USB_Gecko output facilities +bool GRRLIB_GeckoInit(); +void GRRLIB_GeckoPrintf (const char *text, ...); + +//------------------------------------------------------------------------------ +// GRRLIB_3D.c - 3D functions for GRRLIB +void GRRLIB_SetBackgroundColour(u8 r, u8 g, u8 b, u8 a); +void GRRLIB_Camera3dSettings(f32 posx, f32 posy, f32 posz, f32 upx, f32 upy, f32 upz, f32 lookx, f32 looky, f32 lookz); +void GRRLIB_3dMode(f32 minDist, f32 maxDist, f32 fov, bool texturemode, bool normalmode); +void GRRLIB_2dMode(); +void GRRLIB_ObjectViewBegin(void); +void GRRLIB_ObjectViewScale(f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_ObjectViewRotate(f32 angx, f32 angy, f32 angz); +void GRRLIB_ObjectViewTrans(f32 posx, f32 posy, f32 posz); +void GRRLIB_ObjectViewEnd(void); +void GRRLIB_ObjectView(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_ObjectViewInv(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_SetTexture(GRRLIB_texImg *tex, bool rep); +void GRRLIB_DrawTorus(f32 r, f32 R, int nsides, int rings, bool filled, u32 col); +void GRRLIB_DrawSphere(f32 r, int lats, int longs, bool filled, u32 col); +void GRRLIB_DrawCube(f32 size, bool filled, u32 col); +void GRRLIB_DrawCylinder(f32 r, f32 h, int d, bool filled, u32 col); +void GRRLIB_DrawCone(f32 r, f32 h, int d, bool filled, u32 col); +void GRRLIB_DrawTessPanel(f32 w, f32 wstep, f32 h, f32 hstep, bool filled, u32 col); +void GRRLIB_SetLightAmbient(u32 ambientcolor); +void GRRLIB_SetLightDiff(u8 num, guVector pos, f32 distattn, f32 brightness, u32 lightcolor); +void GRRLIB_SetLightSpec(u8 num, guVector dir, f32 shy, u32 lightcolor, u32 speccolor); +void GRRLIB_SetLightSpot(u8 num, guVector pos, guVector lookat, f32 angAttn0, f32 angAttn1, f32 angAttn2, f32 distAttn0, f32 distAttn1, f32 distAttn2, u32 lightcolor); +void GRRLIB_SetLightOff(void); + +//------------------------------------------------------------------------------ +// GRRLIB_ttf.c - FreeType function for GRRLIB +GRRLIB_ttfFont* GRRLIB_LoadTTF(const u8* file_base, s32 file_size); +void GRRLIB_FreeTTF(GRRLIB_ttfFont *myFont); +void GRRLIB_PrintfTTF(int x, int y, GRRLIB_ttfFont *myFont, const char *string, unsigned int fontSize, const u32 color); +void GRRLIB_PrintfTTFW(int x, int y, GRRLIB_ttfFont *myFont, const wchar_t *string, unsigned int fontSize, const u32 color); +unsigned int GRRLIB_WidthTTF(GRRLIB_ttfFont *myFont, const char *, unsigned int); +unsigned int GRRLIB_WidthTTFW(GRRLIB_ttfFont *myFont, const wchar_t *, unsigned int); + +#endif // __GRRLIB_FNLIB_H__ +/** @} */ // end of group diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_cExtn.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_cExtn.h new file mode 100644 index 0000000..a374f57 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_cExtn.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_cExtn.h + * Inline functions to offer additional C primitives. + */ + +#include + +/** + * A helper function for the YCbCr -> RGB conversion. + * Clamps the given value into a range of 0 - 255 and thus preventing an overflow. + * @param Value The value to clamp. Using float to increase the precision. This makes a full spectrum (0 - 255) possible. + * @return Returns a clean, clamped unsigned char. + */ +INLINE +u8 GRRLIB_ClampVar8 (f32 Value) { + Value = roundf(Value); + if (Value < 0) Value = 0; + else if (Value > 255) Value = 255; + + return (u8)Value; +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_clipping.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_clipping.h new file mode 100644 index 0000000..3efde49 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_clipping.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_clipping.h + * Inline functions to control clipping. + */ + +/** + * Reset the clipping to normal. + */ +INLINE +void GRRLIB_ClipReset (void) { + GX_SetClipMode( GX_CLIP_ENABLE ); + GX_SetScissor( 0, 0, rmode->fbWidth, rmode->efbHeight ); +} + +/** + * Clip the drawing area to an rectangle. + * @param x The x-coordinate of the rectangle. + * @param y The y-coordinate of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + */ +INLINE +void GRRLIB_ClipDrawing (const int x, const int y, + const int width, const int height) { + GX_SetClipMode( GX_CLIP_ENABLE ); + GX_SetScissor( x, y, width, height ); +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_collision.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_collision.h new file mode 100644 index 0000000..248a24f --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_collision.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_collision.h + * Inline functions for collision detection. + */ + +/** + * Determine whether the specified point lies within the specified rectangle. + * @param hotx Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param hoty Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param hotw The width of the rectangle. + * @param hoth The height of the rectangle. + * @param wpadx Specifies the x-coordinate of the point. + * @param wpady Specifies the y-coordinate of the point. + * @return If the specified point lies within the rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_PtInRect (const int hotx, const int hoty, + const int hotw, const int hoth, + const int wpadx, const int wpady) { + return( ((wpadx>=hotx) && (wpadx<=(hotx+hotw))) && + ((wpady>=hoty) && (wpady<=(hoty+hoth))) ); +} + +/** + * Determine whether a specified rectangle lies within another rectangle. + * @param rect1x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param rect1y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param rect1w Specifies the width of the rectangle. + * @param rect1h Specifies the height of the rectangle. + * @param rect2x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param rect2y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param rect2w Specifies the width of the rectangle. + * @param rect2h Specifies the height of the rectangle. + * @return If the specified rectangle lies within the other rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_RectInRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h) { + return ( (rect1x >= rect2x) && (rect1y >= rect2y) && + (rect1x+rect1w <= rect2x+rect2w) && + (rect1y+rect1h <= rect2y+rect2h) ); +} + +/** + * Determine whether a part of a specified rectangle lies on another rectangle. + * @param rect1x Specifies the x-coordinate of the upper-left corner of the first rectangle. + * @param rect1y Specifies the y-coordinate of the upper-left corner of the first rectangle. + * @param rect1w Specifies the width of the first rectangle. + * @param rect1h Specifies the height of the first rectangle. + * @param rect2x Specifies the x-coordinate of the upper-left corner of the second rectangle. + * @param rect2y Specifies the y-coordinate of the upper-left corner of the second rectangle. + * @param rect2w Specifies the width of the second rectangle. + * @param rect2h Specifies the height of the second rectangle. + * @return If the specified rectangle lies on the other rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_RectOnRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h) { + return GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x, rect2y) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x+rect2w, rect2y) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x+rect2w, rect2y+rect2h) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x, rect2y+rect2h); +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbComplex.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbComplex.h new file mode 100644 index 0000000..298cfe8 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbComplex.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbComplex.h + * Inline functions for complex (N-point) shape drawing. + */ + +/** + * Draw an array of points. + * @param v Array containing the points. + * @param color The color of the points in RGBA format. + * @param n Number of points in the vector array. + */ +INLINE +void GRRLIB_NPlot (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_POINTS); +} + +/** + * Draw a polygon. + * @param v The vector containing the coordinates of the polygon. + * @param color The color of the filled polygon in RGBA format. + * @param n Number of points in the vector. + */ +INLINE +void GRRLIB_NGone (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_LINESTRIP); +} + +/** + * Draw a filled polygon. + * @param v The vector containing the coordinates of the polygon. + * @param color The color of the filled polygon in RGBA format. + * @param n Number of points in the vector. + */ +INLINE +void GRRLIB_NGoneFilled (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_TRIANGLEFAN); +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbGX.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbGX.h new file mode 100644 index 0000000..8554d53 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbGX.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbGX.h + * Inline functions for interfacing directly to the GX Engine. + */ + +/** + * Draws a vector. + * @param v The vector to draw. + * @param color The color of the vector in RGBA format. + * @param n Number of points in the vector. + * @param fmt Type of primitive. + */ +INLINE +void GRRLIB_GXEngine (const guVector v[], const u32 color[], const long n, + const u8 fmt) { + int i; + + GX_Begin(fmt, GX_VTXFMT0, n); + for (i = 0; i < n; i++) { + GX_Position3f32(v[i].x, v[i].y, v[i].z); + GX_Color1u32(color[i]); + } + GX_End(); +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbSimple.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbSimple.h new file mode 100644 index 0000000..8d62073 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_fbSimple.h @@ -0,0 +1,114 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbSimple.h + * Inline functions for primitive point and line drawing. + */ + +/** + * Clear screen with a specific color. + * @param color The color to use to fill the screen. + */ +INLINE +void GRRLIB_FillScreen (const u32 color) { + GRRLIB_Rectangle(-40, -40, rmode->fbWidth +80, rmode->xfbHeight +80, color, 1); +} + +/** + * Draw a dot. + * @param x Specifies the x-coordinate of the dot. + * @param y Specifies the y-coordinate of the dot. + * @param color The color of the dot in RGBA format. + * @author Jespa + */ +INLINE +void GRRLIB_Plot (const f32 x, const f32 y, const u32 color) { + GX_Begin(GX_POINTS, GX_VTXFMT0, 1); + GX_Position3f32(x, y, 0); + GX_Color1u32(color); + GX_End(); +} + +/** + * Draw a line. + * @param x1 Starting point for line for the x coordinate. + * @param y1 Starting point for line for the y coordinate. + * @param x2 Ending point for line for the x coordinate. + * @param y2 Ending point for line for the x coordinate. + * @param color Line color in RGBA format. + * @author JESPA + */ +INLINE +void GRRLIB_Line (const f32 x1, const f32 y1, + const f32 x2, const f32 y2, const u32 color) { + GX_Begin(GX_LINES, GX_VTXFMT0, 2); + GX_Position3f32(x1, y1, 0); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0); + GX_Color1u32(color); + GX_End(); +} + +/** + * Draw a rectangle. + * @param x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param color The color of the rectangle in RGBA format. + * @param filled Set to true to fill the rectangle. + */ +INLINE +void GRRLIB_Rectangle (const f32 x, const f32 y, + const f32 width, const f32 height, + const u32 color, const u8 filled) { + f32 x2 = x + width; + f32 y2 = y + height; + + if (filled) { + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y2, 0.0f); + GX_Color1u32(color); + GX_End(); + } + else { + GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 5); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_End(); + } +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_handle.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_handle.h new file mode 100644 index 0000000..52577eb --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_handle.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_handle.h + * Inline functions for manipulating texture handles. + */ + +/** + * Set a texture's X and Y handles. + * For example, it could be used for the rotation of a texture. + * @param tex The texture to set the handle on. + * @param x The x-coordinate of the handle. + * @param y The y-coordinate of the handle. + */ +INLINE +void GRRLIB_SetHandle (GRRLIB_texImg *tex, const int x, const int y) { + if (tex->tiledtex) { + tex->handlex = -(((int)tex->tilew)/2) + x; + tex->handley = -(((int)tex->tileh)/2) + y; + } else { + tex->handlex = -(((int)tex->w)/2) + x; + tex->handley = -(((int)tex->h)/2) + y; + } +} + +/** + * Center a texture's handles. + * For example, it could be used for the rotation of a texture. + * @param tex The texture to center. + * @param enabled + */ +INLINE +void GRRLIB_SetMidHandle (GRRLIB_texImg *tex, const bool enabled) { + if (enabled) { + if (tex->tiledtex) { + tex->offsetx = (((int)tex->tilew)/2); + tex->offsety = (((int)tex->tileh)/2); + } else { + tex->offsetx = (((int)tex->w)/2); + tex->offsety = (((int)tex->h)/2); + } + GRRLIB_SetHandle(tex, tex->offsetx, tex->offsety); + } else { + GRRLIB_SetHandle(tex, 0, 0); + tex->offsetx = 0; + tex->offsety = 0; + } +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_pixel.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_pixel.h new file mode 100644 index 0000000..b42acdf --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_pixel.h @@ -0,0 +1,103 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_pixel.h + * Inline functions for manipulating pixels in textures. + */ + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +/** + * Return the color value of a pixel from a GRRLIB_texImg. + * @param x Specifies the x-coordinate of the pixel in the texture. + * @param y Specifies the y-coordinate of the pixel in the texture. + * @param tex The texture to get the color from. + * @return The color of a pixel in RGBA format. + */ +INLINE +u32 GRRLIB_GetPixelFromtexImg (const int x, const int y, + const GRRLIB_texImg *tex) { + register u32 offs; + register u32 ar; + register u8* bp = (u8*)tex->data; + + offs = (((y&(~3))<<2)*tex->w) + ((x&(~3))<<4) + ((((y&3)<<2) + (x&3)) <<1); + + ar = (u32)(*((u16*)(bp+offs ))); + return (ar<<24) | ( ((u32)(*((u16*)(bp+offs+32)))) <<8) | (ar>>8); // Wii is big-endian +} + +/** + * Set the color value of a pixel to a GRRLIB_texImg. + * @see GRRLIB_FlushTex + * @param x Specifies the x-coordinate of the pixel in the texture. + * @param y Specifies the y-coordinate of the pixel in the texture. + * @param tex The texture to set the color to. + * @param color The color of the pixel in RGBA format. + */ +INLINE +void GRRLIB_SetPixelTotexImg (const int x, const int y, + GRRLIB_texImg *tex, const u32 color) { + register u32 offs; + register u8* bp = (u8*)tex->data; + + offs = (((y&(~3))<<2)*tex->w) + ((x&(~3))<<4) + ((((y&3)<<2) + (x&3)) <<1); + + *((u16*)(bp+offs )) = (u16)((color <<8) | (color >>24)); + *((u16*)(bp+offs+32)) = (u16) (color >>8); +} + +/** + * Reads a pixel directly from the FrontBuffer. + * @param x The x-coordinate within the FB. + * @param y The y-coordinate within the FB. + * @return The color of a pixel in RGBA format. + */ +INLINE +u32 GRRLIB_GetPixelFromFB (int x, int y) { + u32 regval,val; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + val = *(u32*)regval; + + return RGBA(_SHIFTR(val,16,8), _SHIFTR(val,8,8), val&0xff, _SHIFTR(val,24,8)); +} + +/** + * Writes a pixel directly from the FrontBuffer. + * @param x The x-coordinate within the FB. + * @param y The y-coordinate within the FB. + * @param pokeColor The color of the pixel in RGBA format. + */ +INLINE +void GRRLIB_SetPixelToFB (int x, int y, u32 pokeColor) { + u32 regval; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + *(u32*)regval = _SHIFTL(A(pokeColor),24,8) | _SHIFTL(R(pokeColor),16,8) | _SHIFTL(G(pokeColor),8,8) | (B(pokeColor)&0xff); +} diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_private.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_private.h new file mode 100644 index 0000000..e3274df --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_private.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_private.h + * The symbols declared in this file are PRIVATE. + * They are not part of the GRRLIB public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + */ + +#ifndef __GRRLIB_PRIVATE_H__ +#define __GRRLIB_PRIVATE_H__ + +#include + +/** + * Used for version checking. + * @param a Major version number. + * @param b Minor version number. + * @param c Revision version number. + */ +#define GRRLIB_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) + +//------------------------------------------------------------------------------ +// GRRLIB_ttf.c - FreeType function for GRRLIB +int GRRLIB_InitTTF(); +void GRRLIB_ExitTTF(); + +#endif // __GRRLIB_PRIVATE_H__ diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_settings.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_settings.h new file mode 100644 index 0000000..2384ded --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_settings.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_settings.h + * Inline functions for configuring the GRRLIB settings. + */ + +#ifndef GX_BM_SUBTRACT + /** + * Blending type. + * libogc revision 4170 fixed a typo. GX_BM_SUBSTRACT was renamed GX_BM_SUBTRACT. + * But for previous versions this define is needed. + */ + #define GX_BM_SUBTRACT GX_BM_SUBSTRACT +#endif + +extern GRRLIB_drawSettings GRRLIB_Settings; + +/** + * Set a blending mode. + * @param blendmode The blending mode to use (Default: GRRLIB_BLEND_ALPHA). + */ +INLINE +void GRRLIB_SetBlend (const GRRLIB_blendMode blendmode) { + GRRLIB_Settings.blend = blendmode; + switch (GRRLIB_Settings.blend) { + case GRRLIB_BLEND_ALPHA: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_ADD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_DSTALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_SCREEN: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCCLR, GX_BL_DSTALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_MULTI: + GX_SetBlendMode(GX_BM_SUBTRACT, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_INV: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_INVSRCCLR, GX_BL_INVSRCCLR, GX_LO_CLEAR); + break; + } +} + +/** + * Get the current blending mode. + * @return The current blending mode. + */ +INLINE +GRRLIB_blendMode GRRLIB_GetBlend (void) { + return GRRLIB_Settings.blend; +} + +/** + * Turn anti-aliasing on/off. + * @param aa Set to true to enable anti-aliasing (Default: Enabled). + */ +INLINE +void GRRLIB_SetAntiAliasing (const bool aa) { + GRRLIB_Settings.antialias = aa; +} + +/** + * Get current anti-aliasing setting. + * @return True if anti-aliasing is enabled. + */ +INLINE +bool GRRLIB_GetAntiAliasing (void) { + return GRRLIB_Settings.antialias; +} + diff --git a/lib/grrlib/GRRLIB/grrlib/GRRLIB_texSetup.h b/lib/grrlib/GRRLIB/grrlib/GRRLIB_texSetup.h new file mode 100644 index 0000000..adb9b26 --- /dev/null +++ b/lib/grrlib/GRRLIB/grrlib/GRRLIB_texSetup.h @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_texSetup.h + * Inline functions for the basic manipulation of textures. + */ + +#if 0 + +#include +#include +#include + +/** + * Create an empty texture. + * @param w Width of the new texture to create. + * @param h Height of the new texture to create. + * @return A GRRLIB_texImg structure newly created. + */ +INLINE +GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h) +{ + GRRLIB_texImg *my_texture = (struct GRRLIB_texImg *)calloc(1, sizeof(GRRLIB_texImg)); + + if(my_texture != NULL) { + my_texture->data = memalign(32, h * w * 4); + my_texture->w = w; + my_texture->h = h; + + // Initialize the texture + memset(my_texture->data, '\0', (h * w) << 2); + + GRRLIB_SetHandle(my_texture, 0, 0); + GRRLIB_FlushTex(my_texture); + } + return my_texture; +} + +/** + * Write the contents of a texture in the data cache down to main memory. + * For performance the CPU holds a data cache where modifications are stored before they get written down to main memory. + * @param tex The texture to flush. + */ +INLINE +void GRRLIB_FlushTex (GRRLIB_texImg *tex) { + DCFlushRange(tex->data, tex->w * tex->h * 4); +} + +/** + * Free memory allocated for texture. + * @param tex A GRRLIB_texImg structure. + */ +INLINE +void GRRLIB_FreeTexture (GRRLIB_texImg *tex) { + if(tex != NULL) { + if (tex->data != NULL) free(tex->data); + free(tex); + tex = NULL; + } +} + +/** + * Clear a texture to transparent black. + * @param tex Texture to clear. + */ +INLINE +void GRRLIB_ClearTex(GRRLIB_texImg* tex) { + bzero(tex->data, (tex->h * tex->w) << 2); + GRRLIB_FlushTex(tex); +} +#endif + diff --git a/lib/grrlib/include/grrlib.h b/lib/grrlib/include/grrlib.h new file mode 100644 index 0000000..f5bce85 --- /dev/null +++ b/lib/grrlib/include/grrlib.h @@ -0,0 +1,264 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB.h + * GRRLIB user include file. + */ +/** + * @defgroup AllFunc Everything in GRRLIB + * This is the complete list of funtions, structures, defines, typedefs, enumerations and variables you may want to used to make your homebrew with GRRLIB. + * You simply need to include grrlib.h in your project to have access to all of these. + * @{ + */ + +#ifndef __GRRLIB_H__ +#define __GRRLIB_H__ + +/** + * Version information for GRRLIB. + */ +#define GRRLIB_VER_STRING "4.3.1" + +//============================================================================== +// Includes +//============================================================================== +#include +#include + +#if (_V_MAJOR_*10 + _V_MINOR_) < 18 +#define guVector Vector +#endif +//============================================================================== + +//============================================================================== +// C++ header +//============================================================================== +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +//============================================================================== +// Extra standard declarations +//============================================================================== +typedef unsigned int uint;/**< The uint keyword signifies an integral type. */ + +//============================================================================== +// Primitive colour macros +//============================================================================== +#define R(c) (((c) >>24) &0xFF) /**< Exract Red component of colour. */ +#define G(c) (((c) >>16) &0xFF) /**< Exract Green component of colour. */ +#define B(c) (((c) >> 8) &0xFF) /**< Exract Blue component of colour. */ +#define A(c) ( (c) &0xFF) /**< Exract Alpha component of colour. */ + +/** + * Build an RGB pixel from components. + * @param r Red component. + * @param g Green component. + * @param b Blue component. + * @param a Alpha component. + */ +#define RGBA(r,g,b,a) ( (u32)( ( ((u32)(r)) <<24) | \ + ((((u32)(g)) &0xFF) <<16) | \ + ((((u32)(b)) &0xFF) << 8) | \ + ( ((u32)(a)) &0xFF ) ) ) + +//============================================================================== +// typedefs, enumerators & structs +//============================================================================== +/** + * GRRLIB Blending Modes. + */ +typedef enum GRRLIB_blendMode { + GRRLIB_BLEND_ALPHA = 0, /**< Alpha Blending. */ + GRRLIB_BLEND_ADD = 1, /**< Additive Blending. */ + GRRLIB_BLEND_SCREEN = 2, /**< Alpha Light Blending. */ + GRRLIB_BLEND_MULTI = 3, /**< Multiply Blending. */ + GRRLIB_BLEND_INV = 4, /**< Invert Color Blending. */ +} GRRLIB_blendMode; + +#define GRRLIB_BLEND_NONE (GRRLIB_BLEND_ALPHA) /**< Alias for GRRLIB_BLEND_ALPHA. */ +#define GRRLIB_BLEND_LIGHT (GRRLIB_BLEND_ADD) /**< Alias for GRRLIB_BLEND_ADD. */ +#define GRRLIB_BLEND_SHADE (GRRLIB_BLEND_MULTI) /**< Alias for GRRLIB_BLEND_MULTI. */ + +//------------------------------------------------------------------------------ +/** + * Structure to hold the current drawing settings. + */ +typedef struct GRRLIB_drawSettings { + bool antialias; /**< AntiAlias is enabled when set to true. */ + GRRLIB_blendMode blend; /**< Blending Mode. */ + int lights; /**< Active lights. */ +} GRRLIB_drawSettings; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the texture information. + */ +typedef struct GRRLIB_texImg { + int tex_format; + int tex_lod; + uint w; /**< The width of the texture in pixels. */ + uint h; /**< The height of the texture in pixels. */ + int handlex; /**< Texture handle x. */ + int handley; /**< Texture handle y. */ + int offsetx; /**< Texture offset x. */ + int offsety; /**< Texture offset y. */ + + bool tiledtex; /**< Texture is tiled if set to true. */ + uint tilew; /**< The width of one tile in pixels. */ + uint tileh; /**< The height of one tile in pixels. */ + uint nbtilew; /**< Number of tiles for the x axis. */ + uint nbtileh; /**< Number of tiles for the y axis. */ + uint tilestart; /**< Offset to tile starting position. */ + f32 ofnormaltexx;/**< Offset of normalized texture on x. */ + f32 ofnormaltexy;/**< Offset of normalized texture on y. */ + + void *data; /**< Pointer to the texture data. */ +} GRRLIB_texImg; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the bytemap character information. + */ +typedef struct GRRLIB_bytemapChar { + u8 width; /**< Character width. */ + u8 height; /**< Character height. */ + s8 relx; /**< Horizontal offset relative to cursor (-128 to 127). */ + s8 rely; /**< Vertical offset relative to cursor (-128 to 127). */ + u8 kerning; /**< Kerning (Horizontal cursor shift after drawing the character). */ + u8 *data; /**< Character data (uncompressed, 8 bits per pixel). */ +} GRRLIB_bytemapChar; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the bytemap font information. + */ +typedef struct GRRLIB_bytemapFont { + char *name; /**< Font name. */ + u32 *palette; /**< Font palette. */ + u16 nbChar; /**< Number of characters in font. */ + u8 version; /**< Version. */ + s8 tracking; /**< Tracking (Add-space after each char) (-128 to 127). */ + + GRRLIB_bytemapChar charDef[256]; /**< Array of bitmap characters. */ +} GRRLIB_bytemapFont; + +//------------------------------------------------------------------------------ +/** + * Structure to hold the TTF information. + */ +typedef struct GRRLIB_Font { + void *face; /**< A TTF face object. */ + bool kerning; /**< true whenever a face object contains kerning data that can be accessed with FT_Get_Kerning. */ +} GRRLIB_ttfFont; + +//============================================================================== +// Allow general access to screen and frame information +//============================================================================== +#if defined __GRRLIB_CORE__ +# define GRR_EXTERN +# define GRR_INIT(v) = v +# define GRR_INITS(...) = { __VA_ARGS__ } +#else +# define GRR_EXTERN extern +# define GRR_INIT(v) +# define GRR_INITS(...) +#endif + +GRR_EXTERN GXRModeObj *rmode; +GRR_EXTERN void *xfb[2] GRR_INITS(NULL, NULL); +GRR_EXTERN u32 fb GRR_INIT(0); +//============================================================================== +// procedure and function prototypes +// Inline function handling - http://www.greenend.org.uk/rjk/2003/03/inline.html +//============================================================================== +#include "grrlib/GRRLIB__lib.h" + +#if defined __GRRLIB_CORE__ +# define INLINE +#else +# if __GNUC__ && !__GNUC_STDC_INLINE__ +# define INLINE static inline +# else +# define INLINE inline +# endif +#endif +#include "grrlib/GRRLIB__inline.h" + +//============================================================================== +// C++ footer +//============================================================================== +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif // __GRRLIB_H__ +/** @} */ // end of group +/** + * @mainpage GRRLIB Documentation + * @image html grrlib_logo.png + * Welcome to the GRRLIB documentation. + * A complete list of functions is available from this \ref AllFunc "page". + * + * @section Introduction + * GRRLIB is a C/C++ 2D/3D graphics library for Wii application developers. + * It is essentially a wrapper which presents a friendly interface to the Nintendo GX core. + * + * @section Links + * Forum: http://grrlib.santo.fr/forum\n + * Code: http://code.google.com/p/grrlib\n + * IRC: ##GRRLIB on EFnet + * + * @section Credits + * Project Leader : NoNameNo\n + * Documentation : Crayon, BlueChip\n + * Lead Coder : NoNameNo\n + * Support Coders : Crayon, Xane, DragonMinded, BlueChip\n + * Advisors : RedShade, Jespa\n + * + * @section Licence + * Copyright (c) 2010 The GRRLIB Team + * + * 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. + * + * @example template/source/main.c + * This example shows the minimum code required to use GRRLIB. + * It could be used as a template to start a new project. + * More elaborate examples can be found inside the \e examples folder. + */ diff --git a/lib/grrlib/include/grrlib/GRRLIB__inline.h b/lib/grrlib/include/grrlib/GRRLIB__inline.h new file mode 100644 index 0000000..773720f --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB__inline.h @@ -0,0 +1,145 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB__inline.h + * GRRLIB inline function prototypes. + * Do not include GRRLIB__inline.h directly, include only GRRLIB.h. + */ +/** + * @addtogroup AllFunc + * @{ + */ + +#ifndef __GRRLIB_H__ +# error Do not include GRRLIB__inline.h directly, include only GRRLIB.h +#endif + +#ifndef __GRRLIB_FNINLINE_H__ +#define __GRRLIB_FNINLINE_H__ + +//============================================================================== +// Prototypes for inlined functions +//============================================================================== + +//------------------------------------------------------------------------------ +// GRRLIB_cExtn.h - C extensions (helper functions) +INLINE u8 GRRLIB_ClampVar8 (f32 Value); + +//------------------------------------------------------------------------------ +// GRRLIB_clipping.h - Clipping control +INLINE void GRRLIB_ClipReset (void); +INLINE void GRRLIB_ClipDrawing (const int x, const int y, + const int width, const int height); + +//------------------------------------------------------------------------------ +// GRRLIB_collision.h - Collision detection +INLINE bool GRRLIB_PtInRect (const int hotx, const int hoty, + const int hotw, const int hoth, + const int wpadx, const int wpady); + +INLINE bool GRRLIB_RectInRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h); + +INLINE bool GRRLIB_RectOnRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h); + +//------------------------------------------------------------------------------ +// GRRLIB_fbComplex.h - +INLINE void GRRLIB_NPlot (const guVector v[], const u32 color[], + const long n); +INLINE void GRRLIB_NGone (const guVector v[], const u32 color[], + const long n); +INLINE void GRRLIB_NGoneFilled (const guVector v[], const u32 color[], + const long n); + +//------------------------------------------------------------------------------ +// GRRLIB_fbGX.h - +INLINE void GRRLIB_GXEngine (const guVector v[], const u32 color[], + const long n, const u8 fmt); + +//------------------------------------------------------------------------------ +// GRRLIB_fbSimple.h - +INLINE void GRRLIB_FillScreen (const u32 color); +INLINE void GRRLIB_Plot (const f32 x, const f32 y, const u32 color); +INLINE void GRRLIB_Line (const f32 x1, const f32 y1, + const f32 x2, const f32 y2, const u32 color); +INLINE void GRRLIB_Rectangle (const f32 x, const f32 y, + const f32 width, const f32 height, + const u32 color, const u8 filled); + +//------------------------------------------------------------------------------ +// GRRLIB_handle.h - Texture handle manipulation +INLINE void GRRLIB_SetHandle (GRRLIB_texImg *tex, const int x, const int y); +INLINE void GRRLIB_SetMidHandle (GRRLIB_texImg *tex, const bool enabled); + +//------------------------------------------------------------------------------ +// GRRLIB_pixel.h - Pixel manipulation +INLINE u32 GRRLIB_GetPixelFromtexImg (const int x, const int y, + const GRRLIB_texImg *tex); + +INLINE void GRRLIB_SetPixelTotexImg (const int x, const int y, + GRRLIB_texImg *tex, const u32 color); + +INLINE u32 GRRLIB_GetPixelFromFB (int x, int y); +INLINE void GRRLIB_SetPixelToFB (int x, int y, u32 pokeColor); + +//------------------------------------------------------------------------------ +// GRRLIB_settings.h - Rendering functions +INLINE void GRRLIB_SetBlend (const GRRLIB_blendMode blendmode); +INLINE GRRLIB_blendMode GRRLIB_GetBlend (void); +INLINE void GRRLIB_SetAntiAliasing (const bool aa); +INLINE bool GRRLIB_GetAntiAliasing (void); + +//------------------------------------------------------------------------------ +// GRRLIB_texSetup.h - Create and setup textures +#if 0 +INLINE GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h); +INLINE void GRRLIB_ClearTex (GRRLIB_texImg* tex); +INLINE void GRRLIB_FlushTex (GRRLIB_texImg *tex); +INLINE void GRRLIB_FreeTexture (GRRLIB_texImg *tex); +#endif +GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h); +void GRRLIB_ClearTex (GRRLIB_texImg* tex); +void GRRLIB_FlushTex (GRRLIB_texImg *tex); +void GRRLIB_FreeTexture (GRRLIB_texImg *tex); + +//============================================================================== +// Definitions of inlined functions +//============================================================================== +#include // C extensions (helper functions) +#include // Clipping control +#include // Collision detection +#include // Render to framebuffer: Complex primitives +#include // Render to framebuffer: Simple GX wrapper +#include // Render to framebuffer: Simple primitives +#include // Texture handle manipulation +#include // Pixel manipulation +#include // GRRLIB Settings +#include // Setup for textures + +#endif // __GRRLIB_FNINLINE_H__ +/** @} */ // end of group diff --git a/lib/grrlib/include/grrlib/GRRLIB__lib.h b/lib/grrlib/include/grrlib/GRRLIB__lib.h new file mode 100644 index 0000000..fafc194 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB__lib.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/** + * @file GRRLIB__lib.h + * GRRLIB library function prototypes. + * Do not include GRRLIB__lib.h directly, include only GRRLIB.h. + */ +/** + * @addtogroup AllFunc + * @{ + */ + +#ifndef __GRRLIB_H__ +# error Do not include GRRLIB__lib.h directly, include only GRRLIB.h +#endif + +#ifndef __GRRLIB_FNLIB_H__ +#define __GRRLIB_FNLIB_H__ + +//============================================================================== +// Prototypes for library contained functions +//============================================================================== + +//------------------------------------------------------------------------------ +// GRRLIB_bmf.c - BitMapFont functions +GRRLIB_bytemapFont* GRRLIB_LoadBMF (const u8 my_bmf[] ); +void GRRLIB_FreeBMF (GRRLIB_bytemapFont *bmf); + +void GRRLIB_InitTileSet (GRRLIB_texImg *tex, + const uint tilew, const uint tileh, + const uint tilestart); + +//------------------------------------------------------------------------------ +// GRRLIB_bmfx.c - Bitmap f/x +void GRRLIB_BMFX_FlipH (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_FlipV (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Grayscale (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Sepia (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Invert (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest); + +void GRRLIB_BMFX_Blur (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +void GRRLIB_BMFX_Scatter (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +void GRRLIB_BMFX_Pixelate (const GRRLIB_texImg *texsrc, + GRRLIB_texImg *texdest, const u32 factor); + +//------------------------------------------------------------------------------ +// GRRLIB_core.c - GRRLIB core functions +int GRRLIB_Init (void); +void GRRLIB_Exit (void); + +//------------------------------------------------------------------------------ +// GRRLIB_fbAdvanced.c - Render to framebuffer: Advanced primitives +void GRRLIB_Circle (const f32 x, const f32 y, const f32 radius, + const u32 color, const u8 filled); + +//------------------------------------------------------------------------------ +// GRRLIB_fileIO - File I/O (SD Card) +int GRRLIB_LoadFile (const char* filename, + unsigned char* *data); +GRRLIB_texImg* GRRLIB_LoadTextureFromFile (const char* filename); +bool GRRLIB_ScrShot (const char* filename); + +//------------------------------------------------------------------------------ +// GRRLIB_print.c - Will someone please tell me what these are :) +void GRRLIB_Printf (const f32 xpos, const f32 ypos, + const GRRLIB_texImg *tex, const u32 color, + const f32 zoom, const char *text, ...); + +void GRRLIB_PrintBMF (const f32 xpos, const f32 ypos, + const GRRLIB_bytemapFont *bmf, + const char *text, ...); + +//------------------------------------------------------------------------------ +// GRRLIB_render.c - Rendering functions +void GRRLIB_DrawImg (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color); + +void GRRLIB_DrawImgQuad (const guVector pos[4], GRRLIB_texImg *tex, + const u32 color); + +void GRRLIB_DrawTile (const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color, const int frame); + +void GRRLIB_DrawPart (const f32 xpos, const f32 ypos, const f32 partx, const f32 party, + const f32 partw, const f32 parth, const GRRLIB_texImg *tex, + const f32 degrees, const f32 scaleX, const f32 scaleY, + const u32 color); + +void GRRLIB_DrawTileQuad (const guVector pos[4], GRRLIB_texImg *tex, const u32 color, const int frame); + +void GRRLIB_Render (void); + +//------------------------------------------------------------------------------ +// GRRLIB_snapshot.c - Create a texture containing a snapshot of a part of the framebuffer +void GRRLIB_Screen2Texture (int posx, int posy, GRRLIB_texImg *tex, bool clear); +void GRRLIB_CompoStart (void); +void GRRLIB_CompoEnd(int posx, int posy, GRRLIB_texImg *tex); + +//------------------------------------------------------------------------------ +// GRRLIB_texEdit.c - Modifying the content of a texture +GRRLIB_texImg* GRRLIB_LoadTexture (const u8 *my_img); +GRRLIB_texImg* GRRLIB_LoadTexturePNG (const u8 *my_png); +GRRLIB_texImg* GRRLIB_LoadTextureJPG (const u8 *my_jpg); +GRRLIB_texImg* GRRLIB_LoadTextureJPGEx (const u8 *my_jpg, const int); +GRRLIB_texImg* GRRLIB_LoadTextureBMP (const u8 *my_bmp); + +//------------------------------------------------------------------------------ +// GRRLIB_gecko.c - USB_Gecko output facilities +bool GRRLIB_GeckoInit(); +void GRRLIB_GeckoPrintf (const char *text, ...); + +//------------------------------------------------------------------------------ +// GRRLIB_3D.c - 3D functions for GRRLIB +void GRRLIB_SetBackgroundColour(u8 r, u8 g, u8 b, u8 a); +void GRRLIB_Camera3dSettings(f32 posx, f32 posy, f32 posz, f32 upx, f32 upy, f32 upz, f32 lookx, f32 looky, f32 lookz); +void GRRLIB_3dMode(f32 minDist, f32 maxDist, f32 fov, bool texturemode, bool normalmode); +void GRRLIB_2dMode(); +void GRRLIB_ObjectViewBegin(void); +void GRRLIB_ObjectViewScale(f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_ObjectViewRotate(f32 angx, f32 angy, f32 angz); +void GRRLIB_ObjectViewTrans(f32 posx, f32 posy, f32 posz); +void GRRLIB_ObjectViewEnd(void); +void GRRLIB_ObjectView(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_ObjectViewInv(f32 posx, f32 posy, f32 posz, f32 angx, f32 angy, f32 angz, f32 scalx, f32 scaly, f32 scalz); +void GRRLIB_SetTexture(GRRLIB_texImg *tex, bool rep); +void GRRLIB_DrawTorus(f32 r, f32 R, int nsides, int rings, bool filled, u32 col); +void GRRLIB_DrawSphere(f32 r, int lats, int longs, bool filled, u32 col); +void GRRLIB_DrawCube(f32 size, bool filled, u32 col); +void GRRLIB_DrawCylinder(f32 r, f32 h, int d, bool filled, u32 col); +void GRRLIB_DrawCone(f32 r, f32 h, int d, bool filled, u32 col); +void GRRLIB_DrawTessPanel(f32 w, f32 wstep, f32 h, f32 hstep, bool filled, u32 col); +void GRRLIB_SetLightAmbient(u32 ambientcolor); +void GRRLIB_SetLightDiff(u8 num, guVector pos, f32 distattn, f32 brightness, u32 lightcolor); +void GRRLIB_SetLightSpec(u8 num, guVector dir, f32 shy, u32 lightcolor, u32 speccolor); +void GRRLIB_SetLightSpot(u8 num, guVector pos, guVector lookat, f32 angAttn0, f32 angAttn1, f32 angAttn2, f32 distAttn0, f32 distAttn1, f32 distAttn2, u32 lightcolor); +void GRRLIB_SetLightOff(void); + +//------------------------------------------------------------------------------ +// GRRLIB_ttf.c - FreeType function for GRRLIB +GRRLIB_ttfFont* GRRLIB_LoadTTF(const u8* file_base, s32 file_size); +void GRRLIB_FreeTTF(GRRLIB_ttfFont *myFont); +void GRRLIB_PrintfTTF(int x, int y, GRRLIB_ttfFont *myFont, const char *string, unsigned int fontSize, const u32 color); +void GRRLIB_PrintfTTFW(int x, int y, GRRLIB_ttfFont *myFont, const wchar_t *string, unsigned int fontSize, const u32 color); +unsigned int GRRLIB_WidthTTF(GRRLIB_ttfFont *myFont, const char *, unsigned int); +unsigned int GRRLIB_WidthTTFW(GRRLIB_ttfFont *myFont, const wchar_t *, unsigned int); + +#endif // __GRRLIB_FNLIB_H__ +/** @} */ // end of group diff --git a/lib/grrlib/include/grrlib/GRRLIB_cExtn.h b/lib/grrlib/include/grrlib/GRRLIB_cExtn.h new file mode 100644 index 0000000..a374f57 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_cExtn.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_cExtn.h + * Inline functions to offer additional C primitives. + */ + +#include + +/** + * A helper function for the YCbCr -> RGB conversion. + * Clamps the given value into a range of 0 - 255 and thus preventing an overflow. + * @param Value The value to clamp. Using float to increase the precision. This makes a full spectrum (0 - 255) possible. + * @return Returns a clean, clamped unsigned char. + */ +INLINE +u8 GRRLIB_ClampVar8 (f32 Value) { + Value = roundf(Value); + if (Value < 0) Value = 0; + else if (Value > 255) Value = 255; + + return (u8)Value; +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_clipping.h b/lib/grrlib/include/grrlib/GRRLIB_clipping.h new file mode 100644 index 0000000..3efde49 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_clipping.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_clipping.h + * Inline functions to control clipping. + */ + +/** + * Reset the clipping to normal. + */ +INLINE +void GRRLIB_ClipReset (void) { + GX_SetClipMode( GX_CLIP_ENABLE ); + GX_SetScissor( 0, 0, rmode->fbWidth, rmode->efbHeight ); +} + +/** + * Clip the drawing area to an rectangle. + * @param x The x-coordinate of the rectangle. + * @param y The y-coordinate of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + */ +INLINE +void GRRLIB_ClipDrawing (const int x, const int y, + const int width, const int height) { + GX_SetClipMode( GX_CLIP_ENABLE ); + GX_SetScissor( x, y, width, height ); +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_collision.h b/lib/grrlib/include/grrlib/GRRLIB_collision.h new file mode 100644 index 0000000..248a24f --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_collision.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_collision.h + * Inline functions for collision detection. + */ + +/** + * Determine whether the specified point lies within the specified rectangle. + * @param hotx Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param hoty Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param hotw The width of the rectangle. + * @param hoth The height of the rectangle. + * @param wpadx Specifies the x-coordinate of the point. + * @param wpady Specifies the y-coordinate of the point. + * @return If the specified point lies within the rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_PtInRect (const int hotx, const int hoty, + const int hotw, const int hoth, + const int wpadx, const int wpady) { + return( ((wpadx>=hotx) && (wpadx<=(hotx+hotw))) && + ((wpady>=hoty) && (wpady<=(hoty+hoth))) ); +} + +/** + * Determine whether a specified rectangle lies within another rectangle. + * @param rect1x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param rect1y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param rect1w Specifies the width of the rectangle. + * @param rect1h Specifies the height of the rectangle. + * @param rect2x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param rect2y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param rect2w Specifies the width of the rectangle. + * @param rect2h Specifies the height of the rectangle. + * @return If the specified rectangle lies within the other rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_RectInRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h) { + return ( (rect1x >= rect2x) && (rect1y >= rect2y) && + (rect1x+rect1w <= rect2x+rect2w) && + (rect1y+rect1h <= rect2y+rect2h) ); +} + +/** + * Determine whether a part of a specified rectangle lies on another rectangle. + * @param rect1x Specifies the x-coordinate of the upper-left corner of the first rectangle. + * @param rect1y Specifies the y-coordinate of the upper-left corner of the first rectangle. + * @param rect1w Specifies the width of the first rectangle. + * @param rect1h Specifies the height of the first rectangle. + * @param rect2x Specifies the x-coordinate of the upper-left corner of the second rectangle. + * @param rect2y Specifies the y-coordinate of the upper-left corner of the second rectangle. + * @param rect2w Specifies the width of the second rectangle. + * @param rect2h Specifies the height of the second rectangle. + * @return If the specified rectangle lies on the other rectangle, the return value is true otherwise it's false. + */ +INLINE +bool GRRLIB_RectOnRect (const int rect1x, const int rect1y, + const int rect1w, const int rect1h, + const int rect2x, const int rect2y, + const int rect2w, const int rect2h) { + return GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x, rect2y) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x+rect2w, rect2y) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x+rect2w, rect2y+rect2h) || + GRRLIB_PtInRect(rect1x, rect1y, rect1w, rect1h, rect2x, rect2y+rect2h); +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_fbComplex.h b/lib/grrlib/include/grrlib/GRRLIB_fbComplex.h new file mode 100644 index 0000000..298cfe8 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_fbComplex.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbComplex.h + * Inline functions for complex (N-point) shape drawing. + */ + +/** + * Draw an array of points. + * @param v Array containing the points. + * @param color The color of the points in RGBA format. + * @param n Number of points in the vector array. + */ +INLINE +void GRRLIB_NPlot (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_POINTS); +} + +/** + * Draw a polygon. + * @param v The vector containing the coordinates of the polygon. + * @param color The color of the filled polygon in RGBA format. + * @param n Number of points in the vector. + */ +INLINE +void GRRLIB_NGone (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_LINESTRIP); +} + +/** + * Draw a filled polygon. + * @param v The vector containing the coordinates of the polygon. + * @param color The color of the filled polygon in RGBA format. + * @param n Number of points in the vector. + */ +INLINE +void GRRLIB_NGoneFilled (const guVector v[], const u32 color[], const long n) { + GRRLIB_GXEngine(v, color, n, GX_TRIANGLEFAN); +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_fbGX.h b/lib/grrlib/include/grrlib/GRRLIB_fbGX.h new file mode 100644 index 0000000..8554d53 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_fbGX.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbGX.h + * Inline functions for interfacing directly to the GX Engine. + */ + +/** + * Draws a vector. + * @param v The vector to draw. + * @param color The color of the vector in RGBA format. + * @param n Number of points in the vector. + * @param fmt Type of primitive. + */ +INLINE +void GRRLIB_GXEngine (const guVector v[], const u32 color[], const long n, + const u8 fmt) { + int i; + + GX_Begin(fmt, GX_VTXFMT0, n); + for (i = 0; i < n; i++) { + GX_Position3f32(v[i].x, v[i].y, v[i].z); + GX_Color1u32(color[i]); + } + GX_End(); +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_fbSimple.h b/lib/grrlib/include/grrlib/GRRLIB_fbSimple.h new file mode 100644 index 0000000..8d62073 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_fbSimple.h @@ -0,0 +1,114 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_fbSimple.h + * Inline functions for primitive point and line drawing. + */ + +/** + * Clear screen with a specific color. + * @param color The color to use to fill the screen. + */ +INLINE +void GRRLIB_FillScreen (const u32 color) { + GRRLIB_Rectangle(-40, -40, rmode->fbWidth +80, rmode->xfbHeight +80, color, 1); +} + +/** + * Draw a dot. + * @param x Specifies the x-coordinate of the dot. + * @param y Specifies the y-coordinate of the dot. + * @param color The color of the dot in RGBA format. + * @author Jespa + */ +INLINE +void GRRLIB_Plot (const f32 x, const f32 y, const u32 color) { + GX_Begin(GX_POINTS, GX_VTXFMT0, 1); + GX_Position3f32(x, y, 0); + GX_Color1u32(color); + GX_End(); +} + +/** + * Draw a line. + * @param x1 Starting point for line for the x coordinate. + * @param y1 Starting point for line for the y coordinate. + * @param x2 Ending point for line for the x coordinate. + * @param y2 Ending point for line for the x coordinate. + * @param color Line color in RGBA format. + * @author JESPA + */ +INLINE +void GRRLIB_Line (const f32 x1, const f32 y1, + const f32 x2, const f32 y2, const u32 color) { + GX_Begin(GX_LINES, GX_VTXFMT0, 2); + GX_Position3f32(x1, y1, 0); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0); + GX_Color1u32(color); + GX_End(); +} + +/** + * Draw a rectangle. + * @param x Specifies the x-coordinate of the upper-left corner of the rectangle. + * @param y Specifies the y-coordinate of the upper-left corner of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param color The color of the rectangle in RGBA format. + * @param filled Set to true to fill the rectangle. + */ +INLINE +void GRRLIB_Rectangle (const f32 x, const f32 y, + const f32 width, const f32 height, + const u32 color, const u8 filled) { + f32 x2 = x + width; + f32 y2 = y + height; + + if (filled) { + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y2, 0.0f); + GX_Color1u32(color); + GX_End(); + } + else { + GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 5); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x2, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y2, 0.0f); + GX_Color1u32(color); + GX_Position3f32(x, y, 0.0f); + GX_Color1u32(color); + GX_End(); + } +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_handle.h b/lib/grrlib/include/grrlib/GRRLIB_handle.h new file mode 100644 index 0000000..52577eb --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_handle.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_handle.h + * Inline functions for manipulating texture handles. + */ + +/** + * Set a texture's X and Y handles. + * For example, it could be used for the rotation of a texture. + * @param tex The texture to set the handle on. + * @param x The x-coordinate of the handle. + * @param y The y-coordinate of the handle. + */ +INLINE +void GRRLIB_SetHandle (GRRLIB_texImg *tex, const int x, const int y) { + if (tex->tiledtex) { + tex->handlex = -(((int)tex->tilew)/2) + x; + tex->handley = -(((int)tex->tileh)/2) + y; + } else { + tex->handlex = -(((int)tex->w)/2) + x; + tex->handley = -(((int)tex->h)/2) + y; + } +} + +/** + * Center a texture's handles. + * For example, it could be used for the rotation of a texture. + * @param tex The texture to center. + * @param enabled + */ +INLINE +void GRRLIB_SetMidHandle (GRRLIB_texImg *tex, const bool enabled) { + if (enabled) { + if (tex->tiledtex) { + tex->offsetx = (((int)tex->tilew)/2); + tex->offsety = (((int)tex->tileh)/2); + } else { + tex->offsetx = (((int)tex->w)/2); + tex->offsety = (((int)tex->h)/2); + } + GRRLIB_SetHandle(tex, tex->offsetx, tex->offsety); + } else { + GRRLIB_SetHandle(tex, 0, 0); + tex->offsetx = 0; + tex->offsety = 0; + } +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_pixel.h b/lib/grrlib/include/grrlib/GRRLIB_pixel.h new file mode 100644 index 0000000..b42acdf --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_pixel.h @@ -0,0 +1,103 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_pixel.h + * Inline functions for manipulating pixels in textures. + */ + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +/** + * Return the color value of a pixel from a GRRLIB_texImg. + * @param x Specifies the x-coordinate of the pixel in the texture. + * @param y Specifies the y-coordinate of the pixel in the texture. + * @param tex The texture to get the color from. + * @return The color of a pixel in RGBA format. + */ +INLINE +u32 GRRLIB_GetPixelFromtexImg (const int x, const int y, + const GRRLIB_texImg *tex) { + register u32 offs; + register u32 ar; + register u8* bp = (u8*)tex->data; + + offs = (((y&(~3))<<2)*tex->w) + ((x&(~3))<<4) + ((((y&3)<<2) + (x&3)) <<1); + + ar = (u32)(*((u16*)(bp+offs ))); + return (ar<<24) | ( ((u32)(*((u16*)(bp+offs+32)))) <<8) | (ar>>8); // Wii is big-endian +} + +/** + * Set the color value of a pixel to a GRRLIB_texImg. + * @see GRRLIB_FlushTex + * @param x Specifies the x-coordinate of the pixel in the texture. + * @param y Specifies the y-coordinate of the pixel in the texture. + * @param tex The texture to set the color to. + * @param color The color of the pixel in RGBA format. + */ +INLINE +void GRRLIB_SetPixelTotexImg (const int x, const int y, + GRRLIB_texImg *tex, const u32 color) { + register u32 offs; + register u8* bp = (u8*)tex->data; + + offs = (((y&(~3))<<2)*tex->w) + ((x&(~3))<<4) + ((((y&3)<<2) + (x&3)) <<1); + + *((u16*)(bp+offs )) = (u16)((color <<8) | (color >>24)); + *((u16*)(bp+offs+32)) = (u16) (color >>8); +} + +/** + * Reads a pixel directly from the FrontBuffer. + * @param x The x-coordinate within the FB. + * @param y The y-coordinate within the FB. + * @return The color of a pixel in RGBA format. + */ +INLINE +u32 GRRLIB_GetPixelFromFB (int x, int y) { + u32 regval,val; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + val = *(u32*)regval; + + return RGBA(_SHIFTR(val,16,8), _SHIFTR(val,8,8), val&0xff, _SHIFTR(val,24,8)); +} + +/** + * Writes a pixel directly from the FrontBuffer. + * @param x The x-coordinate within the FB. + * @param y The y-coordinate within the FB. + * @param pokeColor The color of the pixel in RGBA format. + */ +INLINE +void GRRLIB_SetPixelToFB (int x, int y, u32 pokeColor) { + u32 regval; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + *(u32*)regval = _SHIFTL(A(pokeColor),24,8) | _SHIFTL(R(pokeColor),16,8) | _SHIFTL(G(pokeColor),8,8) | (B(pokeColor)&0xff); +} diff --git a/lib/grrlib/include/grrlib/GRRLIB_private.h b/lib/grrlib/include/grrlib/GRRLIB_private.h new file mode 100644 index 0000000..e3274df --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_private.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_private.h + * The symbols declared in this file are PRIVATE. + * They are not part of the GRRLIB public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + */ + +#ifndef __GRRLIB_PRIVATE_H__ +#define __GRRLIB_PRIVATE_H__ + +#include + +/** + * Used for version checking. + * @param a Major version number. + * @param b Minor version number. + * @param c Revision version number. + */ +#define GRRLIB_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) + +//------------------------------------------------------------------------------ +// GRRLIB_ttf.c - FreeType function for GRRLIB +int GRRLIB_InitTTF(); +void GRRLIB_ExitTTF(); + +#endif // __GRRLIB_PRIVATE_H__ diff --git a/lib/grrlib/include/grrlib/GRRLIB_settings.h b/lib/grrlib/include/grrlib/GRRLIB_settings.h new file mode 100644 index 0000000..2384ded --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_settings.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_settings.h + * Inline functions for configuring the GRRLIB settings. + */ + +#ifndef GX_BM_SUBTRACT + /** + * Blending type. + * libogc revision 4170 fixed a typo. GX_BM_SUBSTRACT was renamed GX_BM_SUBTRACT. + * But for previous versions this define is needed. + */ + #define GX_BM_SUBTRACT GX_BM_SUBSTRACT +#endif + +extern GRRLIB_drawSettings GRRLIB_Settings; + +/** + * Set a blending mode. + * @param blendmode The blending mode to use (Default: GRRLIB_BLEND_ALPHA). + */ +INLINE +void GRRLIB_SetBlend (const GRRLIB_blendMode blendmode) { + GRRLIB_Settings.blend = blendmode; + switch (GRRLIB_Settings.blend) { + case GRRLIB_BLEND_ALPHA: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_ADD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_DSTALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_SCREEN: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCCLR, GX_BL_DSTALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_MULTI: + GX_SetBlendMode(GX_BM_SUBTRACT, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case GRRLIB_BLEND_INV: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_INVSRCCLR, GX_BL_INVSRCCLR, GX_LO_CLEAR); + break; + } +} + +/** + * Get the current blending mode. + * @return The current blending mode. + */ +INLINE +GRRLIB_blendMode GRRLIB_GetBlend (void) { + return GRRLIB_Settings.blend; +} + +/** + * Turn anti-aliasing on/off. + * @param aa Set to true to enable anti-aliasing (Default: Enabled). + */ +INLINE +void GRRLIB_SetAntiAliasing (const bool aa) { + GRRLIB_Settings.antialias = aa; +} + +/** + * Get current anti-aliasing setting. + * @return True if anti-aliasing is enabled. + */ +INLINE +bool GRRLIB_GetAntiAliasing (void) { + return GRRLIB_Settings.antialias; +} + diff --git a/lib/grrlib/include/grrlib/GRRLIB_texSetup.h b/lib/grrlib/include/grrlib/GRRLIB_texSetup.h new file mode 100644 index 0000000..adb9b26 --- /dev/null +++ b/lib/grrlib/include/grrlib/GRRLIB_texSetup.h @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------------ +Copyright (c) 2010 The GRRLIB Team + +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. +------------------------------------------------------------------------------*/ + +/* + * @file GRRLIB_texSetup.h + * Inline functions for the basic manipulation of textures. + */ + +#if 0 + +#include +#include +#include + +/** + * Create an empty texture. + * @param w Width of the new texture to create. + * @param h Height of the new texture to create. + * @return A GRRLIB_texImg structure newly created. + */ +INLINE +GRRLIB_texImg* GRRLIB_CreateEmptyTexture (const uint w, const uint h) +{ + GRRLIB_texImg *my_texture = (struct GRRLIB_texImg *)calloc(1, sizeof(GRRLIB_texImg)); + + if(my_texture != NULL) { + my_texture->data = memalign(32, h * w * 4); + my_texture->w = w; + my_texture->h = h; + + // Initialize the texture + memset(my_texture->data, '\0', (h * w) << 2); + + GRRLIB_SetHandle(my_texture, 0, 0); + GRRLIB_FlushTex(my_texture); + } + return my_texture; +} + +/** + * Write the contents of a texture in the data cache down to main memory. + * For performance the CPU holds a data cache where modifications are stored before they get written down to main memory. + * @param tex The texture to flush. + */ +INLINE +void GRRLIB_FlushTex (GRRLIB_texImg *tex) { + DCFlushRange(tex->data, tex->w * tex->h * 4); +} + +/** + * Free memory allocated for texture. + * @param tex A GRRLIB_texImg structure. + */ +INLINE +void GRRLIB_FreeTexture (GRRLIB_texImg *tex) { + if(tex != NULL) { + if (tex->data != NULL) free(tex->data); + free(tex); + tex = NULL; + } +} + +/** + * Clear a texture to transparent black. + * @param tex Texture to clear. + */ +INLINE +void GRRLIB_ClearTex(GRRLIB_texImg* tex) { + bzero(tex->data, (tex->h * tex->w) << 2); + GRRLIB_FlushTex(tex); +} +#endif + diff --git a/lib/jpeg/include/jconfig.h b/lib/jpeg/include/jconfig.h new file mode 100644 index 0000000..97ca026 --- /dev/null +++ b/lib/jpeg/include/jconfig.h @@ -0,0 +1,46 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/lib/jpeg/include/jmorecfg.h b/lib/jpeg/include/jmorecfg.h new file mode 100644 index 0000000..fe6d87d --- /dev/null +++ b/lib/jpeg/include/jmorecfg.h @@ -0,0 +1,371 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/lib/jpeg/include/jpeglib.h b/lib/jpeg/include/jpeglib.h new file mode 100644 index 0000000..2b30fbd --- /dev/null +++ b/lib/jpeg/include/jpeglib.h @@ -0,0 +1,1158 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 80". + */ + +#define JPEG_LIB_VERSION 80 /* Version 8.0 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_h_scaled_size/DCTSIZE) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/lib/jpeg/src/Makefile b/lib/jpeg/src/Makefile new file mode 100644 index 0000000..e65dbe3 --- /dev/null +++ b/lib/jpeg/src/Makefile @@ -0,0 +1,53 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +include $(DEVKITPPC)/wii_rules + +#PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +#CC := $(PREFIX)gcc +#AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii +DEPSDIR := . + +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := ../include +DEST_LIB := ../lib$(DKV) + +INCLUDE := +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -Os $(MACHDEP) $(INCLUDE) + +LIB := jpeg +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +DEPENDS := $(OFILES:.o=.d) +ARC := lib$(LIB).a +HDR := jpeglib.h jconfig.h jmorecfg.h + +#all : $(OFILES) $(ARC) +# $(AR) -r $(ARC) $(OFILES) + +all : $(ARC) + +$(ARC) : $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) $(DEPENDS) + +install : + mkdir -p $(DEST_LIB) $(DEST_INC) + cp -f $(ARC) $(DEST_LIB)/ + cp -f $(HDR) $(DEST_INC)/ + +#%.o : %.c +# $(CC) $(CFLAGS) -c $< -o $@ + diff --git a/lib/jpeg/src/Makefile-orig b/lib/jpeg/src/Makefile-orig new file mode 100644 index 0000000..116076d --- /dev/null +++ b/lib/jpeg/src/Makefile-orig @@ -0,0 +1,40 @@ +# Quick'n'dirty makefile [BC] v2 + +ifeq ($(strip $(DEVKITPPC)),) + $(error "Use export DEVKITPPC=devkitPPC and try again") +endif + +ifeq ($(strip $(DEVKITPRO)),) + $(error "Use export DEVKITPRO=devkitPRO and try again") +endif + +PREFIX := $(DEVKITPPC)/bin/powerpc-eabi- +CC := $(PREFIX)gcc +AR := $(PREFIX)ar + +LIBOGC_INC := $(DEVKITPRO)/libogc/include +LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii + +INCLUDE := +MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float +CFLAGS := -O2 $(MACHDEP) $(INCLUDE) + +LIB := jpeg +CFILES := $(wildcard *.c) +OFILES := $(CFILES:.c=.o) +ARC := lib$(LIB).a +HDR := jpeglib.h jconfig.h jmorecfg.h + +all : $(OFILES) + $(AR) -r $(ARC) $(OFILES) + +clean : + rm -f $(OFILES) $(ARC) + +install : + mkdir -p $(LIBOGC_LIB) $(LIBOGC_INC) + cp -f $(ARC) $(LIBOGC_LIB)/ + cp -f $(HDR) $(LIBOGC_INC)/ + +%.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/jpeg/src/cderror.h b/lib/jpeg/src/cderror.h new file mode 100644 index 0000000..fb72a51 --- /dev/null +++ b/lib/jpeg/src/cderror.h @@ -0,0 +1,134 @@ +/* + * cderror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the cjpeg/djpeg + * applications. These strings are not needed as part of the JPEG library + * proper. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef CDERROR_H +#define CDERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* CDERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */ + +#ifdef BMP_SUPPORTED +JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format") +JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported") +JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length") +JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1") +JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB") +JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported") +JMESSAGE(JERR_BMP_EMPTY, "Empty BMP image") +JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM") +JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image") +JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image") +JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image") +JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image") +#endif /* BMP_SUPPORTED */ + +#ifdef GIF_SUPPORTED +JMESSAGE(JERR_GIF_BUG, "GIF output got confused") +JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d") +JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB") +JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file") +JMESSAGE(JERR_GIF_NOT, "Not a GIF file") +JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image") +JMESSAGE(JTRC_GIF_BADVERSION, + "Warning: unexpected GIF version number '%c%c%c'") +JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x") +JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input") +JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file") +JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring") +JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image") +JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits") +#endif /* GIF_SUPPORTED */ + +#ifdef PPM_SUPPORTED +JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB") +JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file") +JMESSAGE(JERR_PPM_NOT, "Not a PPM/PGM file") +JMESSAGE(JTRC_PGM, "%ux%u PGM image") +JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image") +JMESSAGE(JTRC_PPM, "%ux%u PPM image") +JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image") +#endif /* PPM_SUPPORTED */ + +#ifdef RLE_SUPPORTED +JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library") +JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB") +JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE") +JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file") +JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header") +JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header") +JMESSAGE(JERR_RLE_NOT, "Not an RLE file") +JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE") +JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup") +JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file") +JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d") +JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file") +JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d") +JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d") +#endif /* RLE_SUPPORTED */ + +#ifdef TARGA_SUPPORTED +JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format") +JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file") +JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB") +JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image") +JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image") +JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image") +#else +JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled") +#endif /* TARGA_SUPPORTED */ + +JMESSAGE(JERR_BAD_CMAP_FILE, + "Color map file is invalid or of unsupported format") +JMESSAGE(JERR_TOO_MANY_COLORS, + "Output file format cannot handle %d colormap entries") +JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed") +#ifdef TARGA_SUPPORTED +JMESSAGE(JERR_UNKNOWN_FORMAT, + "Unrecognized input file format --- perhaps you need -targa") +#else +JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format") +#endif +JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTADDONCODE +} ADDON_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE diff --git a/lib/jpeg/src/cdjpeg.c b/lib/jpeg/src/cdjpeg.c new file mode 100644 index 0000000..89fe633 --- /dev/null +++ b/lib/jpeg/src/cdjpeg.c @@ -0,0 +1,181 @@ +/* + * cdjpeg.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains common support routines used by the IJG application + * programs (cjpeg, djpeg, jpegtran). + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include /* to declare isupper(), tolower() */ +#ifdef NEED_SIGNAL_CATCHER +#include /* to declare signal() */ +#endif +#ifdef USE_SETMODE +#include /* to declare setmode()'s parameter macros */ +/* If you have setmode() but not , just delete this line: */ +#include /* to declare setmode() */ +#endif + + +/* + * Signal catcher to ensure that temporary files are removed before aborting. + * NB: for Amiga Manx C this is actually a global routine named _abort(); + * we put "#define signal_catcher _abort" in jconfig.h. Talk about bogus... + */ + +#ifdef NEED_SIGNAL_CATCHER + +static j_common_ptr sig_cinfo; + +void /* must be global for Manx C */ +signal_catcher (int signum) +{ + if (sig_cinfo != NULL) { + if (sig_cinfo->err != NULL) /* turn off trace output */ + sig_cinfo->err->trace_level = 0; + jpeg_destroy(sig_cinfo); /* clean up memory allocation & temp files */ + } + exit(EXIT_FAILURE); +} + + +GLOBAL(void) +enable_signal_catcher (j_common_ptr cinfo) +{ + sig_cinfo = cinfo; +#ifdef SIGINT /* not all systems have SIGINT */ + signal(SIGINT, signal_catcher); +#endif +#ifdef SIGTERM /* not all systems have SIGTERM */ + signal(SIGTERM, signal_catcher); +#endif +} + +#endif + + +/* + * Optional progress monitor: display a percent-done figure on stderr. + */ + +#ifdef PROGRESS_REPORT + +METHODDEF(void) +progress_monitor (j_common_ptr cinfo) +{ + cd_progress_ptr prog = (cd_progress_ptr) cinfo->progress; + int total_passes = prog->pub.total_passes + prog->total_extra_passes; + int percent_done = (int) (prog->pub.pass_counter*100L/prog->pub.pass_limit); + + if (percent_done != prog->percent_done) { + prog->percent_done = percent_done; + if (total_passes > 1) { + fprintf(stderr, "\rPass %d/%d: %3d%% ", + prog->pub.completed_passes + prog->completed_extra_passes + 1, + total_passes, percent_done); + } else { + fprintf(stderr, "\r %3d%% ", percent_done); + } + fflush(stderr); + } +} + + +GLOBAL(void) +start_progress_monitor (j_common_ptr cinfo, cd_progress_ptr progress) +{ + /* Enable progress display, unless trace output is on */ + if (cinfo->err->trace_level == 0) { + progress->pub.progress_monitor = progress_monitor; + progress->completed_extra_passes = 0; + progress->total_extra_passes = 0; + progress->percent_done = -1; + cinfo->progress = &progress->pub; + } +} + + +GLOBAL(void) +end_progress_monitor (j_common_ptr cinfo) +{ + /* Clear away progress display */ + if (cinfo->err->trace_level == 0) { + fprintf(stderr, "\r \r"); + fflush(stderr); + } +} + +#endif + + +/* + * Case-insensitive matching of possibly-abbreviated keyword switches. + * keyword is the constant keyword (must be lower case already), + * minchars is length of minimum legal abbreviation. + */ + +GLOBAL(boolean) +keymatch (char * arg, const char * keyword, int minchars) +{ + register int ca, ck; + register int nmatched = 0; + + while ((ca = *arg++) != '\0') { + if ((ck = *keyword++) == '\0') + return FALSE; /* arg longer than keyword, no good */ + if (isupper(ca)) /* force arg to lcase (assume ck is already) */ + ca = tolower(ca); + if (ca != ck) + return FALSE; /* no good */ + nmatched++; /* count matched characters */ + } + /* reached end of argument; fail if it's too short for unique abbrev */ + if (nmatched < minchars) + return FALSE; + return TRUE; /* A-OK */ +} + + +/* + * Routines to establish binary I/O mode for stdin and stdout. + * Non-Unix systems often require some hacking to get out of text mode. + */ + +GLOBAL(FILE *) +read_stdin (void) +{ + FILE * input_file = stdin; + +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdin), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((input_file = fdopen(fileno(stdin), READ_BINARY)) == NULL) { + fprintf(stderr, "Cannot reopen stdin\n"); + exit(EXIT_FAILURE); + } +#endif + return input_file; +} + + +GLOBAL(FILE *) +write_stdout (void) +{ + FILE * output_file = stdout; + +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdout), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((output_file = fdopen(fileno(stdout), WRITE_BINARY)) == NULL) { + fprintf(stderr, "Cannot reopen stdout\n"); + exit(EXIT_FAILURE); + } +#endif + return output_file; +} diff --git a/lib/jpeg/src/cdjpeg.d b/lib/jpeg/src/cdjpeg.d new file mode 100644 index 0000000..9929eb0 --- /dev/null +++ b/lib/jpeg/src/cdjpeg.d @@ -0,0 +1,16 @@ +cdjpeg.o: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/cdjpeg.h b/lib/jpeg/src/cdjpeg.h new file mode 100644 index 0000000..ed024ac --- /dev/null +++ b/lib/jpeg/src/cdjpeg.h @@ -0,0 +1,187 @@ +/* + * cdjpeg.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains common declarations for the sample applications + * cjpeg and djpeg. It is NOT used by the core JPEG library. + */ + +#define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ +#define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" /* get library error codes too */ +#include "cderror.h" /* get application-specific error codes */ + + +/* + * Object interface for cjpeg's source file decoding modules + */ + +typedef struct cjpeg_source_struct * cjpeg_source_ptr; + +struct cjpeg_source_struct { + JMETHOD(void, start_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(void, finish_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + + FILE *input_file; + + JSAMPARRAY buffer; + JDIMENSION buffer_height; +}; + + +/* + * Object interface for djpeg's output file encoding modules + */ + +typedef struct djpeg_dest_struct * djpeg_dest_ptr; + +struct djpeg_dest_struct { + /* start_output is called after jpeg_start_decompress finishes. + * The color map will be ready at this time, if one is needed. + */ + JMETHOD(void, start_output, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo)); + /* Emit the specified number of pixel rows from the buffer. */ + JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied)); + /* Finish up at the end of the image. */ + JMETHOD(void, finish_output, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo)); + + /* Target file spec; filled in by djpeg.c after object is created. */ + FILE * output_file; + + /* Output pixel-row buffer. Created by module init or start_output. + * Width is cinfo->output_width * cinfo->output_components; + * height is buffer_height. + */ + JSAMPARRAY buffer; + JDIMENSION buffer_height; +}; + + +/* + * cjpeg/djpeg may need to perform extra passes to convert to or from + * the source/destination file format. The JPEG library does not know + * about these passes, but we'd like them to be counted by the progress + * monitor. We use an expanded progress monitor object to hold the + * additional pass count. + */ + +struct cdjpeg_progress_mgr { + struct jpeg_progress_mgr pub; /* fields known to JPEG library */ + int completed_extra_passes; /* extra passes completed */ + int total_extra_passes; /* total extra */ + /* last printed percentage stored here to avoid multiple printouts */ + int percent_done; +}; + +typedef struct cdjpeg_progress_mgr * cd_progress_ptr; + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_read_bmp jIRdBMP +#define jinit_write_bmp jIWrBMP +#define jinit_read_gif jIRdGIF +#define jinit_write_gif jIWrGIF +#define jinit_read_ppm jIRdPPM +#define jinit_write_ppm jIWrPPM +#define jinit_read_rle jIRdRLE +#define jinit_write_rle jIWrRLE +#define jinit_read_targa jIRdTarga +#define jinit_write_targa jIWrTarga +#define read_quant_tables RdQTables +#define read_scan_script RdScnScript +#define set_quality_ratings SetQRates +#define set_quant_slots SetQSlots +#define set_sample_factors SetSFacts +#define read_color_map RdCMap +#define enable_signal_catcher EnSigCatcher +#define start_progress_monitor StProgMon +#define end_progress_monitor EnProgMon +#define read_stdin RdStdin +#define write_stdout WrStdout +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Module selection routines for I/O modules. */ + +EXTERN(cjpeg_source_ptr) jinit_read_bmp JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_bmp JPP((j_decompress_ptr cinfo, + boolean is_os2)); +EXTERN(cjpeg_source_ptr) jinit_read_gif JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_gif JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_ppm JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_ppm JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_rle JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_rle JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_targa JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_targa JPP((j_decompress_ptr cinfo)); + +/* cjpeg support routines (in rdswitch.c) */ + +EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, + boolean force_baseline)); +EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); +EXTERN(boolean) set_quality_ratings JPP((j_compress_ptr cinfo, char *arg, + boolean force_baseline)); +EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); +EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); + +/* djpeg support routines (in rdcolmap.c) */ + +EXTERN(void) read_color_map JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* common support routines (in cdjpeg.c) */ + +EXTERN(void) enable_signal_catcher JPP((j_common_ptr cinfo)); +EXTERN(void) start_progress_monitor JPP((j_common_ptr cinfo, + cd_progress_ptr progress)); +EXTERN(void) end_progress_monitor JPP((j_common_ptr cinfo)); +EXTERN(boolean) keymatch JPP((char * arg, const char * keyword, int minchars)); +EXTERN(FILE *) read_stdin JPP((void)); +EXTERN(FILE *) write_stdout JPP((void)); + +/* miscellaneous useful macros */ + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#define WRITE_BINARY "w" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#define WRITE_BINARY "wb", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#define WRITE_BINARY "wb" +#endif +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#ifdef VMS +#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ +#else +#define EXIT_SUCCESS 0 +#endif +#endif +#ifndef EXIT_WARNING +#ifdef VMS +#define EXIT_WARNING 1 /* VMS is very nonstandard */ +#else +#define EXIT_WARNING 2 +#endif +#endif diff --git a/lib/jpeg/src/cjpeg.c b/lib/jpeg/src/cjpeg.c new file mode 100644 index 0000000..8acd302 --- /dev/null +++ b/lib/jpeg/src/cjpeg.c @@ -0,0 +1,616 @@ +/* + * cjpeg.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2003-2008 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for the JPEG compressor. + * It should work on any system with Unix- or MS-DOS-style command lines. + * + * Two different command line styles are permitted, depending on the + * compile-time switch TWO_FILE_COMMANDLINE: + * cjpeg [options] inputfile outputfile + * cjpeg [options] [inputfile] + * In the second style, output is always to standard output, which you'd + * normally redirect to a file or pipe to some other program. Input is + * either from a named file or from standard input (typically redirected). + * The second style is convenient on Unix but is unhelpful on systems that + * don't support pipes. Also, you MUST use the first style if your system + * doesn't do binary I/O to stdin/stdout. + * To simplify script writing, the "-outfile" switch is provided. The syntax + * cjpeg [options] -outfile outputfile inputfile + * works regardless of which command line style is used. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jversion.h" /* for version message */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* Create the add-on message string table. */ + +#define JMESSAGE(code,string) string , + +static const char * const cdjpeg_message_table[] = { +#include "cderror.h" + NULL +}; + + +/* + * This routine determines what format the input file is, + * and selects the appropriate input-reading module. + * + * To determine which family of input formats the file belongs to, + * we may look only at the first byte of the file, since C does not + * guarantee that more than one character can be pushed back with ungetc. + * Looking at additional bytes would require one of these approaches: + * 1) assume we can fseek() the input file (fails for piped input); + * 2) assume we can push back more than one character (works in + * some C implementations, but unportable); + * 3) provide our own buffering (breaks input readers that want to use + * stdio directly, such as the RLE library); + * or 4) don't put back the data, and modify the input_init methods to assume + * they start reading after the start of file (also breaks RLE library). + * #1 is attractive for MS-DOS but is untenable on Unix. + * + * The most portable solution for file types that can't be identified by their + * first byte is to make the user tell us what they are. This is also the + * only approach for "raw" file types that contain only arbitrary values. + * We presently apply this method for Targa files. Most of the time Targa + * files start with 0x00, so we recognize that case. Potentially, however, + * a Targa file could start with any byte value (byte 0 is the length of the + * seldom-used ID field), so we provide a switch to force Targa input mode. + */ + +static boolean is_targa; /* records user -targa switch */ + + +LOCAL(cjpeg_source_ptr) +select_file_type (j_compress_ptr cinfo, FILE * infile) +{ + int c; + + if (is_targa) { +#ifdef TARGA_SUPPORTED + return jinit_read_targa(cinfo); +#else + ERREXIT(cinfo, JERR_TGA_NOTCOMP); +#endif + } + + if ((c = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_INPUT_EMPTY); + if (ungetc(c, infile) == EOF) + ERREXIT(cinfo, JERR_UNGETC_FAILED); + + switch (c) { +#ifdef BMP_SUPPORTED + case 'B': + return jinit_read_bmp(cinfo); +#endif +#ifdef GIF_SUPPORTED + case 'G': + return jinit_read_gif(cinfo); +#endif +#ifdef PPM_SUPPORTED + case 'P': + return jinit_read_ppm(cinfo); +#endif +#ifdef RLE_SUPPORTED + case 'R': + return jinit_read_rle(cinfo); +#endif +#ifdef TARGA_SUPPORTED + case 0x00: + return jinit_read_targa(cinfo); +#endif + default: + ERREXIT(cinfo, JERR_UNKNOWN_FORMAT); + break; + } + + return NULL; /* suppress compiler warnings */ +} + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -quality N[,...] Compression quality (0..100; 5-95 is useful range)\n"); + fprintf(stderr, " -grayscale Create monochrome JPEG file\n"); +#ifdef ENTROPY_OPT_SUPPORTED + fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); +#endif +#ifdef C_PROGRESSIVE_SUPPORTED + fprintf(stderr, " -progressive Create progressive JPEG file\n"); +#endif +#ifdef DCT_SCALING_SUPPORTED + fprintf(stderr, " -scale M/N Scale image by fraction M/N, eg, 1/2\n"); +#endif +#ifdef TARGA_SUPPORTED + fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); +#endif + fprintf(stderr, "Switches for advanced users:\n"); +#ifdef DCT_ISLOW_SUPPORTED + fprintf(stderr, " -dct int Use integer DCT method%s\n", + (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); +#endif +#ifdef DCT_IFAST_SUPPORTED + fprintf(stderr, " -dct fast Use fast integer DCT (less accurate)%s\n", + (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : "")); +#endif +#ifdef DCT_FLOAT_SUPPORTED + fprintf(stderr, " -dct float Use floating-point DCT method%s\n", + (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : "")); +#endif + fprintf(stderr, " -nosmooth Don't use high-quality downsampling\n"); + fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n"); +#ifdef INPUT_SMOOTHING_SUPPORTED + fprintf(stderr, " -smooth N Smooth dithered input (N=1..100 is strength)\n"); +#endif + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + fprintf(stderr, "Switches for wizards:\n"); +#ifdef C_ARITH_CODING_SUPPORTED + fprintf(stderr, " -arithmetic Use arithmetic coding\n"); +#endif + fprintf(stderr, " -baseline Force baseline quantization tables\n"); + fprintf(stderr, " -qtables file Use quantization tables given in file\n"); + fprintf(stderr, " -qslots N[,...] Set component quantization tables\n"); + fprintf(stderr, " -sample HxV[,...] Set component sampling factors\n"); +#ifdef C_MULTISCAN_FILES_SUPPORTED + fprintf(stderr, " -scans file Create multi-scan JPEG per script file\n"); +#endif + exit(EXIT_FAILURE); +} + + +LOCAL(int) +parse_switches (j_compress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + boolean force_baseline; + boolean simple_progressive; + char * qualityarg = NULL; /* saves -quality parm if any */ + char * qtablefile = NULL; /* saves -qtables filename if any */ + char * qslotsarg = NULL; /* saves -qslots parm if any */ + char * samplearg = NULL; /* saves -sample parm if any */ + char * scansarg = NULL; /* saves -scans parm if any */ + + /* Set up default JPEG parameters. */ + + force_baseline = FALSE; /* by default, allow 16-bit quantizers */ + simple_progressive = FALSE; + is_targa = FALSE; + outfilename = NULL; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "arithmetic", 1)) { + /* Use arithmetic coding. */ +#ifdef C_ARITH_CODING_SUPPORTED + cinfo->arith_code = TRUE; +#else + fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "baseline", 1)) { + /* Force baseline-compatible output (8-bit quantizer values). */ + force_baseline = TRUE; + + } else if (keymatch(arg, "dct", 2)) { + /* Select DCT algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "int", 1)) { + cinfo->dct_method = JDCT_ISLOW; + } else if (keymatch(argv[argn], "fast", 2)) { + cinfo->dct_method = JDCT_IFAST; + } else if (keymatch(argv[argn], "float", 2)) { + cinfo->dct_method = JDCT_FLOAT; + } else + usage(); + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's CJPEG, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) { + /* Force a monochrome JPEG file to be generated. */ + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "nosmooth", 3)) { + /* Suppress fancy downsampling */ + cinfo->do_fancy_downsampling = FALSE; + + } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) { + /* Enable entropy parm optimization. */ +#ifdef ENTROPY_OPT_SUPPORTED + cinfo->optimize_coding = TRUE; +#else + fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "progressive", 1)) { + /* Select simple progressive mode. */ +#ifdef C_PROGRESSIVE_SUPPORTED + simple_progressive = TRUE; + /* We must postpone execution until num_components is known. */ +#else + fprintf(stderr, "%s: sorry, progressive output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "quality", 1)) { + /* Quality ratings (quantization table scaling factors). */ + if (++argn >= argc) /* advance to next argument */ + usage(); + qualityarg = argv[argn]; + + } else if (keymatch(arg, "qslots", 2)) { + /* Quantization table slot numbers. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + qslotsarg = argv[argn]; + /* Must delay setting qslots until after we have processed any + * colorspace-determining switches, since jpeg_set_colorspace sets + * default quant table numbers. + */ + + } else if (keymatch(arg, "qtables", 2)) { + /* Quantization tables fetched from file. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + qtablefile = argv[argn]; + /* We postpone actually reading the file in case -quality comes later. */ + + } else if (keymatch(arg, "restart", 1)) { + /* Restart interval in MCU rows (or in MCUs with 'b'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (lval < 0 || lval > 65535L) + usage(); + if (ch == 'b' || ch == 'B') { + cinfo->restart_interval = (unsigned int) lval; + cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */ + } else { + cinfo->restart_in_rows = (int) lval; + /* restart_interval will be computed during startup */ + } + + } else if (keymatch(arg, "sample", 2)) { + /* Set sampling factors. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + samplearg = argv[argn]; + /* Must delay setting sample factors until after we have processed any + * colorspace-determining switches, since jpeg_set_colorspace sets + * default sampling factors. + */ + + } else if (keymatch(arg, "scale", 4)) { + /* Scale the image by a fraction M/N. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d/%d", + &cinfo->scale_num, &cinfo->scale_denom) != 2) + usage(); + + } else if (keymatch(arg, "scans", 4)) { + /* Set scan script. */ +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (++argn >= argc) /* advance to next argument */ + usage(); + scansarg = argv[argn]; + /* We must postpone reading the file in case -progressive appears. */ +#else + fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "smooth", 2)) { + /* Set input smoothing factor. */ + int val; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &val) != 1) + usage(); + if (val < 0 || val > 100) + usage(); + cinfo->smoothing_factor = val; + + } else if (keymatch(arg, "targa", 1)) { + /* Input file is Targa format. */ + is_targa = TRUE; + + } else { + usage(); /* bogus switch */ + } + } + + /* Post-switch-scanning cleanup */ + + if (for_real) { + + /* Set quantization tables for selected quality. */ + /* Some or all may be overridden if -qtables is present. */ + if (qualityarg != NULL) /* process -quality if it was present */ + if (! set_quality_ratings(cinfo, qualityarg, force_baseline)) + usage(); + + if (qtablefile != NULL) /* process -qtables if it was present */ + if (! read_quant_tables(cinfo, qtablefile, force_baseline)) + usage(); + + if (qslotsarg != NULL) /* process -qslots if it was present */ + if (! set_quant_slots(cinfo, qslotsarg)) + usage(); + + if (samplearg != NULL) /* process -sample if it was present */ + if (! set_sample_factors(cinfo, samplearg)) + usage(); + +#ifdef C_PROGRESSIVE_SUPPORTED + if (simple_progressive) /* process -progressive; -scans can override */ + jpeg_simple_progression(cinfo); +#endif + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (scansarg != NULL) /* process -scans if it was present */ + if (! read_scan_script(cinfo, scansarg)) + usage(); +#endif + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + int file_index; + cjpeg_source_ptr src_mgr; + FILE * input_file; + FILE * output_file; + JDIMENSION num_scanlines; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "cjpeg"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG compression object with default error handling. */ + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + /* Add some application-specific error messages (from cderror.h) */ + jerr.addon_message_table = cdjpeg_message_table; + jerr.first_addon_message = JMSG_FIRSTADDONCODE; + jerr.last_addon_message = JMSG_LASTADDONCODE; + + /* Now safe to enable signal catcher. */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &cinfo); +#endif + + /* Initialize JPEG parameters. + * Much of this may be overridden later. + * In particular, we don't yet know the input file's color space, + * but we need to provide some value for jpeg_set_defaults() to work. + */ + + cinfo.in_color_space = JCS_RGB; /* arbitrary guess */ + jpeg_set_defaults(&cinfo); + + /* Scan command line to find file names. + * It is convenient to use just one switch-parsing routine, but the switch + * values read here are ignored; we will rescan the switches after opening + * the input file. + */ + + file_index = parse_switches(&cinfo, argc, argv, 0, FALSE); + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + input_file = read_stdin(); + } + + /* Open the output file. */ + if (outfilename != NULL) { + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + output_file = write_stdout(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &cinfo, &progress); +#endif + + /* Figure out the input file format, and set up to read it. */ + src_mgr = select_file_type(&cinfo, input_file); + src_mgr->input_file = input_file; + + /* Read the input file header to obtain file size & colorspace. */ + (*src_mgr->start_input) (&cinfo, src_mgr); + + /* Now that we know input colorspace, fix colorspace-dependent defaults */ + jpeg_default_colorspace(&cinfo); + + /* Adjust default compression parameters by re-parsing the options */ + file_index = parse_switches(&cinfo, argc, argv, 0, TRUE); + + /* Specify data destination for compression */ + jpeg_stdio_dest(&cinfo, output_file); + + /* Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Process data */ + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines); + } + + /* Finish compression and release memory */ + (*src_mgr->finish_input) (&cinfo, src_mgr); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + /* Close files, if we opened them */ + if (input_file != stdin) + fclose(input_file); + if (output_file != stdout) + fclose(output_file); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &cinfo); +#endif + + /* All done. */ + exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/lib/jpeg/src/cjpeg.d b/lib/jpeg/src/cjpeg.d new file mode 100644 index 0000000..3a5307b --- /dev/null +++ b/lib/jpeg/src/cjpeg.d @@ -0,0 +1,18 @@ +cjpeg.o: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h jversion.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: + +jversion.h: diff --git a/lib/jpeg/src/djpeg.c b/lib/jpeg/src/djpeg.c new file mode 100644 index 0000000..363c628 --- /dev/null +++ b/lib/jpeg/src/djpeg.c @@ -0,0 +1,617 @@ +/* + * djpeg.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for the JPEG decompressor. + * It should work on any system with Unix- or MS-DOS-style command lines. + * + * Two different command line styles are permitted, depending on the + * compile-time switch TWO_FILE_COMMANDLINE: + * djpeg [options] inputfile outputfile + * djpeg [options] [inputfile] + * In the second style, output is always to standard output, which you'd + * normally redirect to a file or pipe to some other program. Input is + * either from a named file or from standard input (typically redirected). + * The second style is convenient on Unix but is unhelpful on systems that + * don't support pipes. Also, you MUST use the first style if your system + * doesn't do binary I/O to stdin/stdout. + * To simplify script writing, the "-outfile" switch is provided. The syntax + * djpeg [options] -outfile outputfile inputfile + * works regardless of which command line style is used. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jversion.h" /* for version message */ + +#include /* to declare isprint() */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* Create the add-on message string table. */ + +#define JMESSAGE(code,string) string , + +static const char * const cdjpeg_message_table[] = { +#include "cderror.h" + NULL +}; + + +/* + * This list defines the known output image formats + * (not all of which need be supported by a given version). + * You can change the default output format by defining DEFAULT_FMT; + * indeed, you had better do so if you undefine PPM_SUPPORTED. + */ + +typedef enum { + FMT_BMP, /* BMP format (Windows flavor) */ + FMT_GIF, /* GIF format */ + FMT_OS2, /* BMP format (OS/2 flavor) */ + FMT_PPM, /* PPM/PGM (PBMPLUS formats) */ + FMT_RLE, /* RLE format */ + FMT_TARGA, /* Targa format */ + FMT_TIFF /* TIFF format */ +} IMAGE_FORMATS; + +#ifndef DEFAULT_FMT /* so can override from CFLAGS in Makefile */ +#define DEFAULT_FMT FMT_PPM +#endif + +static IMAGE_FORMATS requested_fmt; + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -colors N Reduce image to no more than N colors\n"); + fprintf(stderr, " -fast Fast, low-quality processing\n"); + fprintf(stderr, " -grayscale Force grayscale output\n"); +#ifdef IDCT_SCALING_SUPPORTED + fprintf(stderr, " -scale M/N Scale output image by fraction M/N, eg, 1/8\n"); +#endif +#ifdef BMP_SUPPORTED + fprintf(stderr, " -bmp Select BMP output format (Windows style)%s\n", + (DEFAULT_FMT == FMT_BMP ? " (default)" : "")); +#endif +#ifdef GIF_SUPPORTED + fprintf(stderr, " -gif Select GIF output format%s\n", + (DEFAULT_FMT == FMT_GIF ? " (default)" : "")); +#endif +#ifdef BMP_SUPPORTED + fprintf(stderr, " -os2 Select BMP output format (OS/2 style)%s\n", + (DEFAULT_FMT == FMT_OS2 ? " (default)" : "")); +#endif +#ifdef PPM_SUPPORTED + fprintf(stderr, " -pnm Select PBMPLUS (PPM/PGM) output format%s\n", + (DEFAULT_FMT == FMT_PPM ? " (default)" : "")); +#endif +#ifdef RLE_SUPPORTED + fprintf(stderr, " -rle Select Utah RLE output format%s\n", + (DEFAULT_FMT == FMT_RLE ? " (default)" : "")); +#endif +#ifdef TARGA_SUPPORTED + fprintf(stderr, " -targa Select Targa output format%s\n", + (DEFAULT_FMT == FMT_TARGA ? " (default)" : "")); +#endif + fprintf(stderr, "Switches for advanced users:\n"); +#ifdef DCT_ISLOW_SUPPORTED + fprintf(stderr, " -dct int Use integer DCT method%s\n", + (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); +#endif +#ifdef DCT_IFAST_SUPPORTED + fprintf(stderr, " -dct fast Use fast integer DCT (less accurate)%s\n", + (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : "")); +#endif +#ifdef DCT_FLOAT_SUPPORTED + fprintf(stderr, " -dct float Use floating-point DCT method%s\n", + (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : "")); +#endif + fprintf(stderr, " -dither fs Use F-S dithering (default)\n"); + fprintf(stderr, " -dither none Don't use dithering in quantization\n"); + fprintf(stderr, " -dither ordered Use ordered dither (medium speed, quality)\n"); +#ifdef QUANT_2PASS_SUPPORTED + fprintf(stderr, " -map FILE Map to colors used in named image file\n"); +#endif + fprintf(stderr, " -nosmooth Don't use high-quality upsampling\n"); +#ifdef QUANT_1PASS_SUPPORTED + fprintf(stderr, " -onepass Use 1-pass quantization (fast, low quality)\n"); +#endif + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + exit(EXIT_FAILURE); +} + + +LOCAL(int) +parse_switches (j_decompress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + + /* Set up default JPEG parameters. */ + requested_fmt = DEFAULT_FMT; /* set default output file format */ + outfilename = NULL; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "bmp", 1)) { + /* BMP output format. */ + requested_fmt = FMT_BMP; + + } else if (keymatch(arg, "colors", 1) || keymatch(arg, "colours", 1) || + keymatch(arg, "quantize", 1) || keymatch(arg, "quantise", 1)) { + /* Do color quantization. */ + int val; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &val) != 1) + usage(); + cinfo->desired_number_of_colors = val; + cinfo->quantize_colors = TRUE; + + } else if (keymatch(arg, "dct", 2)) { + /* Select IDCT algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "int", 1)) { + cinfo->dct_method = JDCT_ISLOW; + } else if (keymatch(argv[argn], "fast", 2)) { + cinfo->dct_method = JDCT_IFAST; + } else if (keymatch(argv[argn], "float", 2)) { + cinfo->dct_method = JDCT_FLOAT; + } else + usage(); + + } else if (keymatch(arg, "dither", 2)) { + /* Select dithering algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "fs", 2)) { + cinfo->dither_mode = JDITHER_FS; + } else if (keymatch(argv[argn], "none", 2)) { + cinfo->dither_mode = JDITHER_NONE; + } else if (keymatch(argv[argn], "ordered", 2)) { + cinfo->dither_mode = JDITHER_ORDERED; + } else + usage(); + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's DJPEG, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "fast", 1)) { + /* Select recommended processing options for quick-and-dirty output. */ + cinfo->two_pass_quantize = FALSE; + cinfo->dither_mode = JDITHER_ORDERED; + if (! cinfo->quantize_colors) /* don't override an earlier -colors */ + cinfo->desired_number_of_colors = 216; + cinfo->dct_method = JDCT_FASTEST; + cinfo->do_fancy_upsampling = FALSE; + + } else if (keymatch(arg, "gif", 1)) { + /* GIF output format. */ + requested_fmt = FMT_GIF; + + } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) { + /* Force monochrome output. */ + cinfo->out_color_space = JCS_GRAYSCALE; + + } else if (keymatch(arg, "map", 3)) { + /* Quantize to a color map taken from an input file. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (for_real) { /* too expensive to do twice! */ +#ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ + FILE * mapfile; + + if ((mapfile = fopen(argv[argn], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); + exit(EXIT_FAILURE); + } + read_color_map(cinfo, mapfile); + fclose(mapfile); + cinfo->quantize_colors = TRUE; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "nosmooth", 3)) { + /* Suppress fancy upsampling */ + cinfo->do_fancy_upsampling = FALSE; + + } else if (keymatch(arg, "onepass", 3)) { + /* Use fast one-pass quantization. */ + cinfo->two_pass_quantize = FALSE; + + } else if (keymatch(arg, "os2", 3)) { + /* BMP output format (OS/2 flavor). */ + requested_fmt = FMT_OS2; + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "pnm", 1) || keymatch(arg, "ppm", 1)) { + /* PPM/PGM output format. */ + requested_fmt = FMT_PPM; + + } else if (keymatch(arg, "rle", 1)) { + /* RLE output format. */ + requested_fmt = FMT_RLE; + + } else if (keymatch(arg, "scale", 1)) { + /* Scale the output image by a fraction M/N. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d/%d", + &cinfo->scale_num, &cinfo->scale_denom) < 1) + usage(); + + } else if (keymatch(arg, "targa", 1)) { + /* Targa output format. */ + requested_fmt = FMT_TARGA; + + } else { + usage(); /* bogus switch */ + } + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * Marker processor for COM and interesting APPn markers. + * This replaces the library's built-in processor, which just skips the marker. + * We want to print out the marker as text, to the extent possible. + * Note this code relies on a non-suspending data source. + */ + +LOCAL(unsigned int) +jpeg_getc (j_decompress_ptr cinfo) +/* Read next byte */ +{ + struct jpeg_source_mgr * datasrc = cinfo->src; + + if (datasrc->bytes_in_buffer == 0) { + if (! (*datasrc->fill_input_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + datasrc->bytes_in_buffer--; + return GETJOCTET(*datasrc->next_input_byte++); +} + + +METHODDEF(boolean) +print_text_marker (j_decompress_ptr cinfo) +{ + boolean traceit = (cinfo->err->trace_level >= 1); + INT32 length; + unsigned int ch; + unsigned int lastch = 0; + + length = jpeg_getc(cinfo) << 8; + length += jpeg_getc(cinfo); + length -= 2; /* discount the length word itself */ + + if (traceit) { + if (cinfo->unread_marker == JPEG_COM) + fprintf(stderr, "Comment, length %ld:\n", (long) length); + else /* assume it is an APPn otherwise */ + fprintf(stderr, "APP%d, length %ld:\n", + cinfo->unread_marker - JPEG_APP0, (long) length); + } + + while (--length >= 0) { + ch = jpeg_getc(cinfo); + if (traceit) { + /* Emit the character in a readable form. + * Nonprintables are converted to \nnn form, + * while \ is converted to \\. + * Newlines in CR, CR/LF, or LF form will be printed as one newline. + */ + if (ch == '\r') { + fprintf(stderr, "\n"); + } else if (ch == '\n') { + if (lastch != '\r') + fprintf(stderr, "\n"); + } else if (ch == '\\') { + fprintf(stderr, "\\\\"); + } else if (isprint(ch)) { + putc(ch, stderr); + } else { + fprintf(stderr, "\\%03o", ch); + } + lastch = ch; + } + } + + if (traceit) + fprintf(stderr, "\n"); + + return TRUE; +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + int file_index; + djpeg_dest_ptr dest_mgr = NULL; + FILE * input_file; + FILE * output_file; + JDIMENSION num_scanlines; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "djpeg"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG decompression object with default error handling. */ + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + /* Add some application-specific error messages (from cderror.h) */ + jerr.addon_message_table = cdjpeg_message_table; + jerr.first_addon_message = JMSG_FIRSTADDONCODE; + jerr.last_addon_message = JMSG_LASTADDONCODE; + + /* Insert custom marker processor for COM and APP12. + * APP12 is used by some digital camera makers for textual info, + * so we provide the ability to display it as text. + * If you like, additional APPn marker types can be selected for display, + * but don't try to override APP0 or APP14 this way (see libjpeg.doc). + */ + jpeg_set_marker_processor(&cinfo, JPEG_COM, print_text_marker); + jpeg_set_marker_processor(&cinfo, JPEG_APP0+12, print_text_marker); + + /* Now safe to enable signal catcher. */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &cinfo); +#endif + + /* Scan command line to find file names. */ + /* It is convenient to use just one switch-parsing routine, but the switch + * values read here are ignored; we will rescan the switches after opening + * the input file. + * (Exception: tracing level set here controls verbosity for COM markers + * found during jpeg_read_header...) + */ + + file_index = parse_switches(&cinfo, argc, argv, 0, FALSE); + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + input_file = read_stdin(); + } + + /* Open the output file. */ + if (outfilename != NULL) { + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + output_file = write_stdout(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &cinfo, &progress); +#endif + + /* Specify data source for decompression */ + jpeg_stdio_src(&cinfo, input_file); + + /* Read file header, set default decompression parameters */ + (void) jpeg_read_header(&cinfo, TRUE); + + /* Adjust default decompression parameters by re-parsing the options */ + file_index = parse_switches(&cinfo, argc, argv, 0, TRUE); + + /* Initialize the output module now to let it override any crucial + * option settings (for instance, GIF wants to force color quantization). + */ + switch (requested_fmt) { +#ifdef BMP_SUPPORTED + case FMT_BMP: + dest_mgr = jinit_write_bmp(&cinfo, FALSE); + break; + case FMT_OS2: + dest_mgr = jinit_write_bmp(&cinfo, TRUE); + break; +#endif +#ifdef GIF_SUPPORTED + case FMT_GIF: + dest_mgr = jinit_write_gif(&cinfo); + break; +#endif +#ifdef PPM_SUPPORTED + case FMT_PPM: + dest_mgr = jinit_write_ppm(&cinfo); + break; +#endif +#ifdef RLE_SUPPORTED + case FMT_RLE: + dest_mgr = jinit_write_rle(&cinfo); + break; +#endif +#ifdef TARGA_SUPPORTED + case FMT_TARGA: + dest_mgr = jinit_write_targa(&cinfo); + break; +#endif + default: + ERREXIT(&cinfo, JERR_UNSUPPORTED_FORMAT); + break; + } + dest_mgr->output_file = output_file; + + /* Start decompressor */ + (void) jpeg_start_decompress(&cinfo); + + /* Write output file header */ + (*dest_mgr->start_output) (&cinfo, dest_mgr); + + /* Process data */ + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + +#ifdef PROGRESS_REPORT + /* Hack: count final pass as done in case finish_output does an extra pass. + * The library won't have updated completed_passes. + */ + progress.pub.completed_passes = progress.pub.total_passes; +#endif + + /* Finish decompression and release memory. + * I must do it in this order because output module has allocated memory + * of lifespan JPOOL_IMAGE; it needs to finish before releasing memory. + */ + (*dest_mgr->finish_output) (&cinfo, dest_mgr); + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + /* Close files, if we opened them */ + if (input_file != stdin) + fclose(input_file); + if (output_file != stdout) + fclose(output_file); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &cinfo); +#endif + + /* All done. */ + exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/lib/jpeg/src/djpeg.d b/lib/jpeg/src/djpeg.d new file mode 100644 index 0000000..bf9f1ee --- /dev/null +++ b/lib/jpeg/src/djpeg.d @@ -0,0 +1,18 @@ +djpeg.o: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h jversion.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: + +jversion.h: diff --git a/lib/jpeg/src/jaricom.c b/lib/jpeg/src/jaricom.c new file mode 100644 index 0000000..d130aac --- /dev/null +++ b/lib/jpeg/src/jaricom.c @@ -0,0 +1,153 @@ +/* + * jaricom.c + * + * Developed 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains probability estimation tables for common use in + * arithmetic entropy encoding and decoding routines. + * + * This data represents Table D.2 in the JPEG spec (ISO/IEC IS 10918-1 + * and CCITT Recommendation ITU-T T.81) and Table 24 in the JBIG spec + * (ISO/IEC IS 11544 and CCITT Recommendation ITU-T T.82). + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +/* The following #define specifies the packing of the four components + * into the compact INT32 representation. + * Note that this formula must match the actual arithmetic encoder + * and decoder implementation. The implementation has to be changed + * if this formula is changed. + * The current organization is leaned on Markus Kuhn's JBIG + * implementation (jbig_tab.c). + */ + +#define V(i,a,b,c,d) (((INT32)a << 16) | ((INT32)c << 8) | ((INT32)d << 7) | b) + +const INT32 jpeg_aritab[113+1] = { +/* + * Index, Qe_Value, Next_Index_LPS, Next_Index_MPS, Switch_MPS + */ + V( 0, 0x5a1d, 1, 1, 1 ), + V( 1, 0x2586, 14, 2, 0 ), + V( 2, 0x1114, 16, 3, 0 ), + V( 3, 0x080b, 18, 4, 0 ), + V( 4, 0x03d8, 20, 5, 0 ), + V( 5, 0x01da, 23, 6, 0 ), + V( 6, 0x00e5, 25, 7, 0 ), + V( 7, 0x006f, 28, 8, 0 ), + V( 8, 0x0036, 30, 9, 0 ), + V( 9, 0x001a, 33, 10, 0 ), + V( 10, 0x000d, 35, 11, 0 ), + V( 11, 0x0006, 9, 12, 0 ), + V( 12, 0x0003, 10, 13, 0 ), + V( 13, 0x0001, 12, 13, 0 ), + V( 14, 0x5a7f, 15, 15, 1 ), + V( 15, 0x3f25, 36, 16, 0 ), + V( 16, 0x2cf2, 38, 17, 0 ), + V( 17, 0x207c, 39, 18, 0 ), + V( 18, 0x17b9, 40, 19, 0 ), + V( 19, 0x1182, 42, 20, 0 ), + V( 20, 0x0cef, 43, 21, 0 ), + V( 21, 0x09a1, 45, 22, 0 ), + V( 22, 0x072f, 46, 23, 0 ), + V( 23, 0x055c, 48, 24, 0 ), + V( 24, 0x0406, 49, 25, 0 ), + V( 25, 0x0303, 51, 26, 0 ), + V( 26, 0x0240, 52, 27, 0 ), + V( 27, 0x01b1, 54, 28, 0 ), + V( 28, 0x0144, 56, 29, 0 ), + V( 29, 0x00f5, 57, 30, 0 ), + V( 30, 0x00b7, 59, 31, 0 ), + V( 31, 0x008a, 60, 32, 0 ), + V( 32, 0x0068, 62, 33, 0 ), + V( 33, 0x004e, 63, 34, 0 ), + V( 34, 0x003b, 32, 35, 0 ), + V( 35, 0x002c, 33, 9, 0 ), + V( 36, 0x5ae1, 37, 37, 1 ), + V( 37, 0x484c, 64, 38, 0 ), + V( 38, 0x3a0d, 65, 39, 0 ), + V( 39, 0x2ef1, 67, 40, 0 ), + V( 40, 0x261f, 68, 41, 0 ), + V( 41, 0x1f33, 69, 42, 0 ), + V( 42, 0x19a8, 70, 43, 0 ), + V( 43, 0x1518, 72, 44, 0 ), + V( 44, 0x1177, 73, 45, 0 ), + V( 45, 0x0e74, 74, 46, 0 ), + V( 46, 0x0bfb, 75, 47, 0 ), + V( 47, 0x09f8, 77, 48, 0 ), + V( 48, 0x0861, 78, 49, 0 ), + V( 49, 0x0706, 79, 50, 0 ), + V( 50, 0x05cd, 48, 51, 0 ), + V( 51, 0x04de, 50, 52, 0 ), + V( 52, 0x040f, 50, 53, 0 ), + V( 53, 0x0363, 51, 54, 0 ), + V( 54, 0x02d4, 52, 55, 0 ), + V( 55, 0x025c, 53, 56, 0 ), + V( 56, 0x01f8, 54, 57, 0 ), + V( 57, 0x01a4, 55, 58, 0 ), + V( 58, 0x0160, 56, 59, 0 ), + V( 59, 0x0125, 57, 60, 0 ), + V( 60, 0x00f6, 58, 61, 0 ), + V( 61, 0x00cb, 59, 62, 0 ), + V( 62, 0x00ab, 61, 63, 0 ), + V( 63, 0x008f, 61, 32, 0 ), + V( 64, 0x5b12, 65, 65, 1 ), + V( 65, 0x4d04, 80, 66, 0 ), + V( 66, 0x412c, 81, 67, 0 ), + V( 67, 0x37d8, 82, 68, 0 ), + V( 68, 0x2fe8, 83, 69, 0 ), + V( 69, 0x293c, 84, 70, 0 ), + V( 70, 0x2379, 86, 71, 0 ), + V( 71, 0x1edf, 87, 72, 0 ), + V( 72, 0x1aa9, 87, 73, 0 ), + V( 73, 0x174e, 72, 74, 0 ), + V( 74, 0x1424, 72, 75, 0 ), + V( 75, 0x119c, 74, 76, 0 ), + V( 76, 0x0f6b, 74, 77, 0 ), + V( 77, 0x0d51, 75, 78, 0 ), + V( 78, 0x0bb6, 77, 79, 0 ), + V( 79, 0x0a40, 77, 48, 0 ), + V( 80, 0x5832, 80, 81, 1 ), + V( 81, 0x4d1c, 88, 82, 0 ), + V( 82, 0x438e, 89, 83, 0 ), + V( 83, 0x3bdd, 90, 84, 0 ), + V( 84, 0x34ee, 91, 85, 0 ), + V( 85, 0x2eae, 92, 86, 0 ), + V( 86, 0x299a, 93, 87, 0 ), + V( 87, 0x2516, 86, 71, 0 ), + V( 88, 0x5570, 88, 89, 1 ), + V( 89, 0x4ca9, 95, 90, 0 ), + V( 90, 0x44d9, 96, 91, 0 ), + V( 91, 0x3e22, 97, 92, 0 ), + V( 92, 0x3824, 99, 93, 0 ), + V( 93, 0x32b4, 99, 94, 0 ), + V( 94, 0x2e17, 93, 86, 0 ), + V( 95, 0x56a8, 95, 96, 1 ), + V( 96, 0x4f46, 101, 97, 0 ), + V( 97, 0x47e5, 102, 98, 0 ), + V( 98, 0x41cf, 103, 99, 0 ), + V( 99, 0x3c3d, 104, 100, 0 ), + V( 100, 0x375e, 99, 93, 0 ), + V( 101, 0x5231, 105, 102, 0 ), + V( 102, 0x4c0f, 106, 103, 0 ), + V( 103, 0x4639, 107, 104, 0 ), + V( 104, 0x415e, 103, 99, 0 ), + V( 105, 0x5627, 105, 106, 1 ), + V( 106, 0x50e7, 108, 107, 0 ), + V( 107, 0x4b85, 109, 103, 0 ), + V( 108, 0x5597, 110, 109, 0 ), + V( 109, 0x504f, 111, 107, 0 ), + V( 110, 0x5a10, 110, 111, 1 ), + V( 111, 0x5522, 112, 109, 0 ), + V( 112, 0x59eb, 112, 111, 1 ), +/* + * This last entry is used for fixed probability estimate of 0.5 + * as recommended in Section 10.3 Table 5 of ITU-T Rec. T.851. + */ + V( 113, 0x5a1d, 113, 113, 0 ) +}; diff --git a/lib/jpeg/src/jaricom.d b/lib/jpeg/src/jaricom.d new file mode 100644 index 0000000..f746b67 --- /dev/null +++ b/lib/jpeg/src/jaricom.d @@ -0,0 +1,14 @@ +jaricom.o: jaricom.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcapimin.c b/lib/jpeg/src/jcapimin.c new file mode 100644 index 0000000..3382d91 --- /dev/null +++ b/lib/jpeg/src/jcapimin.c @@ -0,0 +1,288 @@ +/* + * jcapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * Modified 2003-2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-compression case or the transcoding-only + * case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jcapistd.c. But also see jcparam.c for + * parameter-setup helper routines, jcomapi.c for routines shared by + * compression and decompression, and jctrans.c for the transcoding case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG compression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_compress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = FALSE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->dest = NULL; + + cinfo->comp_info = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + cinfo->quant_tbl_ptrs[i] = NULL; + cinfo->q_scale_factor[i] = 100; + } + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Must do it here for emit_dqt in case jpeg_write_tables is used */ + cinfo->block_size = DCTSIZE; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + + cinfo->script_space = NULL; + + cinfo->input_gamma = 1.0; /* in case application forgets */ + + /* OK, I'm ready */ + cinfo->global_state = CSTATE_START; +} + + +/* + * Destruction of a JPEG compression object + */ + +GLOBAL(void) +jpeg_destroy_compress (j_compress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG compression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_compress (j_compress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Forcibly suppress or un-suppress all quantization and Huffman tables. + * Marks all currently defined tables as already written (if suppress) + * or not written (if !suppress). This will control whether they get emitted + * by a subsequent jpeg_start_compress call. + * + * This routine is exported for use by applications that want to produce + * abbreviated JPEG datastreams. It logically belongs in jcparam.c, but + * since it is called by jpeg_start_compress, we put it here --- otherwise + * jcparam.o would be linked whether the application used it or not. + */ + +GLOBAL(void) +jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) +{ + int i; + JQUANT_TBL * qtbl; + JHUFF_TBL * htbl; + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) + qtbl->sent_table = suppress; + } + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + } +} + + +/* + * Finish JPEG compression. + * + * If a multipass operating mode was selected, this may do a great deal of + * work including most of the actual output. + */ + +GLOBAL(void) +jpeg_finish_compress (j_compress_ptr cinfo) +{ + JDIMENSION iMCU_row; + + if (cinfo->global_state == CSTATE_SCANNING || + cinfo->global_state == CSTATE_RAW_OK) { + /* Terminate first pass */ + if (cinfo->next_scanline < cinfo->image_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_pass) (cinfo); + } else if (cinfo->global_state != CSTATE_WRCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any remaining passes */ + while (! cinfo->master->is_last_pass) { + (*cinfo->master->prepare_for_pass) (cinfo); + for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) { + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) iMCU_row; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* We bypass the main controller and invoke coef controller directly; + * all work is being done from the coefficient buffer. + */ + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + (*cinfo->master->finish_pass) (cinfo); + } + /* Write EOI, do final cleanup */ + (*cinfo->marker->write_file_trailer) (cinfo); + (*cinfo->dest->term_destination) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); +} + + +/* + * Write a special marker. + * This is only recommended for writing COM or APPn markers. + * Must be called after jpeg_start_compress() and before + * first call to jpeg_write_scanlines() or jpeg_write_raw_data(). + */ + +GLOBAL(void) +jpeg_write_marker (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen) +{ + JMETHOD(void, write_marker_byte, (j_compress_ptr info, int val)); + + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); + write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ + while (datalen--) { + (*write_marker_byte) (cinfo, *dataptr); + dataptr++; + } +} + +/* Same, but piecemeal. */ + +GLOBAL(void) +jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +{ + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); +} + +GLOBAL(void) +jpeg_write_m_byte (j_compress_ptr cinfo, int val) +{ + (*cinfo->marker->write_marker_byte) (cinfo, val); +} + + +/* + * Alternate compression function: just write an abbreviated table file. + * Before calling this, all parameters and a data destination must be set up. + * + * To produce a pair of files containing abbreviated tables and abbreviated + * image data, one would proceed as follows: + * + * initialize JPEG object + * set JPEG parameters + * set destination to table file + * jpeg_write_tables(cinfo); + * set destination to image file + * jpeg_start_compress(cinfo, FALSE); + * write data... + * jpeg_finish_compress(cinfo); + * + * jpeg_write_tables has the side effect of marking all tables written + * (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress + * will not re-emit the tables unless it is passed write_all_tables=TRUE. + */ + +GLOBAL(void) +jpeg_write_tables (j_compress_ptr cinfo) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Initialize the marker writer ... bit of a crock to do it here. */ + jinit_marker_writer(cinfo); + /* Write them tables! */ + (*cinfo->marker->write_tables_only) (cinfo); + /* And clean up. */ + (*cinfo->dest->term_destination) (cinfo); + /* + * In library releases up through v6a, we called jpeg_abort() here to free + * any working memory allocated by the destination manager and marker + * writer. Some applications had a problem with that: they allocated space + * of their own from the library memory manager, and didn't want it to go + * away during write_tables. So now we do nothing. This will cause a + * memory leak if an app calls write_tables repeatedly without doing a full + * compression cycle or otherwise resetting the JPEG object. However, that + * seems less bad than unexpectedly freeing memory in the normal case. + * An app that prefers the old behavior can call jpeg_abort for itself after + * each call to jpeg_write_tables(). + */ +} diff --git a/lib/jpeg/src/jcapimin.d b/lib/jpeg/src/jcapimin.d new file mode 100644 index 0000000..e70b298 --- /dev/null +++ b/lib/jpeg/src/jcapimin.d @@ -0,0 +1,14 @@ +jcapimin.o: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcapistd.c b/lib/jpeg/src/jcapistd.c new file mode 100644 index 0000000..fed66ca --- /dev/null +++ b/lib/jpeg/src/jcapistd.c @@ -0,0 +1,161 @@ +/* + * jcapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-compression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_compress, it will end up linking in the entire compressor. + * We thus must separate this file from jcapimin.c to avoid linking the + * whole compression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Compression initialization. + * Before calling this, all parameters and a data destination must be set up. + * + * We require a write_all_tables parameter as a failsafe check when writing + * multiple datastreams from the same compression object. Since prior runs + * will have left all the tables marked sent_table=TRUE, a subsequent run + * would emit an abbreviated stream (no tables) by default. This may be what + * is wanted, but for safety's sake it should not be the default behavior: + * programmers should have to make a deliberate choice to emit abbreviated + * images. Therefore the documentation and examples should encourage people + * to pass write_all_tables=TRUE; then it will take active thought to do the + * wrong thing. + */ + +GLOBAL(void) +jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (write_all_tables) + jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + jinit_compress_master(cinfo); + /* Set up for the first pass */ + (*cinfo->master->prepare_for_pass) (cinfo); + /* Ready for application to drive first pass through jpeg_write_scanlines + * or jpeg_write_raw_data. + */ + cinfo->next_scanline = 0; + cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); +} + + +/* + * Write some scanlines of data to the JPEG compressor. + * + * The return value will be the number of lines actually written. + * This should be less than the supplied num_lines only in case that + * the data destination module has requested suspension of the compressor, + * or if more than image_height scanlines are passed in. + * + * Note: we warn about excess calls to jpeg_write_scanlines() since + * this likely signals an application programmer error. However, + * excess scanlines passed in the last valid call are *silently* ignored, + * so that the application need not adjust num_lines for end-of-image + * when using a multiple-scanline buffer. + */ + +GLOBAL(JDIMENSION) +jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION num_lines) +{ + JDIMENSION row_ctr, rows_left; + + if (cinfo->global_state != CSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_scanlines. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_scanlines. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Ignore any extra scanlines at bottom of image. */ + rows_left = cinfo->image_height - cinfo->next_scanline; + if (num_lines > rows_left) + num_lines = rows_left; + + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + cinfo->next_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to write raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != CSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_raw_data. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_raw_data. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Verify that at least one iMCU row has been passed. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; + if (num_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Directly compress the row. */ + if (! (*cinfo->coef->compress_data) (cinfo, data)) { + /* If compressor did not consume the whole row, suspend processing. */ + return 0; + } + + /* OK, we processed one iMCU row. */ + cinfo->next_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} diff --git a/lib/jpeg/src/jcapistd.d b/lib/jpeg/src/jcapistd.d new file mode 100644 index 0000000..9a5be18 --- /dev/null +++ b/lib/jpeg/src/jcapistd.d @@ -0,0 +1,14 @@ +jcapistd.o: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcarith.c b/lib/jpeg/src/jcarith.c new file mode 100644 index 0000000..69afce5 --- /dev/null +++ b/lib/jpeg/src/jcarith.c @@ -0,0 +1,934 @@ +/* + * jcarith.c + * + * Developed 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains portable arithmetic entropy encoding routines for JPEG + * (implementing the ISO/IEC IS 10918-1 and CCITT Recommendation ITU-T T.81). + * + * Both sequential and progressive modes are supported in this single module. + * + * Suspension is not currently supported in this module. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Expanded entropy encoder object for arithmetic encoding. */ + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + INT32 c; /* C register, base of coding interval, layout as in sec. D.1.3 */ + INT32 a; /* A register, normalized size of coding interval */ + INT32 sc; /* counter for stacked 0xFF values which might overflow */ + INT32 zc; /* counter for pending 0x00 output values which might * + * be discarded at the end ("Pacman" termination) */ + int ct; /* bit shift counter, determines when next byte will be written */ + int buffer; /* buffer for most recent output byte != 0xFF */ + + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + int dc_context[MAX_COMPS_IN_SCAN]; /* context index for DC conditioning */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to statistics areas (these workspaces have image lifespan) */ + unsigned char * dc_stats[NUM_ARITH_TBLS]; + unsigned char * ac_stats[NUM_ARITH_TBLS]; + + /* Statistics bin for coding with fixed probability 0.5 */ + unsigned char fixed_bin[4]; +} arith_entropy_encoder; + +typedef arith_entropy_encoder * arith_entropy_ptr; + +/* The following two definitions specify the allocation chunk size + * for the statistics area. + * According to sections F.1.4.4.1.3 and F.1.4.4.2, we need at least + * 49 statistics bins for DC, and 245 statistics bins for AC coding. + * + * We use a compact representation with 1 byte per statistics bin, + * thus the numbers directly represent byte sizes. + * This 1 byte per statistics bin contains the meaning of the MPS + * (more probable symbol) in the highest bit (mask 0x80), and the + * index into the probability estimation state machine table + * in the lower bits (mask 0x7F). + */ + +#define DC_STAT_BINS 64 +#define AC_STAT_BINS 256 + +/* NOTE: Uncomment the following #define if you want to use the + * given formula for calculating the AC conditioning parameter Kx + * for spectral selection progressive coding in section G.1.3.2 + * of the spec (Kx = Kmin + SRL (8 + Se - Kmin) 4). + * Although the spec and P&M authors claim that this "has proven + * to give good results for 8 bit precision samples", I'm not + * convinced yet that this is really beneficial. + * Early tests gave only very marginal compression enhancements + * (a few - around 5 or so - bytes even for very large files), + * which would turn out rather negative if we'd suppress the + * DAC (Define Arithmetic Conditioning) marker segments for + * the default parameters in the future. + * Note that currently the marker writing module emits 12-byte + * DAC segments for a full-component scan in a color image. + * This is not worth worrying about IMHO. However, since the + * spec defines the default values to be used if the tables + * are omitted (unlike Huffman tables, which are required + * anyway), one might optimize this behaviour in the future, + * and then it would be disadvantageous to use custom tables if + * they don't provide sufficient gain to exceed the DAC size. + * + * On the other hand, I'd consider it as a reasonable result + * that the conditioning has no significant influence on the + * compression performance. This means that the basic + * statistical model is already rather stable. + * + * Thus, at the moment, we use the default conditioning values + * anyway, and do not use the custom formula. + * +#define CALCULATE_SPECTRAL_CONDITIONING + */ + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + * We assume that int right shift is unsigned if INT32 right shift is, + * which should be safe. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS int ishift_temp; +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +LOCAL(void) +emit_byte (int val, j_compress_ptr cinfo) +/* Write next output byte; we do not support suspension in this module. */ +{ + struct jpeg_destination_mgr * dest = cinfo->dest; + + *dest->next_output_byte++ = (JOCTET) val; + if (--dest->free_in_buffer == 0) + if (! (*dest->empty_output_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); +} + + +/* + * Finish up at the end of an arithmetic-compressed scan. + */ + +METHODDEF(void) +finish_pass (j_compress_ptr cinfo) +{ + arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; + INT32 temp; + + /* Section D.1.8: Termination of encoding */ + + /* Find the e->c in the coding interval with the largest + * number of trailing zero bits */ + if ((temp = (e->a - 1 + e->c) & 0xFFFF0000L) < e->c) + e->c = temp + 0x8000L; + else + e->c = temp; + /* Send remaining bytes to output */ + e->c <<= e->ct; + if (e->c & 0xF8000000L) { + /* One final overflow has to be handled */ + if (e->buffer >= 0) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + emit_byte(e->buffer + 1, cinfo); + if (e->buffer + 1 == 0xFF) + emit_byte(0x00, cinfo); + } + e->zc += e->sc; /* carry-over converts stacked 0xFF bytes to 0x00 */ + e->sc = 0; + } else { + if (e->buffer == 0) + ++e->zc; + else if (e->buffer >= 0) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + emit_byte(e->buffer, cinfo); + } + if (e->sc) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + do { + emit_byte(0xFF, cinfo); + emit_byte(0x00, cinfo); + } while (--e->sc); + } + } + /* Output final bytes only if they are not 0x00 */ + if (e->c & 0x7FFF800L) { + if (e->zc) /* output final pending zero bytes */ + do emit_byte(0x00, cinfo); + while (--e->zc); + emit_byte((e->c >> 19) & 0xFF, cinfo); + if (((e->c >> 19) & 0xFF) == 0xFF) + emit_byte(0x00, cinfo); + if (e->c & 0x7F800L) { + emit_byte((e->c >> 11) & 0xFF, cinfo); + if (((e->c >> 11) & 0xFF) == 0xFF) + emit_byte(0x00, cinfo); + } + } +} + + +/* + * The core arithmetic encoding routine (common in JPEG and JBIG). + * This needs to go as fast as possible. + * Machine-dependent optimization facilities + * are not utilized in this portable implementation. + * However, this code should be fairly efficient and + * may be a good base for further optimizations anyway. + * + * Parameter 'val' to be encoded may be 0 or 1 (binary decision). + * + * Note: I've added full "Pacman" termination support to the + * byte output routines, which is equivalent to the optional + * Discard_final_zeros procedure (Figure D.15) in the spec. + * Thus, we always produce the shortest possible output + * stream compliant to the spec (no trailing zero bytes, + * except for FF stuffing). + * + * I've also introduced a new scheme for accessing + * the probability estimation state machine table, + * derived from Markus Kuhn's JBIG implementation. + */ + +LOCAL(void) +arith_encode (j_compress_ptr cinfo, unsigned char *st, int val) +{ + register arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; + register unsigned char nl, nm; + register INT32 qe, temp; + register int sv; + + /* Fetch values from our compact representation of Table D.2: + * Qe values and probability estimation state machine + */ + sv = *st; + qe = jpeg_aritab[sv & 0x7F]; /* => Qe_Value */ + nl = qe & 0xFF; qe >>= 8; /* Next_Index_LPS + Switch_MPS */ + nm = qe & 0xFF; qe >>= 8; /* Next_Index_MPS */ + + /* Encode & estimation procedures per sections D.1.4 & D.1.5 */ + e->a -= qe; + if (val != (sv >> 7)) { + /* Encode the less probable symbol */ + if (e->a >= qe) { + /* If the interval size (qe) for the less probable symbol (LPS) + * is larger than the interval size for the MPS, then exchange + * the two symbols for coding efficiency, otherwise code the LPS + * as usual: */ + e->c += e->a; + e->a = qe; + } + *st = (sv & 0x80) ^ nl; /* Estimate_after_LPS */ + } else { + /* Encode the more probable symbol */ + if (e->a >= 0x8000L) + return; /* A >= 0x8000 -> ready, no renormalization required */ + if (e->a < qe) { + /* If the interval size (qe) for the less probable symbol (LPS) + * is larger than the interval size for the MPS, then exchange + * the two symbols for coding efficiency: */ + e->c += e->a; + e->a = qe; + } + *st = (sv & 0x80) ^ nm; /* Estimate_after_MPS */ + } + + /* Renormalization & data output per section D.1.6 */ + do { + e->a <<= 1; + e->c <<= 1; + if (--e->ct == 0) { + /* Another byte is ready for output */ + temp = e->c >> 19; + if (temp > 0xFF) { + /* Handle overflow over all stacked 0xFF bytes */ + if (e->buffer >= 0) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + emit_byte(e->buffer + 1, cinfo); + if (e->buffer + 1 == 0xFF) + emit_byte(0x00, cinfo); + } + e->zc += e->sc; /* carry-over converts stacked 0xFF bytes to 0x00 */ + e->sc = 0; + /* Note: The 3 spacer bits in the C register guarantee + * that the new buffer byte can't be 0xFF here + * (see page 160 in the P&M JPEG book). */ + e->buffer = temp & 0xFF; /* new output byte, might overflow later */ + } else if (temp == 0xFF) { + ++e->sc; /* stack 0xFF byte (which might overflow later) */ + } else { + /* Output all stacked 0xFF bytes, they will not overflow any more */ + if (e->buffer == 0) + ++e->zc; + else if (e->buffer >= 0) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + emit_byte(e->buffer, cinfo); + } + if (e->sc) { + if (e->zc) + do emit_byte(0x00, cinfo); + while (--e->zc); + do { + emit_byte(0xFF, cinfo); + emit_byte(0x00, cinfo); + } while (--e->sc); + } + e->buffer = temp & 0xFF; /* new output byte (can still overflow) */ + } + e->c &= 0x7FFFFL; + e->ct += 8; + } + } while (e->a < 0x8000L); +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(void) +emit_restart (j_compress_ptr cinfo, int restart_num) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + int ci; + jpeg_component_info * compptr; + + finish_pass(cinfo); + + emit_byte(0xFF, cinfo); + emit_byte(JPEG_RST0 + restart_num, cinfo); + + /* Re-initialize statistics areas */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) { + MEMZERO(entropy->dc_stats[compptr->dc_tbl_no], DC_STAT_BINS); + /* Reset DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + entropy->dc_context[ci] = 0; + } + /* AC needs no table when not present */ + if (cinfo->Se) { + MEMZERO(entropy->ac_stats[compptr->ac_tbl_no], AC_STAT_BINS); + } + } + + /* Reset arithmetic encoding variables */ + entropy->c = 0; + entropy->a = 0x10000L; + entropy->sc = 0; + entropy->zc = 0; + entropy->ct = 11; + entropy->buffer = -1; /* empty */ +} + + +/* + * MCU encoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + unsigned char *st; + int blkn, ci, tbl; + int v, v2, m; + ISHIFT_TEMPS + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + emit_restart(cinfo, entropy->next_restart_num); + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + tbl = cinfo->cur_comp_info[ci]->dc_tbl_no; + + /* Compute the DC value after the required point transform by Al. + * This is simply an arithmetic right shift. + */ + m = IRIGHT_SHIFT((int) ((*block)[0]), cinfo->Al); + + /* Sections F.1.4.1 & F.1.4.4.1: Encoding of DC coefficients */ + + /* Table F.4: Point to statistics bin S0 for DC coefficient coding */ + st = entropy->dc_stats[tbl] + entropy->dc_context[ci]; + + /* Figure F.4: Encode_DC_DIFF */ + if ((v = m - entropy->last_dc_val[ci]) == 0) { + arith_encode(cinfo, st, 0); + entropy->dc_context[ci] = 0; /* zero diff category */ + } else { + entropy->last_dc_val[ci] = m; + arith_encode(cinfo, st, 1); + /* Figure F.6: Encoding nonzero value v */ + /* Figure F.7: Encoding the sign of v */ + if (v > 0) { + arith_encode(cinfo, st + 1, 0); /* Table F.4: SS = S0 + 1 */ + st += 2; /* Table F.4: SP = S0 + 2 */ + entropy->dc_context[ci] = 4; /* small positive diff category */ + } else { + v = -v; + arith_encode(cinfo, st + 1, 1); /* Table F.4: SS = S0 + 1 */ + st += 3; /* Table F.4: SN = S0 + 3 */ + entropy->dc_context[ci] = 8; /* small negative diff category */ + } + /* Figure F.8: Encoding the magnitude category of v */ + m = 0; + if (v -= 1) { + arith_encode(cinfo, st, 1); + m = 1; + v2 = v; + st = entropy->dc_stats[tbl] + 20; /* Table F.4: X1 = 20 */ + while (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st += 1; + } + } + arith_encode(cinfo, st, 0); + /* Section F.1.4.4.1.2: Establish dc_context conditioning category */ + if (m < (int) ((1L << cinfo->arith_dc_L[tbl]) >> 1)) + entropy->dc_context[ci] = 0; /* zero diff category */ + else if (m > (int) ((1L << cinfo->arith_dc_U[tbl]) >> 1)) + entropy->dc_context[ci] += 8; /* large diff category */ + /* Figure F.9: Encoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + arith_encode(cinfo, st, (m & v) ? 1 : 0); + } + } + + return TRUE; +} + + +/* + * MCU encoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + unsigned char *st; + int tbl, k, ke; + int v, v2, m; + const int * natural_order; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + emit_restart(cinfo, entropy->next_restart_num); + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + natural_order = cinfo->natural_order; + + /* Encode the MCU data block */ + block = MCU_data[0]; + tbl = cinfo->cur_comp_info[0]->ac_tbl_no; + + /* Sections F.1.4.2 & F.1.4.4.2: Encoding of AC coefficients */ + + /* Establish EOB (end-of-block) index */ + for (ke = cinfo->Se; ke > 0; ke--) + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if ((v = (*block)[natural_order[ke]]) >= 0) { + if (v >>= cinfo->Al) break; + } else { + v = -v; + if (v >>= cinfo->Al) break; + } + + /* Figure F.5: Encode_AC_Coefficients */ + for (k = cinfo->Ss; k <= ke; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + arith_encode(cinfo, st, 0); /* EOB decision */ + for (;;) { + if ((v = (*block)[natural_order[k]]) >= 0) { + if (v >>= cinfo->Al) { + arith_encode(cinfo, st + 1, 1); + arith_encode(cinfo, entropy->fixed_bin, 0); + break; + } + } else { + v = -v; + if (v >>= cinfo->Al) { + arith_encode(cinfo, st + 1, 1); + arith_encode(cinfo, entropy->fixed_bin, 1); + break; + } + } + arith_encode(cinfo, st + 1, 0); st += 3; k++; + } + st += 2; + /* Figure F.8: Encoding the magnitude category of v */ + m = 0; + if (v -= 1) { + arith_encode(cinfo, st, 1); + m = 1; + v2 = v; + if (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st = entropy->ac_stats[tbl] + + (k <= cinfo->arith_ac_K[tbl] ? 189 : 217); + while (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st += 1; + } + } + } + arith_encode(cinfo, st, 0); + /* Figure F.9: Encoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + arith_encode(cinfo, st, (m & v) ? 1 : 0); + } + /* Encode EOB decision only if k <= cinfo->Se */ + if (k <= cinfo->Se) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + arith_encode(cinfo, st, 1); + } + + return TRUE; +} + + +/* + * MCU encoding for DC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + unsigned char *st; + int Al, blkn; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + emit_restart(cinfo, entropy->next_restart_num); + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + st = entropy->fixed_bin; /* use fixed probability estimation */ + Al = cinfo->Al; + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + /* We simply emit the Al'th bit of the DC coefficient value. */ + arith_encode(cinfo, st, (MCU_data[blkn][0][0] >> Al) & 1); + } + + return TRUE; +} + + +/* + * MCU encoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + unsigned char *st; + int tbl, k, ke, kex; + int v; + const int * natural_order; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + emit_restart(cinfo, entropy->next_restart_num); + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + natural_order = cinfo->natural_order; + + /* Encode the MCU data block */ + block = MCU_data[0]; + tbl = cinfo->cur_comp_info[0]->ac_tbl_no; + + /* Section G.1.3.3: Encoding of AC coefficients */ + + /* Establish EOB (end-of-block) index */ + for (ke = cinfo->Se; ke > 0; ke--) + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if ((v = (*block)[natural_order[ke]]) >= 0) { + if (v >>= cinfo->Al) break; + } else { + v = -v; + if (v >>= cinfo->Al) break; + } + + /* Establish EOBx (previous stage end-of-block) index */ + for (kex = ke; kex > 0; kex--) + if ((v = (*block)[natural_order[kex]]) >= 0) { + if (v >>= cinfo->Ah) break; + } else { + v = -v; + if (v >>= cinfo->Ah) break; + } + + /* Figure G.10: Encode_AC_Coefficients_SA */ + for (k = cinfo->Ss; k <= ke; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + if (k > kex) + arith_encode(cinfo, st, 0); /* EOB decision */ + for (;;) { + if ((v = (*block)[natural_order[k]]) >= 0) { + if (v >>= cinfo->Al) { + if (v >> 1) /* previously nonzero coef */ + arith_encode(cinfo, st + 2, (v & 1)); + else { /* newly nonzero coef */ + arith_encode(cinfo, st + 1, 1); + arith_encode(cinfo, entropy->fixed_bin, 0); + } + break; + } + } else { + v = -v; + if (v >>= cinfo->Al) { + if (v >> 1) /* previously nonzero coef */ + arith_encode(cinfo, st + 2, (v & 1)); + else { /* newly nonzero coef */ + arith_encode(cinfo, st + 1, 1); + arith_encode(cinfo, entropy->fixed_bin, 1); + } + break; + } + } + arith_encode(cinfo, st + 1, 0); st += 3; k++; + } + } + /* Encode EOB decision only if k <= cinfo->Se */ + if (k <= cinfo->Se) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + arith_encode(cinfo, st, 1); + } + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of arithmetic-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + jpeg_component_info * compptr; + JBLOCKROW block; + unsigned char *st; + int blkn, ci, tbl, k, ke; + int v, v2, m; + const int * natural_order; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + emit_restart(cinfo, entropy->next_restart_num); + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + natural_order = cinfo->natural_order; + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Sections F.1.4.1 & F.1.4.4.1: Encoding of DC coefficients */ + + tbl = compptr->dc_tbl_no; + + /* Table F.4: Point to statistics bin S0 for DC coefficient coding */ + st = entropy->dc_stats[tbl] + entropy->dc_context[ci]; + + /* Figure F.4: Encode_DC_DIFF */ + if ((v = (*block)[0] - entropy->last_dc_val[ci]) == 0) { + arith_encode(cinfo, st, 0); + entropy->dc_context[ci] = 0; /* zero diff category */ + } else { + entropy->last_dc_val[ci] = (*block)[0]; + arith_encode(cinfo, st, 1); + /* Figure F.6: Encoding nonzero value v */ + /* Figure F.7: Encoding the sign of v */ + if (v > 0) { + arith_encode(cinfo, st + 1, 0); /* Table F.4: SS = S0 + 1 */ + st += 2; /* Table F.4: SP = S0 + 2 */ + entropy->dc_context[ci] = 4; /* small positive diff category */ + } else { + v = -v; + arith_encode(cinfo, st + 1, 1); /* Table F.4: SS = S0 + 1 */ + st += 3; /* Table F.4: SN = S0 + 3 */ + entropy->dc_context[ci] = 8; /* small negative diff category */ + } + /* Figure F.8: Encoding the magnitude category of v */ + m = 0; + if (v -= 1) { + arith_encode(cinfo, st, 1); + m = 1; + v2 = v; + st = entropy->dc_stats[tbl] + 20; /* Table F.4: X1 = 20 */ + while (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st += 1; + } + } + arith_encode(cinfo, st, 0); + /* Section F.1.4.4.1.2: Establish dc_context conditioning category */ + if (m < (int) ((1L << cinfo->arith_dc_L[tbl]) >> 1)) + entropy->dc_context[ci] = 0; /* zero diff category */ + else if (m > (int) ((1L << cinfo->arith_dc_U[tbl]) >> 1)) + entropy->dc_context[ci] += 8; /* large diff category */ + /* Figure F.9: Encoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + arith_encode(cinfo, st, (m & v) ? 1 : 0); + } + + /* Sections F.1.4.2 & F.1.4.4.2: Encoding of AC coefficients */ + + tbl = compptr->ac_tbl_no; + + /* Establish EOB (end-of-block) index */ + for (ke = cinfo->lim_Se; ke > 0; ke--) + if ((*block)[natural_order[ke]]) break; + + /* Figure F.5: Encode_AC_Coefficients */ + for (k = 1; k <= ke; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + arith_encode(cinfo, st, 0); /* EOB decision */ + while ((v = (*block)[natural_order[k]]) == 0) { + arith_encode(cinfo, st + 1, 0); st += 3; k++; + } + arith_encode(cinfo, st + 1, 1); + /* Figure F.6: Encoding nonzero value v */ + /* Figure F.7: Encoding the sign of v */ + if (v > 0) { + arith_encode(cinfo, entropy->fixed_bin, 0); + } else { + v = -v; + arith_encode(cinfo, entropy->fixed_bin, 1); + } + st += 2; + /* Figure F.8: Encoding the magnitude category of v */ + m = 0; + if (v -= 1) { + arith_encode(cinfo, st, 1); + m = 1; + v2 = v; + if (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st = entropy->ac_stats[tbl] + + (k <= cinfo->arith_ac_K[tbl] ? 189 : 217); + while (v2 >>= 1) { + arith_encode(cinfo, st, 1); + m <<= 1; + st += 1; + } + } + } + arith_encode(cinfo, st, 0); + /* Figure F.9: Encoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + arith_encode(cinfo, st, (m & v) ? 1 : 0); + } + /* Encode EOB decision only if k <= cinfo->lim_Se */ + if (k <= cinfo->lim_Se) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + arith_encode(cinfo, st, 1); + } + } + + return TRUE; +} + + +/* + * Initialize for an arithmetic-compressed scan. + */ + +METHODDEF(void) +start_pass (j_compress_ptr cinfo, boolean gather_statistics) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + int ci, tbl; + jpeg_component_info * compptr; + + if (gather_statistics) + /* Make sure to avoid that in the master control logic! + * We are fully adaptive here and need no extra + * statistics gathering pass! + */ + ERREXIT(cinfo, JERR_NOT_COMPILED); + + /* We assume jcmaster.c already validated the progressive scan parameters. */ + + /* Select execution routines */ + if (cinfo->progressive_mode) { + if (cinfo->Ah == 0) { + if (cinfo->Ss == 0) + entropy->pub.encode_mcu = encode_mcu_DC_first; + else + entropy->pub.encode_mcu = encode_mcu_AC_first; + } else { + if (cinfo->Ss == 0) + entropy->pub.encode_mcu = encode_mcu_DC_refine; + else + entropy->pub.encode_mcu = encode_mcu_AC_refine; + } + } else + entropy->pub.encode_mcu = encode_mcu; + + /* Allocate & initialize requested statistics areas */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) { + tbl = compptr->dc_tbl_no; + if (tbl < 0 || tbl >= NUM_ARITH_TBLS) + ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); + if (entropy->dc_stats[tbl] == NULL) + entropy->dc_stats[tbl] = (unsigned char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, DC_STAT_BINS); + MEMZERO(entropy->dc_stats[tbl], DC_STAT_BINS); + /* Initialize DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + entropy->dc_context[ci] = 0; + } + /* AC needs no table when not present */ + if (cinfo->Se) { + tbl = compptr->ac_tbl_no; + if (tbl < 0 || tbl >= NUM_ARITH_TBLS) + ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); + if (entropy->ac_stats[tbl] == NULL) + entropy->ac_stats[tbl] = (unsigned char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, AC_STAT_BINS); + MEMZERO(entropy->ac_stats[tbl], AC_STAT_BINS); +#ifdef CALCULATE_SPECTRAL_CONDITIONING + if (cinfo->progressive_mode) + /* Section G.1.3.2: Set appropriate arithmetic conditioning value Kx */ + cinfo->arith_ac_K[tbl] = cinfo->Ss + ((8 + cinfo->Se - cinfo->Ss) >> 4); +#endif + } + } + + /* Initialize arithmetic encoding variables */ + entropy->c = 0; + entropy->a = 0x10000L; + entropy->sc = 0; + entropy->zc = 0; + entropy->ct = 11; + entropy->buffer = -1; /* empty */ + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* + * Module initialization routine for arithmetic entropy encoding. + */ + +GLOBAL(void) +jinit_arith_encoder (j_compress_ptr cinfo) +{ + arith_entropy_ptr entropy; + int i; + + entropy = (arith_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(arith_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass; + entropy->pub.finish_pass = finish_pass; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + entropy->dc_stats[i] = NULL; + entropy->ac_stats[i] = NULL; + } + + /* Initialize index for fixed probability estimation */ + entropy->fixed_bin[0] = 113; +} diff --git a/lib/jpeg/src/jcarith.d b/lib/jpeg/src/jcarith.d new file mode 100644 index 0000000..01e1444 --- /dev/null +++ b/lib/jpeg/src/jcarith.d @@ -0,0 +1,14 @@ +jcarith.o: jcarith.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jccoefct.c b/lib/jpeg/src/jccoefct.c new file mode 100644 index 0000000..1e02619 --- /dev/null +++ b/lib/jpeg/src/jccoefct.c @@ -0,0 +1,453 @@ +/* + * jccoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for compression. + * This controller is the top level of the JPEG compressor proper. + * The coefficient buffer lies between forward-DCT and entropy encoding steps. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* We use a full-image coefficient buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the DCT + * step is run during the first pass, and subsequent passes need only read + * the buffered coefficients. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#endif +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* For single-pass compression, it's sufficient to buffer just one MCU + * (although this may prove a bit slow in practice). We allocate a + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays. + */ + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +/* Forward declarations */ +METHODDEF(boolean) compress_data + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#ifdef FULL_COEF_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +METHODDEF(boolean) compress_output + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (coef->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_data; + break; +#ifdef FULL_COEF_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, bi, ci, yindex, yoffset, blockcnt; + JDIMENSION ypos, xpos; + jpeg_component_info *compptr; + forward_DCT_ptr forward_DCT; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Determine where data comes from in input_buf and do the DCT thing. + * Each call on forward_DCT processes a horizontal row of DCT blocks + * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks + * sequentially. Dummy blocks at the right or bottom edge are filled in + * specially. The data in them does not matter for image reconstruction, + * so we fill them with values that will encode to the smallest amount of + * data, viz: all zeroes in the AC entries, DC entries equal to previous + * block's DC value. (Thanks to Thomas Kinsman for this idea.) + */ + blkn = 0; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + forward_DCT = cinfo->fdct->forward_DCT[compptr->component_index]; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + xpos = MCU_col_num * compptr->MCU_sample_width; + ypos = yoffset * compptr->DCT_v_scaled_size; + /* ypos == (yoffset+yindex) * DCTSIZE */ + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + (*forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); + if (blockcnt < compptr->MCU_width) { + /* Create some dummy blocks at the right edge of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], + (compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK)); + for (bi = blockcnt; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0]; + } + } + } else { + /* Create a row of dummy blocks at the bottom of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn], + compptr->MCU_width * SIZEOF(JBLOCK)); + for (bi = 0; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0]; + } + } + blkn += compptr->MCU_width; + ypos += compptr->DCT_v_scaled_size; + } + } + /* Try to write the MCU. In event of a suspension failure, we will + * re-DCT the MCU on restart (a bit inefficient, could be fixed...) + */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +#ifdef FULL_COEF_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * This amount of data is read from the source buffer, DCT'd and quantized, + * and saved into the virtual arrays. We also generate suitable dummy blocks + * as needed at the right and lower edges. (The dummy blocks are constructed + * in the virtual arrays, which have been padded appropriately.) This makes + * it possible for subsequent passes not to worry about real vs. dummy blocks. + * + * We must also emit the data to the entropy encoder. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All + * components are DCT'd and loaded into the virtual arrays in this pass. + * However, it may be that only a subset of the components are emitted to + * the entropy encoder during this first pass; be careful about looking + * at the scan-dependent variables (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION blocks_across, MCUs_across, MCUindex; + int bi, ci, h_samp_factor, block_row, block_rows, ndummy; + JCOEF lastDC; + jpeg_component_info *compptr; + JBLOCKARRAY buffer; + JBLOCKROW thisblockrow, lastblockrow; + forward_DCT_ptr forward_DCT; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (coef->iMCU_row_num < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + blocks_across = compptr->width_in_blocks; + h_samp_factor = compptr->h_samp_factor; + /* Count number of dummy blocks to be added at the right margin. */ + ndummy = (int) (blocks_across % h_samp_factor); + if (ndummy > 0) + ndummy = h_samp_factor - ndummy; + forward_DCT = cinfo->fdct->forward_DCT[ci]; + /* Perform DCT for all non-dummy blocks in this iMCU row. Each call + * on forward_DCT processes a complete horizontal row of DCT blocks. + */ + for (block_row = 0; block_row < block_rows; block_row++) { + thisblockrow = buffer[block_row]; + (*forward_DCT) (cinfo, compptr, input_buf[ci], thisblockrow, + (JDIMENSION) (block_row * compptr->DCT_v_scaled_size), + (JDIMENSION) 0, blocks_across); + if (ndummy > 0) { + /* Create dummy blocks at the right edge of the image. */ + thisblockrow += blocks_across; /* => first dummy block */ + jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK)); + lastDC = thisblockrow[-1][0]; + for (bi = 0; bi < ndummy; bi++) { + thisblockrow[bi][0] = lastDC; + } + } + } + /* If at end of image, create dummy block rows as needed. + * The tricky part here is that within each MCU, we want the DC values + * of the dummy blocks to match the last real block's DC value. + * This squeezes a few more bytes out of the resulting file... + */ + if (coef->iMCU_row_num == last_iMCU_row) { + blocks_across += ndummy; /* include lower right corner */ + MCUs_across = blocks_across / h_samp_factor; + for (block_row = block_rows; block_row < compptr->v_samp_factor; + block_row++) { + thisblockrow = buffer[block_row]; + lastblockrow = buffer[block_row-1]; + jzero_far((void FAR *) thisblockrow, + (size_t) (blocks_across * SIZEOF(JBLOCK))); + for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { + lastDC = lastblockrow[h_samp_factor-1][0]; + for (bi = 0; bi < h_samp_factor; bi++) { + thisblockrow[bi][0] = lastDC; + } + thisblockrow += h_samp_factor; /* advance to next MCU in row */ + lastblockrow += h_samp_factor; + } + } + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the entropy encoder, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + +#endif /* FULL_COEF_BUFFER_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef FULL_COEF_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + int ci; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} diff --git a/lib/jpeg/src/jccoefct.d b/lib/jpeg/src/jccoefct.d new file mode 100644 index 0000000..093a0af --- /dev/null +++ b/lib/jpeg/src/jccoefct.d @@ -0,0 +1,14 @@ +jccoefct.o: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jccolor.c b/lib/jpeg/src/jccolor.c new file mode 100644 index 0000000..2663724 --- /dev/null +++ b/lib/jpeg/src/jccolor.c @@ -0,0 +1,459 @@ +/* + * jccolor.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_converter pub; /* public fields */ + + /* Private state for RGB->YCC conversion */ + INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ +} my_color_converter; + +typedef my_color_converter * my_cconvert_ptr; + + +/**************** RGB -> YCbCr conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, + * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) + * were not represented exactly. Now we sacrifice exact representation of + * maximum red and maximum blue in order to get exact grayscales. + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times R,G,B for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + * in the tables to save adding them separately in the inner loop. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS) +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L< Y section */ +#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ +#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ +#define R_CB_OFF (3*(MAXJSAMPLE+1)) +#define G_CB_OFF (4*(MAXJSAMPLE+1)) +#define B_CB_OFF (5*(MAXJSAMPLE+1)) +#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ +#define G_CR_OFF (6*(MAXJSAMPLE+1)) +#define B_CR_OFF (7*(MAXJSAMPLE+1)) +#define TABLE_SIZE (8*(MAXJSAMPLE+1)) + + +/* + * Initialize for RGB->YCC colorspace conversion. + */ + +METHODDEF(void) +rgb_ycc_start (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + INT32 * rgb_ycc_tab; + INT32 i; + + /* Allocate and fill in the conversion tables. */ + cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (TABLE_SIZE * SIZEOF(INT32))); + + for (i = 0; i <= MAXJSAMPLE; i++) { + rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i; + rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i; + rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; + rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i; + rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i; + /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. + * This ensures that the maximum output will round to MAXJSAMPLE + * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + */ + rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +/* B=>Cb and R=>Cr tables are the same + rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +*/ + rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i; + rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i; + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * + * Note that we change from the application's interleaved-pixel format + * to our internal noninterleaved, one-plane-per-component format. + * The input buffer is therefore three times as wide as the output buffer. + * + * A starting row offset is provided only for the output buffer. The caller + * can easily adjust the passed input_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +rgb_ycc_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/**************** Cases other than RGB -> YCbCr **************/ + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles RGB->grayscale conversion, which is the same + * as the RGB->Y portion of RGB->YCbCr. + * We assume rgb_ycc_start has been called (we only use the Y tables). + */ + +METHODDEF(void) +rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* Y */ + outptr[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles Adobe-style CMYK->YCCK conversion, + * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same + * conversion as above, while passing K (black) unchanged. + * We assume rgb_ycc_start has been called. + */ + +METHODDEF(void) +cmyk_ycck_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2, outptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + outptr3 = output_buf[3][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = MAXJSAMPLE - GETJSAMPLE(inptr[0]); + g = MAXJSAMPLE - GETJSAMPLE(inptr[1]); + b = MAXJSAMPLE - GETJSAMPLE(inptr[2]); + /* K passes through as-is */ + outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */ + inptr += 4; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles grayscale output with no conversion. + * The source can be either plain grayscale or YCbCr (since Y == gray). + */ + +METHODDEF(void) +grayscale_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + int instride = cinfo->input_components; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */ + inptr += instride; + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles multi-component colorspaces without conversion. + * We assume input_components == num_components. + */ + +METHODDEF(void) +null_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + register int ci; + int nc = cinfo->num_components; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + /* It seems fastest to make a separate pass for each component. */ + for (ci = 0; ci < nc; ci++) { + inptr = *input_buf; + outptr = output_buf[ci][output_row]; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */ + inptr += nc; + } + } + input_buf++; + output_row++; + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +null_method (j_compress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for input colorspace conversion. + */ + +GLOBAL(void) +jinit_color_converter (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_converter)); + cinfo->cconvert = (struct jpeg_color_converter *) cconvert; + /* set start_pass to null method until we find out differently */ + cconvert->pub.start_pass = null_method; + + /* Make sure input_components agrees with in_color_space */ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + if (cinfo->input_components != 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + if (cinfo->input_components != RGB_PIXELSIZE) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; +#endif /* else share code with YCbCr */ + + case JCS_YCbCr: + if (cinfo->input_components != 3) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->input_components != 4) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->input_components < 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + } + + /* Check num_components, set conversion method based on requested space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_GRAYSCALE) + cconvert->pub.color_convert = grayscale_convert; + else if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_gray_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = grayscale_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_ycc_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = cmyk_ycck_convert; + } else if (cinfo->in_color_space == JCS_YCCK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: /* allow null conversion of JCS_UNKNOWN */ + if (cinfo->jpeg_color_space != cinfo->in_color_space || + cinfo->num_components != cinfo->input_components) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + cconvert->pub.color_convert = null_convert; + break; + } +} diff --git a/lib/jpeg/src/jccolor.d b/lib/jpeg/src/jccolor.d new file mode 100644 index 0000000..d251cd0 --- /dev/null +++ b/lib/jpeg/src/jccolor.d @@ -0,0 +1,14 @@ +jccolor.o: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcdctmgr.c b/lib/jpeg/src/jcdctmgr.c new file mode 100644 index 0000000..550b1a6 --- /dev/null +++ b/lib/jpeg/src/jcdctmgr.c @@ -0,0 +1,482 @@ +/* + * jcdctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the forward-DCT management logic. + * This code selects a particular DCT implementation to be used, + * and it performs related housekeeping chores including coefficient + * quantization. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* Pointer to the DCT routine actually in use */ + forward_DCT_method_ptr do_dct[MAX_COMPONENTS]; + + /* The actual post-DCT divisors --- not identical to the quant table + * entries, because of scaling (especially for an unnormalized DCT). + * Each table is given in normal array order. + */ + DCTELEM * divisors[NUM_QUANT_TBLS]; + +#ifdef DCT_FLOAT_SUPPORTED + /* Same as above for the floating-point case. */ + float_DCT_method_ptr do_float_dct[MAX_COMPONENTS]; + FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; +#endif +} my_fdct_controller; + +typedef my_fdct_controller * my_fdct_ptr; + + +/* The current scaled-DCT routines require ISLOW-style divisor tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef DCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Perform forward DCT on one or more blocks of a component. + * + * The input samples are taken from the sample_data[] array starting at + * position start_row/start_col, and moving to the right for any additional + * blocks. The quantized coefficients are returned in coef_blocks[]. + */ + +METHODDEF(void) +forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for integer DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + forward_DCT_method_ptr do_dct = fdct->do_dct[compptr->component_index]; + DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; + DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += compptr->DCT_h_scaled_size) { + /* Perform the DCT */ + (*do_dct) (workspace, sample_data, start_col); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register DCTELEM temp, qval; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + qval = divisors[i]; + temp = workspace[i]; + /* Divide the coefficient value by qval, ensuring proper rounding. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * + * In most files, at least half of the output values will be zero + * (at default quantization settings, more like three-quarters...) + * so we should ensure that this case is fast. On many machines, + * a comparison is enough cheaper than a divide to make a special test + * a win. Since both inputs will be nonnegative, we need only test + * for a < b to discover whether a/b is 0. + * If your machine's division is fast enough, define FAST_DIVIDE. + */ +#ifdef FAST_DIVIDE +#define DIVIDE_BY(a,b) a /= b +#else +#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0 +#endif + if (temp < 0) { + temp = -temp; + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + temp = -temp; + } else { + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + } + output_ptr[i] = (JCOEF) temp; + } + } + } +} + + +#ifdef DCT_FLOAT_SUPPORTED + +METHODDEF(void) +forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for floating-point DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + float_DCT_method_ptr do_dct = fdct->do_float_dct[compptr->component_index]; + FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; + FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += compptr->DCT_h_scaled_size) { + /* Perform the DCT */ + (*do_dct) (workspace, sample_data, start_col); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register FAST_FLOAT temp; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + /* Apply the quantization and scaling factor */ + temp = workspace[i] * divisors[i]; + /* Round to nearest integer. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * The maximum coefficient size is +-16K (for 12-bit data), so this + * code should work for either 16-bit or 32-bit ints. + */ + output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384); + } + } + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ + + +/* + * Initialize for a processing pass. + * Verify that all referenced Q-tables are present, and set up + * the divisor table for each one. + * In the current implementation, DCT of all components is done during + * the first pass, even if only some components will be output in the + * first scan. Hence all components should be examined here. + */ + +METHODDEF(void) +start_pass_fdctmgr (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + int ci, qtblno, i; + jpeg_component_info *compptr; + int method = 0; + JQUANT_TBL * qtbl; + DCTELEM * dtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper DCT routine for this component's scaling */ + switch ((compptr->DCT_h_scaled_size << 8) + compptr->DCT_v_scaled_size) { +#ifdef DCT_SCALING_SUPPORTED + case ((1 << 8) + 1): + fdct->do_dct[ci] = jpeg_fdct_1x1; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((2 << 8) + 2): + fdct->do_dct[ci] = jpeg_fdct_2x2; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((3 << 8) + 3): + fdct->do_dct[ci] = jpeg_fdct_3x3; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((4 << 8) + 4): + fdct->do_dct[ci] = jpeg_fdct_4x4; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((5 << 8) + 5): + fdct->do_dct[ci] = jpeg_fdct_5x5; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((6 << 8) + 6): + fdct->do_dct[ci] = jpeg_fdct_6x6; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((7 << 8) + 7): + fdct->do_dct[ci] = jpeg_fdct_7x7; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((9 << 8) + 9): + fdct->do_dct[ci] = jpeg_fdct_9x9; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((10 << 8) + 10): + fdct->do_dct[ci] = jpeg_fdct_10x10; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((11 << 8) + 11): + fdct->do_dct[ci] = jpeg_fdct_11x11; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((12 << 8) + 12): + fdct->do_dct[ci] = jpeg_fdct_12x12; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((13 << 8) + 13): + fdct->do_dct[ci] = jpeg_fdct_13x13; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((14 << 8) + 14): + fdct->do_dct[ci] = jpeg_fdct_14x14; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((15 << 8) + 15): + fdct->do_dct[ci] = jpeg_fdct_15x15; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((16 << 8) + 16): + fdct->do_dct[ci] = jpeg_fdct_16x16; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((16 << 8) + 8): + fdct->do_dct[ci] = jpeg_fdct_16x8; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((14 << 8) + 7): + fdct->do_dct[ci] = jpeg_fdct_14x7; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((12 << 8) + 6): + fdct->do_dct[ci] = jpeg_fdct_12x6; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((10 << 8) + 5): + fdct->do_dct[ci] = jpeg_fdct_10x5; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((8 << 8) + 4): + fdct->do_dct[ci] = jpeg_fdct_8x4; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((6 << 8) + 3): + fdct->do_dct[ci] = jpeg_fdct_6x3; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((4 << 8) + 2): + fdct->do_dct[ci] = jpeg_fdct_4x2; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((2 << 8) + 1): + fdct->do_dct[ci] = jpeg_fdct_2x1; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((8 << 8) + 16): + fdct->do_dct[ci] = jpeg_fdct_8x16; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((7 << 8) + 14): + fdct->do_dct[ci] = jpeg_fdct_7x14; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((6 << 8) + 12): + fdct->do_dct[ci] = jpeg_fdct_6x12; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((5 << 8) + 10): + fdct->do_dct[ci] = jpeg_fdct_5x10; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((4 << 8) + 8): + fdct->do_dct[ci] = jpeg_fdct_4x8; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((3 << 8) + 6): + fdct->do_dct[ci] = jpeg_fdct_3x6; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((2 << 8) + 4): + fdct->do_dct[ci] = jpeg_fdct_2x4; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; + case ((1 << 8) + 2): + fdct->do_dct[ci] = jpeg_fdct_1x2; + method = JDCT_ISLOW; /* jfdctint uses islow-style table */ + break; +#endif + case ((DCTSIZE << 8) + DCTSIZE): + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + fdct->do_dct[ci] = jpeg_fdct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + fdct->do_dct[ci] = jpeg_fdct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + fdct->do_float_dct[ci] = jpeg_fdct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT2(cinfo, JERR_BAD_DCTSIZE, + compptr->DCT_h_scaled_size, compptr->DCT_v_scaled_size); + break; + } + qtblno = compptr->quant_tbl_no; + /* Make sure specified quantization table is present */ + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + qtbl = cinfo->quant_tbl_ptrs[qtblno]; + /* Compute divisors for this quant table */ + /* We may do this more than once for same table, but it's not a big deal */ + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + /* For LL&M IDCT method, divisors are equal to raw quantization + * coefficients multiplied by 8 (to counteract scaling). + */ + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = ((DCTELEM) qtbl->quantval[i]) << 3; + } + fdct->pub.forward_DCT[ci] = forward_DCT; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + */ +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = (DCTELEM) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-3); + } + } + fdct->pub.forward_DCT[ci] = forward_DCT; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + FAST_FLOAT * fdtbl; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + if (fdct->float_divisors[qtblno] == NULL) { + fdct->float_divisors[qtblno] = (FAST_FLOAT *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(FAST_FLOAT)); + } + fdtbl = fdct->float_divisors[qtblno]; + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fdtbl[i] = (FAST_FLOAT) + (1.0 / (((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + i++; + } + } + } + fdct->pub.forward_DCT[ci] = forward_DCT_float; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize FDCT manager. + */ + +GLOBAL(void) +jinit_forward_dct (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct; + int i; + + fdct = (my_fdct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; + + /* Mark divisor tables unallocated */ + for (i = 0; i < NUM_QUANT_TBLS; i++) { + fdct->divisors[i] = NULL; +#ifdef DCT_FLOAT_SUPPORTED + fdct->float_divisors[i] = NULL; +#endif + } +} diff --git a/lib/jpeg/src/jcdctmgr.d b/lib/jpeg/src/jcdctmgr.d new file mode 100644 index 0000000..70f3720 --- /dev/null +++ b/lib/jpeg/src/jcdctmgr.d @@ -0,0 +1,16 @@ +jcdctmgr.o: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jchuff.c b/lib/jpeg/src/jchuff.c new file mode 100644 index 0000000..4cbab43 --- /dev/null +++ b/lib/jpeg/src/jchuff.c @@ -0,0 +1,1576 @@ +/* + * jchuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2006-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy encoding routines. + * Both sequential and progressive modes are supported in this single module. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. + * + * We do not support output suspension for the progressive JPEG mode, since + * the library currently does not allow multiple-scan files to be written + * with output suspension. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* The legal range of a DCT coefficient is + * -1024 .. +1023 for 8-bit data; + * -16384 .. +16383 for 12-bit data. + * Hence the magnitude should always fit in 10 or 14 bits respectively. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MAX_COEF_BITS 10 +#else +#define MAX_COEF_BITS 14 +#endif + +/* Derived data constructed for each Huffman table */ + +typedef struct { + unsigned int ehufco[256]; /* code for each symbol */ + char ehufsi[256]; /* length of code for each symbol */ + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ +} c_derived_tbl; + + +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; + + /* Following fields used only in progressive mode */ + + /* Mode flag: TRUE for optimization, FALSE for actual data output */ + boolean gather_statistics; + + /* next_output_byte/free_in_buffer are local copies of cinfo->dest fields. + */ + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ + + /* Coding status for AC components */ + int ac_tbl_no; /* the table number of the single component */ + unsigned int EOBRUN; /* run length of EOBs */ + unsigned int BE; /* # of buffered correction bits before MCU */ + char * bit_buffer; /* buffer for correction bits (1 per char) */ + /* packing correction bits tightly would save some space but cost time... */ +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU (sequential mode). + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + +/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit + * buffer can hold. Larger sizes may slightly improve compression, but + * 1000 is already well into the realm of overkill. + * The minimum safe size is 64 bits. + */ + +#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + * We assume that int right shift is unsigned if INT32 right shift is, + * which should be safe. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS int ishift_temp; +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + */ + +LOCAL(void) +jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + c_derived_tbl *dtbl; + int p, i, l, lastp, si, maxsymbol; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (c_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(c_derived_tbl)); + dtbl = *pdtbl; + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + lastp = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure C.3: generate encoding tables */ + /* These are code and size indexed by symbol value */ + + /* Set all codeless symbols to have code length 0; + * this lets us detect duplicate VAL entries here, and later + * allows emit_bits to detect any attempt to emit such symbols. + */ + MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi)); + + /* This is also a convenient place to check for out-of-range + * and duplicated VAL entries. We allow 0..255 for AC symbols + * but only 0..15 for DC. (We could constrain them further + * based on data depth and mode, but this seems enough.) + */ + maxsymbol = isDC ? 15 : 255; + + for (p = 0; p < lastp; p++) { + i = htbl->huffval[p]; + if (i < 0 || i > maxsymbol || dtbl->ehufsi[i]) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + dtbl->ehufco[i] = huffcode[p]; + dtbl->ehufsi[i] = huffsize[p]; + } +} + + +/* Outputting bytes to the file. + * NB: these must be called only when actually outputting, + * that is, entropy->gather_statistics == FALSE. + */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte_s(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer_s(state)) \ + { action; } } + +/* Emit a byte */ +#define emit_byte_e(entropy,val) \ + { *(entropy)->next_output_byte++ = (JOCTET) (val); \ + if (--(entropy)->free_in_buffer == 0) \ + dump_buffer_e(entropy); } + + +LOCAL(boolean) +dump_buffer_s (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +LOCAL(void) +dump_buffer_e (huff_entropy_ptr entropy) +/* Empty the output buffer; we do not support suspension in this case. */ +{ + struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + + if (! (*dest->empty_output_buffer) (entropy->cinfo)) + ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); + /* After a successful buffer dump, must reset buffer pointers */ + entropy->next_output_byte = dest->next_output_byte; + entropy->free_in_buffer = dest->free_in_buffer; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits_s (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte_s(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte_s(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +INLINE +LOCAL(void) +emit_bits_e (huff_entropy_ptr entropy, unsigned int code, int size) +/* Emit some bits, unless we are in gather mode */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = entropy->saved.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + if (entropy->gather_statistics) + return; /* do nothing if we're only getting stats */ + + put_buffer &= (((INT32) 1)<saved.put_buffer; + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte_e(entropy, c); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte_e(entropy, 0); + } + put_buffer <<= 8; + put_bits -= 8; + } + + entropy->saved.put_buffer = put_buffer; /* update variables */ + entropy->saved.put_bits = put_bits; +} + + +LOCAL(boolean) +flush_bits_s (working_state * state) +{ + if (! emit_bits_s(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +LOCAL(void) +flush_bits_e (huff_entropy_ptr entropy) +{ + emit_bits_e(entropy, 0x7F, 7); /* fill any partial byte with ones */ + entropy->saved.put_buffer = 0; /* and reset bit-buffer to empty */ + entropy->saved.put_bits = 0; +} + + +/* + * Emit (or just count) a Huffman symbol. + */ + +INLINE +LOCAL(void) +emit_dc_symbol (huff_entropy_ptr entropy, int tbl_no, int symbol) +{ + if (entropy->gather_statistics) + entropy->dc_count_ptrs[tbl_no][symbol]++; + else { + c_derived_tbl * tbl = entropy->dc_derived_tbls[tbl_no]; + emit_bits_e(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + } +} + + +INLINE +LOCAL(void) +emit_ac_symbol (huff_entropy_ptr entropy, int tbl_no, int symbol) +{ + if (entropy->gather_statistics) + entropy->ac_count_ptrs[tbl_no][symbol]++; + else { + c_derived_tbl * tbl = entropy->ac_derived_tbls[tbl_no]; + emit_bits_e(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + } +} + + +/* + * Emit bits from a correction bit buffer. + */ + +LOCAL(void) +emit_buffered_bits (huff_entropy_ptr entropy, char * bufstart, + unsigned int nbits) +{ + if (entropy->gather_statistics) + return; /* no real work */ + + while (nbits > 0) { + emit_bits_e(entropy, (unsigned int) (*bufstart), 1); + bufstart++; + nbits--; + } +} + + +/* + * Emit any pending EOBRUN symbol. + */ + +LOCAL(void) +emit_eobrun (huff_entropy_ptr entropy) +{ + register int temp, nbits; + + if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */ + temp = entropy->EOBRUN; + nbits = 0; + while ((temp >>= 1)) + nbits++; + /* safety check: shouldn't happen given limited correction-bit buffer */ + if (nbits > 14) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + emit_ac_symbol(entropy, entropy->ac_tbl_no, nbits << 4); + if (nbits) + emit_bits_e(entropy, entropy->EOBRUN, nbits); + + entropy->EOBRUN = 0; + + /* Emit any buffered correction bits */ + emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE); + entropy->BE = 0; + } +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart_s (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits_s(state)) + return FALSE; + + emit_byte_s(state, 0xFF, return FALSE); + emit_byte_s(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +LOCAL(void) +emit_restart_e (huff_entropy_ptr entropy, int restart_num) +{ + int ci; + + emit_eobrun(entropy); + + if (! entropy->gather_statistics) { + flush_bits_e(entropy); + emit_byte_e(entropy, 0xFF); + emit_byte_e(entropy, JPEG_RST0 + restart_num); + } + + if (entropy->cinfo->Ss == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + } else { + /* Re-initialize all AC-related fields to 0 */ + entropy->EOBRUN = 0; + entropy->BE = 0; + } +} + + +/* + * MCU encoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + int blkn, ci; + int Al = cinfo->Al; + JBLOCKROW block; + jpeg_component_info * compptr; + ISHIFT_TEMPS + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_e(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Compute the DC value after the required point transform by Al. + * This is simply an arithmetic right shift. + */ + temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al); + + /* DC differences are figured on the point-transformed values. */ + temp = temp2 - entropy->saved.last_dc_val[ci]; + entropy->saved.last_dc_val[ci] = temp2; + + /* Encode the DC coefficient difference per section G.1.2.1 */ + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit the Huffman-coded symbol for the number of bits */ + emit_dc_symbol(entropy, compptr->dc_tbl_no, nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + emit_bits_e(entropy, (unsigned int) temp2, nbits); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + register int r, k; + int Se, Al; + const int * natural_order; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_e(entropy, entropy->next_restart_num); + + Se = cinfo->Se; + Al = cinfo->Al; + natural_order = cinfo->natural_order; + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ + + r = 0; /* r = run length of zeros */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = (*block)[natural_order[k]]) == 0) { + r++; + continue; + } + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value; so the code is + * interwoven with finding the abs value (temp) and output bits (temp2). + */ + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ + temp2 = ~temp; + } else { + temp >>= Al; /* apply the point transform */ + temp2 = temp; + } + /* Watch out for case that nonzero coef is zero after point transform */ + if (temp == 0) { + r++; + continue; + } + + /* Emit any pending EOBRUN */ + if (entropy->EOBRUN > 0) + emit_eobrun(entropy); + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + emit_ac_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_ac_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + emit_bits_e(entropy, (unsigned int) temp2, nbits); + + r = 0; /* reset zero run length */ + } + + if (r > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + if (entropy->EOBRUN == 0x7FFF) + emit_eobrun(entropy); /* force it out to avoid overflow */ + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int temp; + int blkn; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_e(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* We simply emit the Al'th bit of the DC coefficient value. */ + temp = (*block)[0]; + emit_bits_e(entropy, (unsigned int) (temp >> Al), 1); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int temp; + register int r, k; + int EOB; + char *BR_buffer; + unsigned int BR; + int Se, Al; + const int * natural_order; + JBLOCKROW block; + int absvalues[DCTSIZE2]; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart_e(entropy, entropy->next_restart_num); + + Se = cinfo->Se; + Al = cinfo->Al; + natural_order = cinfo->natural_order; + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* It is convenient to make a pre-pass to determine the transformed + * coefficients' absolute values and the EOB position. + */ + EOB = 0; + for (k = cinfo->Ss; k <= Se; k++) { + temp = (*block)[natural_order[k]]; + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if (temp < 0) + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + absvalues[k] = temp; /* save abs value for main pass */ + if (temp == 1) + EOB = k; /* EOB = index of last newly-nonzero coef */ + } + + /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ + + r = 0; /* r = run length of zeros */ + BR = 0; /* BR = count of buffered bits added now */ + BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = absvalues[k]) == 0) { + r++; + continue; + } + + /* Emit any required ZRLs, but not if they can be folded into EOB */ + while (r > 15 && k <= EOB) { + /* emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + /* Emit ZRL */ + emit_ac_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + /* Emit buffered correction bits that must be associated with ZRL */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + } + + /* If the coef was previously nonzero, it only needs a correction bit. + * NOTE: a straight translation of the spec's figure G.7 would suggest + * that we also need to test r > 15. But if r > 15, we can only get here + * if k > EOB, which implies that this coefficient is not 1. + */ + if (temp > 1) { + /* The correction bit is the next bit of the absolute value. */ + BR_buffer[BR++] = (char) (temp & 1); + continue; + } + + /* Emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_ac_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1); + + /* Emit output bit for newly-nonzero coef */ + temp = ((*block)[natural_order[k]] < 0) ? 0 : 1; + emit_bits_e(entropy, (unsigned int) temp, 1); + + /* Emit buffered correction bits that must be associated with this code */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + r = 0; /* reset zero run length */ + } + + if (r > 0 || BR > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + entropy->BE += BR; /* concat my correction bits to older ones */ + /* We force out the EOB if we risk either: + * 1. overflow of the EOB counter; + * 2. overflow of the correction bit buffer during the next MCU. + */ + if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1)) + emit_eobrun(entropy); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + int Se = state->cinfo->lim_Se; + const int * natural_order = state->cinfo->natural_order; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits_s(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits_s(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k <= Se; k++) { + if ((temp = block[natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits_s(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits_s(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits_s(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits_s(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart_s(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + if (cinfo->progressive_mode) { + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Flush out any buffered data */ + emit_eobrun(entropy); + flush_bits_e(entropy); + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + } else { + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits_s(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + } +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + int Se = cinfo->lim_Se; + const int * natural_order = cinfo->natural_order; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k <= Se; k++) { + if ((temp = block[natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + + +/* + * Generate the best Huffman code table for the given counts, fill htbl. + * + * The JPEG standard requires that no symbol be assigned a codeword of all + * one bits (so that padding bits added at the end of a compressed segment + * can't look like a valid code). Because of the canonical ordering of + * codewords, this just means that there must be an unused slot in the + * longest codeword length category. Section K.2 of the JPEG spec suggests + * reserving such a slot by pretending that symbol 256 is a valid symbol + * with count 1. In theory that's not optimal; giving it count zero but + * including it in the symbol set anyway should give a better Huffman code. + * But the theoretically better code actually seems to come out worse in + * practice, because it produces more all-ones bytes (which incur stuffed + * zero bytes in the final file). In any case the difference is tiny. + * + * The JPEG standard requires Huffman codes to be no more than 16 bits long. + * If some symbols have a very small but nonzero probability, the Huffman tree + * must be adjusted to meet the code length restriction. We currently use + * the adjustment method suggested in JPEG section K.2. This method is *not* + * optimal; it may not choose the best possible limited-length code. But + * typically only very-low-frequency symbols will be given less-than-optimal + * lengths, so the code is almost optimal. Experimental comparisons against + * an optimal limited-length-code algorithm indicate that the difference is + * microscopic --- usually less than a hundredth of a percent of total size. + * So the extra complexity of an optimal algorithm doesn't seem worthwhile. + */ + +LOCAL(void) +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) +{ +#define MAX_CLEN 32 /* assumed maximum initial code length */ + UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ + int codesize[257]; /* codesize[k] = code length of symbol k */ + int others[257]; /* next symbol in current branch of tree */ + int c1, c2; + int p, i, j; + long v; + + /* This algorithm is explained in section K.2 of the JPEG standard */ + + MEMZERO(bits, SIZEOF(bits)); + MEMZERO(codesize, SIZEOF(codesize)); + for (i = 0; i < 257; i++) + others[i] = -1; /* init links to empty */ + + freq[256] = 1; /* make sure 256 has a nonzero count */ + /* Including the pseudo-symbol 256 in the Huffman procedure guarantees + * that no real symbol is given code-value of all ones, because 256 + * will be placed last in the largest codeword category. + */ + + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ + + for (;;) { + /* Find the smallest nonzero frequency, set c1 = its symbol */ + /* In case of ties, take the larger symbol number */ + c1 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v) { + v = freq[i]; + c1 = i; + } + } + + /* Find the next smallest nonzero frequency, set c2 = its symbol */ + /* In case of ties, take the larger symbol number */ + c2 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v && i != c1) { + v = freq[i]; + c2 = i; + } + } + + /* Done if we've merged everything into one frequency */ + if (c2 < 0) + break; + + /* Else merge the two counts/trees */ + freq[c1] += freq[c2]; + freq[c2] = 0; + + /* Increment the codesize of everything in c1's tree branch */ + codesize[c1]++; + while (others[c1] >= 0) { + c1 = others[c1]; + codesize[c1]++; + } + + others[c1] = c2; /* chain c2 onto c1's tree branch */ + + /* Increment the codesize of everything in c2's tree branch */ + codesize[c2]++; + while (others[c2] >= 0) { + c2 = others[c2]; + codesize[c2]++; + } + } + + /* Now count the number of symbols of each code length */ + for (i = 0; i <= 256; i++) { + if (codesize[i]) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[i] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[i]]++; + } + } + + /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + * Huffman procedure assigned any such lengths, we must adjust the coding. + * Here is what the JPEG spec says about how this next bit works: + * Since symbols are paired for the longest Huffman code, the symbols are + * removed from this length category two at a time. The prefix for the pair + * (which is one bit shorter) is allocated to one of the pair; then, + * skipping the BITS entry for that prefix length, a code word from the next + * shortest nonzero BITS entry is converted into a prefix for two code words + * one bit longer. + */ + + for (i = MAX_CLEN; i > 16; i--) { + while (bits[i] > 0) { + j = i - 2; /* find length of new prefix to be used */ + while (bits[j] == 0) + j--; + + bits[i] -= 2; /* remove two symbols */ + bits[i-1]++; /* one goes in this length */ + bits[j+1] += 2; /* two new symbols in this length */ + bits[j]--; /* symbol of this length is now a prefix */ + } + } + + /* Remove the count for the pseudo-symbol 256 from the largest codelength */ + while (bits[i] == 0) /* find largest codelength still in use */ + i--; + bits[i]--; + + /* Return final symbol counts (only for lengths 0..16) */ + MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits)); + + /* Return a list of the symbols sorted by code length */ + /* It's not real clear to me why we don't need to consider the codelength + * changes made above, but the JPEG spec seems to think this works. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + for (j = 0; j <= 255; j++) { + if (codesize[j] == i) { + htbl->huffval[p] = (UINT8) j; + p++; + } + } + } + + /* Set sent_table FALSE so updated table will be written to JPEG file. */ + htbl->sent_table = FALSE; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, tbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + if (cinfo->progressive_mode) + /* Flush out buffered data (all we care about is counting the EOB symbol) */ + emit_eobrun(entropy); + + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) { + tbl = compptr->dc_tbl_no; + if (! did_dc[tbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[tbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[tbl]); + did_dc[tbl] = TRUE; + } + } + /* AC needs no table when not present */ + if (cinfo->Se) { + tbl = compptr->ac_tbl_no; + if (! did_ac[tbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[tbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[tbl]); + did_ac[tbl] = TRUE; + } + } + } +} + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, tbl; + jpeg_component_info * compptr; + + if (gather_statistics) + entropy->pub.finish_pass = finish_pass_gather; + else + entropy->pub.finish_pass = finish_pass_huff; + + if (cinfo->progressive_mode) { + entropy->cinfo = cinfo; + entropy->gather_statistics = gather_statistics; + + /* We assume jcmaster.c already validated the scan parameters. */ + + /* Select execution routine */ + if (cinfo->Ah == 0) { + if (cinfo->Ss == 0) + entropy->pub.encode_mcu = encode_mcu_DC_first; + else + entropy->pub.encode_mcu = encode_mcu_AC_first; + } else { + if (cinfo->Ss == 0) + entropy->pub.encode_mcu = encode_mcu_DC_refine; + else { + entropy->pub.encode_mcu = encode_mcu_AC_refine; + /* AC refinement needs a correction bit buffer */ + if (entropy->bit_buffer == NULL) + entropy->bit_buffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + MAX_CORR_BITS * SIZEOF(char)); + } + } + + /* Initialize AC stuff */ + entropy->ac_tbl_no = cinfo->cur_comp_info[0]->ac_tbl_no; + entropy->EOBRUN = 0; + entropy->BE = 0; + } else { + if (gather_statistics) + entropy->pub.encode_mcu = encode_mcu_gather; + else + entropy->pub.encode_mcu = encode_mcu_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) { + tbl = compptr->dc_tbl_no; + if (gather_statistics) { + /* Check for invalid table index */ + /* (make_c_derived_tbl does this in the other path) */ + if (tbl < 0 || tbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[tbl] == NULL) + entropy->dc_count_ptrs[tbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[tbl], 257 * SIZEOF(long)); + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, tbl, + & entropy->dc_derived_tbls[tbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + /* AC needs no table when not present */ + if (cinfo->Se) { + tbl = compptr->ac_tbl_no; + if (gather_statistics) { + if (tbl < 0 || tbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + if (entropy->ac_count_ptrs[tbl] == NULL) + entropy->ac_count_ptrs[tbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[tbl], 257 * SIZEOF(long)); + } else { + jpeg_make_c_derived_tbl(cinfo, FALSE, tbl, + & entropy->ac_derived_tbls[tbl]); + } + } + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; + } + + if (cinfo->progressive_mode) + entropy->bit_buffer = NULL; /* needed only in AC refinement scan */ +} diff --git a/lib/jpeg/src/jchuff.d b/lib/jpeg/src/jchuff.d new file mode 100644 index 0000000..326c894 --- /dev/null +++ b/lib/jpeg/src/jchuff.d @@ -0,0 +1,14 @@ +jchuff.o: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcinit.c b/lib/jpeg/src/jcinit.c new file mode 100644 index 0000000..f7aa89f --- /dev/null +++ b/lib/jpeg/src/jcinit.c @@ -0,0 +1,65 @@ +/* + * jcinit.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains initialization logic for the JPEG compressor. + * This routine is in charge of selecting the modules to be executed and + * making an initialization call to each one. + * + * Logically, this code belongs in jcmaster.c. It's split out because + * linking this routine implies linking the entire compression library. + * For a transcoding-only application, we want to be able to use jcmaster.c + * without linking in the whole library. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Master selection of compression modules. + * This is done once at the start of processing an image. We determine + * which modules will be used and give them appropriate initialization calls. + */ + +GLOBAL(void) +jinit_compress_master (j_compress_ptr cinfo) +{ + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, FALSE /* full compression */); + + /* Preprocessing */ + if (! cinfo->raw_data_in) { + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + } + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) + jinit_arith_encoder(cinfo); + else { + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} diff --git a/lib/jpeg/src/jcinit.d b/lib/jpeg/src/jcinit.d new file mode 100644 index 0000000..e8d0127 --- /dev/null +++ b/lib/jpeg/src/jcinit.d @@ -0,0 +1,14 @@ +jcinit.o: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcmainct.c b/lib/jpeg/src/jcmainct.c new file mode 100644 index 0000000..669b7bb --- /dev/null +++ b/lib/jpeg/src/jcmainct.c @@ -0,0 +1,293 @@ +/* + * jcmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for compression. + * The main buffer lies between the pre-processor and the JPEG + * compressor proper; it holds downsampled data in the JPEG colorspace. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Note: currently, there is no operating mode in which a full-image buffer + * is needed at this step. If there were, that mode could not be used with + * "raw data" input, since this module is bypassed in that case. However, + * we've left the code here for possible use in special applications. + */ +#undef FULL_MAIN_BUFFER_SUPPORTED + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_main_controller pub; /* public fields */ + + JDIMENSION cur_iMCU_row; /* number of current iMCU row */ + JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */ + boolean suspended; /* remember if we suspended output */ + J_BUF_MODE pass_mode; /* current operating mode */ + + /* If using just a strip buffer, this points to the entire set of buffers + * (we allocate one for each component). In the full-image case, this + * points to the currently accessible strips of the virtual arrays. + */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* If using full-image storage, this array holds pointers to virtual-array + * control blocks for each component. Unused if not full-image storage. + */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#ifdef FULL_MAIN_BUFFER_SUPPORTED +METHODDEF(void) process_data_buffer_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Do nothing in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + main->cur_iMCU_row = 0; /* initialize counters */ + main->rowgroup_ctr = 0; + main->suspended = FALSE; + main->pass_mode = pass_mode; /* save mode for use by process_data */ + + switch (pass_mode) { + case JBUF_PASS_THRU: +#ifdef FULL_MAIN_BUFFER_SUPPORTED + if (main->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + main->pub.process_data = process_data_simple_main; + break; +#ifdef FULL_MAIN_BUFFER_SUPPORTED + case JBUF_SAVE_SOURCE: + case JBUF_CRANK_DEST: + case JBUF_SAVE_AND_PASS: + if (main->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + main->pub.process_data = process_data_buffer_main; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This routine handles the simple pass-through mode, + * where we have only a strip buffer. + */ + +METHODDEF(void) +process_data_simple_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Read input data if we haven't filled the main buffer yet */ + if (main->rowgroup_ctr < (JDIMENSION) cinfo->min_DCT_v_scaled_size) + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main->buffer, &main->rowgroup_ctr, + (JDIMENSION) cinfo->min_DCT_v_scaled_size); + + /* If we don't have a full iMCU row buffered, return to application for + * more data. Note that preprocessor will always pad to fill the iMCU row + * at the bottom of the image. + */ + if (main->rowgroup_ctr != (JDIMENSION) cinfo->min_DCT_v_scaled_size) + return; + + /* Send the completed row to the compressor */ + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main->suspended) { + (*in_row_ctr)--; + main->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main->suspended) { + (*in_row_ctr)++; + main->suspended = FALSE; + } + main->rowgroup_ctr = 0; + main->cur_iMCU_row++; + } +} + + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + +/* + * Process some data. + * This routine handles all of the modes that use a full-size buffer. + */ + +METHODDEF(void) +process_data_buffer_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci; + jpeg_component_info *compptr; + boolean writing = (main->pass_mode != JBUF_CRANK_DEST); + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Realign the virtual buffers if at the start of an iMCU row. */ + if (main->rowgroup_ctr == 0) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->buffer[ci] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, main->whole_image[ci], + main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); + } + /* In a read pass, pretend we just read some source data. */ + if (! writing) { + *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; + main->rowgroup_ctr = DCTSIZE; + } + } + + /* If a write pass, read input data until the current iMCU row is full. */ + /* Note: preprocessor will pad if necessary to fill the last iMCU row. */ + if (writing) { + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main->buffer, &main->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + /* Return to application if we need more data to fill the iMCU row. */ + if (main->rowgroup_ctr < DCTSIZE) + return; + } + + /* Emit data, unless this is a sink-only pass. */ + if (main->pass_mode != JBUF_SAVE_SOURCE) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main->suspended) { + (*in_row_ctr)--; + main->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main->suspended) { + (*in_row_ctr)++; + main->suspended = FALSE; + } + } + + /* If get here, we are done with this iMCU row. Mark buffer empty. */ + main->rowgroup_ctr = 0; + main->cur_iMCU_row++; + } +} + +#endif /* FULL_MAIN_BUFFER_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_c_main_controller *) main; + main->pub.start_pass = start_pass_main; + + /* We don't need to create a buffer in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + /* Create the buffer. It holds downsampled data, so each component + * may be of a different size. + */ + if (need_full_buffer) { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component */ + /* Note we pad the bottom to a multiple of the iMCU height */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + compptr->width_in_blocks * compptr->DCT_h_scaled_size, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor) * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * compptr->DCT_v_scaled_size)); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + main->whole_image[0] = NULL; /* flag for no virtual arrays */ +#endif + /* Allocate a strip buffer for each component */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_h_scaled_size, + (JDIMENSION) (compptr->v_samp_factor * compptr->DCT_v_scaled_size)); + } + } +} diff --git a/lib/jpeg/src/jcmainct.d b/lib/jpeg/src/jcmainct.d new file mode 100644 index 0000000..9943bb2 --- /dev/null +++ b/lib/jpeg/src/jcmainct.d @@ -0,0 +1,14 @@ +jcmainct.o: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcmarker.c b/lib/jpeg/src/jcmarker.c new file mode 100644 index 0000000..1979385 --- /dev/null +++ b/lib/jpeg/src/jcmarker.c @@ -0,0 +1,680 @@ +/* + * jcmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2003-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write JPEG datastream markers. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_writer pub; /* public fields */ + + unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */ +} my_marker_writer; + +typedef my_marker_writer * my_marker_ptr; + + +/* + * Basic output routines. + * + * Note that we do not support suspension while writing a marker. + * Therefore, an application using suspension must ensure that there is + * enough buffer space for the initial markers (typ. 600-700 bytes) before + * calling jpeg_start_compress, and enough space to write the trailing EOI + * (a few bytes) before calling jpeg_finish_compress. Multipass compression + * modes are not supported at all with suspension, so those two are the only + * points where markers will be written. + */ + +LOCAL(void) +emit_byte (j_compress_ptr cinfo, int val) +/* Emit a byte */ +{ + struct jpeg_destination_mgr * dest = cinfo->dest; + + *(dest->next_output_byte)++ = (JOCTET) val; + if (--dest->free_in_buffer == 0) { + if (! (*dest->empty_output_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } +} + + +LOCAL(void) +emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) +/* Emit a marker code */ +{ + emit_byte(cinfo, 0xFF); + emit_byte(cinfo, (int) mark); +} + + +LOCAL(void) +emit_2bytes (j_compress_ptr cinfo, int value) +/* Emit a 2-byte integer; these are always MSB first in JPEG files */ +{ + emit_byte(cinfo, (value >> 8) & 0xFF); + emit_byte(cinfo, value & 0xFF); +} + + +/* + * Routines to write specific marker types. + */ + +LOCAL(int) +emit_dqt (j_compress_ptr cinfo, int index) +/* Emit a DQT marker */ +/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ +{ + JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + int prec; + int i; + + if (qtbl == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index); + + prec = 0; + for (i = 0; i <= cinfo->lim_Se; i++) { + if (qtbl->quantval[cinfo->natural_order[i]] > 255) + prec = 1; + } + + if (! qtbl->sent_table) { + emit_marker(cinfo, M_DQT); + + emit_2bytes(cinfo, + prec ? cinfo->lim_Se * 2 + 2 + 1 + 2 : cinfo->lim_Se + 1 + 1 + 2); + + emit_byte(cinfo, index + (prec<<4)); + + for (i = 0; i <= cinfo->lim_Se; i++) { + /* The table entries must be emitted in zigzag order. */ + unsigned int qval = qtbl->quantval[cinfo->natural_order[i]]; + if (prec) + emit_byte(cinfo, (int) (qval >> 8)); + emit_byte(cinfo, (int) (qval & 0xFF)); + } + + qtbl->sent_table = TRUE; + } + + return prec; +} + + +LOCAL(void) +emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) +/* Emit a DHT marker */ +{ + JHUFF_TBL * htbl; + int length, i; + + if (is_ac) { + htbl = cinfo->ac_huff_tbl_ptrs[index]; + index += 0x10; /* output index has AC bit set */ + } else { + htbl = cinfo->dc_huff_tbl_ptrs[index]; + } + + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index); + + if (! htbl->sent_table) { + emit_marker(cinfo, M_DHT); + + length = 0; + for (i = 1; i <= 16; i++) + length += htbl->bits[i]; + + emit_2bytes(cinfo, length + 2 + 1 + 16); + emit_byte(cinfo, index); + + for (i = 1; i <= 16; i++) + emit_byte(cinfo, htbl->bits[i]); + + for (i = 0; i < length; i++) + emit_byte(cinfo, htbl->huffval[i]); + + htbl->sent_table = TRUE; + } +} + + +LOCAL(void) +emit_dac (j_compress_ptr cinfo) +/* Emit a DAC marker */ +/* Since the useful info is so small, we want to emit all the tables in */ +/* one DAC marker. Therefore this routine does its own scan of the table. */ +{ +#ifdef C_ARITH_CODING_SUPPORTED + char dc_in_use[NUM_ARITH_TBLS]; + char ac_in_use[NUM_ARITH_TBLS]; + int length, i; + jpeg_component_info *compptr; + + for (i = 0; i < NUM_ARITH_TBLS; i++) + dc_in_use[i] = ac_in_use[i] = 0; + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) + dc_in_use[compptr->dc_tbl_no] = 1; + /* AC needs no table when not present */ + if (cinfo->Se) + ac_in_use[compptr->ac_tbl_no] = 1; + } + + length = 0; + for (i = 0; i < NUM_ARITH_TBLS; i++) + length += dc_in_use[i] + ac_in_use[i]; + + emit_marker(cinfo, M_DAC); + + emit_2bytes(cinfo, length*2 + 2); + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + if (dc_in_use[i]) { + emit_byte(cinfo, i); + emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4)); + } + if (ac_in_use[i]) { + emit_byte(cinfo, i + 0x10); + emit_byte(cinfo, cinfo->arith_ac_K[i]); + } + } +#endif /* C_ARITH_CODING_SUPPORTED */ +} + + +LOCAL(void) +emit_dri (j_compress_ptr cinfo) +/* Emit a DRI marker */ +{ + emit_marker(cinfo, M_DRI); + + emit_2bytes(cinfo, 4); /* fixed length */ + + emit_2bytes(cinfo, (int) cinfo->restart_interval); +} + + +LOCAL(void) +emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) +/* Emit a SOF marker */ +{ + int ci; + jpeg_component_info *compptr; + + emit_marker(cinfo, code); + + emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */ + + /* Make sure image isn't bigger than SOF field can handle */ + if ((long) cinfo->jpeg_height > 65535L || + (long) cinfo->jpeg_width > 65535L) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535); + + emit_byte(cinfo, cinfo->data_precision); + emit_2bytes(cinfo, (int) cinfo->jpeg_height); + emit_2bytes(cinfo, (int) cinfo->jpeg_width); + + emit_byte(cinfo, cinfo->num_components); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + emit_byte(cinfo, compptr->component_id); + emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor); + emit_byte(cinfo, compptr->quant_tbl_no); + } +} + + +LOCAL(void) +emit_sos (j_compress_ptr cinfo) +/* Emit a SOS marker */ +{ + int i, td, ta; + jpeg_component_info *compptr; + + emit_marker(cinfo, M_SOS); + + emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */ + + emit_byte(cinfo, cinfo->comps_in_scan); + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + emit_byte(cinfo, compptr->component_id); + + /* We emit 0 for unused field(s); this is recommended by the P&M text + * but does not seem to be specified in the standard. + */ + + /* DC needs no table for refinement scan */ + td = cinfo->Ss == 0 && cinfo->Ah == 0 ? compptr->dc_tbl_no : 0; + /* AC needs no table when not present */ + ta = cinfo->Se ? compptr->ac_tbl_no : 0; + + emit_byte(cinfo, (td << 4) + ta); + } + + emit_byte(cinfo, cinfo->Ss); + emit_byte(cinfo, cinfo->Se); + emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al); +} + + +LOCAL(void) +emit_pseudo_sos (j_compress_ptr cinfo) +/* Emit a pseudo SOS marker */ +{ + emit_marker(cinfo, M_SOS); + + emit_2bytes(cinfo, 2 + 1 + 3); /* length */ + + emit_byte(cinfo, 0); /* Ns */ + + emit_byte(cinfo, 0); /* Ss */ + emit_byte(cinfo, cinfo->block_size * cinfo->block_size - 1); /* Se */ + emit_byte(cinfo, 0); /* Ah/Al */ +} + + +LOCAL(void) +emit_jfif_app0 (j_compress_ptr cinfo) +/* Emit a JFIF-compliant APP0 marker */ +{ + /* + * Length of APP0 block (2 bytes) + * Block ID (4 bytes - ASCII "JFIF") + * Zero byte (1 byte to terminate the ID string) + * Version Major, Minor (2 bytes - major first) + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + * Xdpu (2 bytes - dots per unit horizontal) + * Ydpu (2 bytes - dots per unit vertical) + * Thumbnail X size (1 byte) + * Thumbnail Y size (1 byte) + */ + + emit_marker(cinfo, M_APP0); + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + + emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */ + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0x49); + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0); + emit_byte(cinfo, cinfo->JFIF_major_version); /* Version fields */ + emit_byte(cinfo, cinfo->JFIF_minor_version); + emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */ + emit_2bytes(cinfo, (int) cinfo->X_density); + emit_2bytes(cinfo, (int) cinfo->Y_density); + emit_byte(cinfo, 0); /* No thumbnail image */ + emit_byte(cinfo, 0); +} + + +LOCAL(void) +emit_adobe_app14 (j_compress_ptr cinfo) +/* Emit an Adobe APP14 marker */ +{ + /* + * Length of APP14 block (2 bytes) + * Block ID (5 bytes - ASCII "Adobe") + * Version Number (2 bytes - currently 100) + * Flags0 (2 bytes - currently 0) + * Flags1 (2 bytes - currently 0) + * Color transform (1 byte) + * + * Although Adobe TN 5116 mentions Version = 101, all the Adobe files + * now in circulation seem to use Version = 100, so that's what we write. + * + * We write the color transform byte as 1 if the JPEG color space is + * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with + * whether the encoder performed a transformation, which is pretty useless. + */ + + emit_marker(cinfo, M_APP14); + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */ + + emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */ + emit_byte(cinfo, 0x64); + emit_byte(cinfo, 0x6F); + emit_byte(cinfo, 0x62); + emit_byte(cinfo, 0x65); + emit_2bytes(cinfo, 100); /* Version */ + emit_2bytes(cinfo, 0); /* Flags0 */ + emit_2bytes(cinfo, 0); /* Flags1 */ + switch (cinfo->jpeg_color_space) { + case JCS_YCbCr: + emit_byte(cinfo, 1); /* Color transform = 1 */ + break; + case JCS_YCCK: + emit_byte(cinfo, 2); /* Color transform = 2 */ + break; + default: + emit_byte(cinfo, 0); /* Color transform = 0 */ + break; + } +} + + +/* + * These routines allow writing an arbitrary marker with parameters. + * The only intended use is to emit COM or APPn markers after calling + * write_file_header and before calling write_frame_header. + * Other uses are not guaranteed to produce desirable results. + * Counting the parameter bytes properly is the caller's responsibility. + */ + +METHODDEF(void) +write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +/* Emit an arbitrary marker header */ +{ + if (datalen > (unsigned int) 65533) /* safety check */ + ERREXIT(cinfo, JERR_BAD_LENGTH); + + emit_marker(cinfo, (JPEG_MARKER) marker); + + emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */ +} + +METHODDEF(void) +write_marker_byte (j_compress_ptr cinfo, int val) +/* Emit one byte of marker parameters following write_marker_header */ +{ + emit_byte(cinfo, val); +} + + +/* + * Write datastream header. + * This consists of an SOI and optional APPn markers. + * We recommend use of the JFIF marker, but not the Adobe marker, + * when using YCbCr or grayscale data. The JFIF marker should NOT + * be used for any other JPEG colorspace. The Adobe marker is helpful + * to distinguish RGB, CMYK, and YCCK colorspaces. + * Note that an application can write additional header markers after + * jpeg_start_compress returns. + */ + +METHODDEF(void) +write_file_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + emit_marker(cinfo, M_SOI); /* first the SOI */ + + /* SOI is defined to reset restart interval to 0 */ + marker->last_restart_interval = 0; + + if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */ + emit_jfif_app0(cinfo); + if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */ + emit_adobe_app14(cinfo); +} + + +/* + * Write frame header. + * This consists of DQT and SOFn markers, and a conditional pseudo SOS marker. + * Note that we do not emit the SOF until we have emitted the DQT(s). + * This avoids compatibility problems with incorrect implementations that + * try to error-check the quant table numbers as soon as they see the SOF. + */ + +METHODDEF(void) +write_frame_header (j_compress_ptr cinfo) +{ + int ci, prec; + boolean is_baseline; + jpeg_component_info *compptr; + + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + prec = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ + + /* Check for a non-baseline specification. + * Note we assume that Huffman table numbers won't be changed later. + */ + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->data_precision != 8 || cinfo->block_size != DCTSIZE) { + is_baseline = FALSE; + } else { + is_baseline = TRUE; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1) + is_baseline = FALSE; + } + if (prec && is_baseline) { + is_baseline = FALSE; + /* If it's baseline except for quantizer size, warn the user */ + TRACEMS(cinfo, 0, JTRC_16BIT_TABLES); + } + } + + /* Emit the proper SOF marker */ + if (cinfo->arith_code) { + if (cinfo->progressive_mode) + emit_sof(cinfo, M_SOF10); /* SOF code for progressive arithmetic */ + else + emit_sof(cinfo, M_SOF9); /* SOF code for sequential arithmetic */ + } else { + if (cinfo->progressive_mode) + emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (is_baseline) + emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ + else + emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */ + } + + /* Check to emit pseudo SOS marker */ + if (cinfo->progressive_mode && cinfo->block_size != DCTSIZE) + emit_pseudo_sos(cinfo); +} + + +/* + * Write scan header. + * This consists of DHT or DAC markers, optional DRI, and SOS. + * Compressed data will be written following the SOS. + */ + +METHODDEF(void) +write_scan_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + int i; + jpeg_component_info *compptr; + + if (cinfo->arith_code) { + /* Emit arith conditioning info. We may have some duplication + * if the file has multiple scans, but it's so small it's hardly + * worth worrying about. + */ + emit_dac(cinfo); + } else { + /* Emit Huffman tables. + * Note that emit_dht() suppresses any duplicate tables. + */ + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + /* AC needs no table when not present */ + if (cinfo->Se) + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } + + /* Emit DRI if required --- note that DRI value could change for each scan. + * We avoid wasting space with unnecessary DRIs, however. + */ + if (cinfo->restart_interval != marker->last_restart_interval) { + emit_dri(cinfo); + marker->last_restart_interval = cinfo->restart_interval; + } + + emit_sos(cinfo); +} + + +/* + * Write datastream trailer. + */ + +METHODDEF(void) +write_file_trailer (j_compress_ptr cinfo) +{ + emit_marker(cinfo, M_EOI); +} + + +/* + * Write an abbreviated table-specification datastream. + * This consists of SOI, DQT and DHT tables, and EOI. + * Any table that is defined and not marked sent_table = TRUE will be + * emitted. Note that all tables will be marked sent_table = TRUE at exit. + */ + +METHODDEF(void) +write_tables_only (j_compress_ptr cinfo) +{ + int i; + + emit_marker(cinfo, M_SOI); + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if (cinfo->quant_tbl_ptrs[i] != NULL) + (void) emit_dqt(cinfo, i); + } + + if (! cinfo->arith_code) { + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if (cinfo->dc_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, FALSE); + if (cinfo->ac_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, TRUE); + } + } + + emit_marker(cinfo, M_EOI); +} + + +/* + * Initialize the marker writer module. + */ + +GLOBAL(void) +jinit_marker_writer (j_compress_ptr cinfo) +{ + my_marker_ptr marker; + + /* Create the subobject */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_marker_writer)); + cinfo->marker = (struct jpeg_marker_writer *) marker; + /* Initialize method pointers */ + marker->pub.write_file_header = write_file_header; + marker->pub.write_frame_header = write_frame_header; + marker->pub.write_scan_header = write_scan_header; + marker->pub.write_file_trailer = write_file_trailer; + marker->pub.write_tables_only = write_tables_only; + marker->pub.write_marker_header = write_marker_header; + marker->pub.write_marker_byte = write_marker_byte; + /* Initialize private state */ + marker->last_restart_interval = 0; +} diff --git a/lib/jpeg/src/jcmarker.d b/lib/jpeg/src/jcmarker.d new file mode 100644 index 0000000..c56d61b --- /dev/null +++ b/lib/jpeg/src/jcmarker.d @@ -0,0 +1,14 @@ +jcmarker.o: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcmaster.c b/lib/jpeg/src/jcmaster.c new file mode 100644 index 0000000..2cf794a --- /dev/null +++ b/lib/jpeg/src/jcmaster.c @@ -0,0 +1,838 @@ +/* + * jcmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2003-2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG compressor. + * These routines are concerned with parameter validation, initial setup, + * and inter-pass control (determining the number of passes and the work + * to be done in each pass). + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; + + +/* + * Support routines that do various essential calculations. + */ + +/* + * Compute JPEG image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + */ + +GLOBAL(void) +jpeg_calc_jpeg_dimensions (j_compress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#ifdef DCT_SCALING_SUPPORTED + + /* Compute actual JPEG image dimensions and DCT scaling choices. */ + if (cinfo->scale_num >= cinfo->scale_denom * 8) { + /* Provide 8/1 scaling */ + cinfo->jpeg_width = cinfo->image_width << 3; + cinfo->jpeg_height = cinfo->image_height << 3; + cinfo->min_DCT_h_scaled_size = 1; + cinfo->min_DCT_v_scaled_size = 1; + } else if (cinfo->scale_num >= cinfo->scale_denom * 4) { + /* Provide 4/1 scaling */ + cinfo->jpeg_width = cinfo->image_width << 2; + cinfo->jpeg_height = cinfo->image_height << 2; + cinfo->min_DCT_h_scaled_size = 2; + cinfo->min_DCT_v_scaled_size = 2; + } else if (cinfo->scale_num * 3 >= cinfo->scale_denom * 8) { + /* Provide 8/3 scaling */ + cinfo->jpeg_width = (cinfo->image_width << 1) + (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 2, 3L); + cinfo->jpeg_height = (cinfo->image_height << 1) + (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 2, 3L); + cinfo->min_DCT_h_scaled_size = 3; + cinfo->min_DCT_v_scaled_size = 3; + } else if (cinfo->scale_num >= cinfo->scale_denom * 2) { + /* Provide 2/1 scaling */ + cinfo->jpeg_width = cinfo->image_width << 1; + cinfo->jpeg_height = cinfo->image_height << 1; + cinfo->min_DCT_h_scaled_size = 4; + cinfo->min_DCT_v_scaled_size = 4; + } else if (cinfo->scale_num * 5 >= cinfo->scale_denom * 8) { + /* Provide 8/5 scaling */ + cinfo->jpeg_width = cinfo->image_width + (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 3, 5L); + cinfo->jpeg_height = cinfo->image_height + (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 3, 5L); + cinfo->min_DCT_h_scaled_size = 5; + cinfo->min_DCT_v_scaled_size = 5; + } else if (cinfo->scale_num * 3 >= cinfo->scale_denom * 4) { + /* Provide 4/3 scaling */ + cinfo->jpeg_width = cinfo->image_width + (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 3L); + cinfo->jpeg_height = cinfo->image_height + (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 3L); + cinfo->min_DCT_h_scaled_size = 6; + cinfo->min_DCT_v_scaled_size = 6; + } else if (cinfo->scale_num * 7 >= cinfo->scale_denom * 8) { + /* Provide 8/7 scaling */ + cinfo->jpeg_width = cinfo->image_width + (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 7L); + cinfo->jpeg_height = cinfo->image_height + (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 7L); + cinfo->min_DCT_h_scaled_size = 7; + cinfo->min_DCT_v_scaled_size = 7; + } else if (cinfo->scale_num >= cinfo->scale_denom) { + /* Provide 1/1 scaling */ + cinfo->jpeg_width = cinfo->image_width; + cinfo->jpeg_height = cinfo->image_height; + cinfo->min_DCT_h_scaled_size = 8; + cinfo->min_DCT_v_scaled_size = 8; + } else if (cinfo->scale_num * 9 >= cinfo->scale_denom * 8) { + /* Provide 8/9 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 8, 9L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 8, 9L); + cinfo->min_DCT_h_scaled_size = 9; + cinfo->min_DCT_v_scaled_size = 9; + } else if (cinfo->scale_num * 5 >= cinfo->scale_denom * 4) { + /* Provide 4/5 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 4, 5L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 4, 5L); + cinfo->min_DCT_h_scaled_size = 10; + cinfo->min_DCT_v_scaled_size = 10; + } else if (cinfo->scale_num * 11 >= cinfo->scale_denom * 8) { + /* Provide 8/11 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 8, 11L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 8, 11L); + cinfo->min_DCT_h_scaled_size = 11; + cinfo->min_DCT_v_scaled_size = 11; + } else if (cinfo->scale_num * 3 >= cinfo->scale_denom * 2) { + /* Provide 2/3 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 2, 3L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 2, 3L); + cinfo->min_DCT_h_scaled_size = 12; + cinfo->min_DCT_v_scaled_size = 12; + } else if (cinfo->scale_num * 13 >= cinfo->scale_denom * 8) { + /* Provide 8/13 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 8, 13L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 8, 13L); + cinfo->min_DCT_h_scaled_size = 13; + cinfo->min_DCT_v_scaled_size = 13; + } else if (cinfo->scale_num * 7 >= cinfo->scale_denom * 4) { + /* Provide 4/7 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 4, 7L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 4, 7L); + cinfo->min_DCT_h_scaled_size = 14; + cinfo->min_DCT_v_scaled_size = 14; + } else if (cinfo->scale_num * 15 >= cinfo->scale_denom * 8) { + /* Provide 8/15 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 8, 15L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 8, 15L); + cinfo->min_DCT_h_scaled_size = 15; + cinfo->min_DCT_v_scaled_size = 15; + } else { + /* Provide 1/2 scaling */ + cinfo->jpeg_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->jpeg_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_h_scaled_size = 16; + cinfo->min_DCT_v_scaled_size = 16; + } + +#else /* !DCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->jpeg_width = cinfo->image_width; + cinfo->jpeg_height = cinfo->image_height; + cinfo->min_DCT_h_scaled_size = DCTSIZE; + cinfo->min_DCT_v_scaled_size = DCTSIZE; + +#endif /* DCT_SCALING_SUPPORTED */ +} + + +LOCAL(void) +jpeg_calc_trans_dimensions (j_compress_ptr cinfo) +{ + if (cinfo->min_DCT_h_scaled_size < 1 || cinfo->min_DCT_h_scaled_size > 16 + || cinfo->min_DCT_h_scaled_size != cinfo->min_DCT_v_scaled_size) + ERREXIT2(cinfo, JERR_BAD_DCTSIZE, + cinfo->min_DCT_h_scaled_size, cinfo->min_DCT_v_scaled_size); + + cinfo->block_size = cinfo->min_DCT_h_scaled_size; + + switch (cinfo->block_size) { + case 2: cinfo->natural_order = jpeg_natural_order2; break; + case 3: cinfo->natural_order = jpeg_natural_order3; break; + case 4: cinfo->natural_order = jpeg_natural_order4; break; + case 5: cinfo->natural_order = jpeg_natural_order5; break; + case 6: cinfo->natural_order = jpeg_natural_order6; break; + case 7: cinfo->natural_order = jpeg_natural_order7; break; + default: cinfo->natural_order = jpeg_natural_order; break; + } + + cinfo->lim_Se = cinfo->block_size < DCTSIZE ? + cinfo->block_size * cinfo->block_size - 1 : DCTSIZE2-1; +} + + +LOCAL(void) +initial_setup (j_compress_ptr cinfo, boolean transcode_only) +/* Do computations that are needed before master selection phase */ +{ + int ci, ssize; + jpeg_component_info *compptr; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + if (transcode_only) + jpeg_calc_trans_dimensions(cinfo); + else + jpeg_calc_jpeg_dimensions(cinfo); + + /* Sanity check on image dimensions */ + if (cinfo->jpeg_height <= 0 || cinfo->jpeg_width <= 0 || + cinfo->num_components <= 0 || cinfo->input_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->jpeg_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->jpeg_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* Width of an input scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Fill in the correct component_index value; don't rely on application */ + compptr->component_index = ci; + /* In selecting the actual DCT scaling for each component, we try to + * scale down the chroma components via DCT scaling rather than downsampling. + * This saves time if the downsampler gets to use 1:1 scaling. + * Note this code adapts subsampling ratios which are powers of 2. + */ + ssize = 1; +#ifdef DCT_SCALING_SUPPORTED + while (cinfo->min_DCT_h_scaled_size * ssize <= + (cinfo->do_fancy_downsampling ? DCTSIZE : DCTSIZE / 2) && + (cinfo->max_h_samp_factor % (compptr->h_samp_factor * ssize * 2)) == 0) { + ssize = ssize * 2; + } +#endif + compptr->DCT_h_scaled_size = cinfo->min_DCT_h_scaled_size * ssize; + ssize = 1; +#ifdef DCT_SCALING_SUPPORTED + while (cinfo->min_DCT_v_scaled_size * ssize <= + (cinfo->do_fancy_downsampling ? DCTSIZE : DCTSIZE / 2) && + (cinfo->max_v_samp_factor % (compptr->v_samp_factor * ssize * 2)) == 0) { + ssize = ssize * 2; + } +#endif + compptr->DCT_v_scaled_size = cinfo->min_DCT_v_scaled_size * ssize; + + /* We don't support DCT ratios larger than 2. */ + if (compptr->DCT_h_scaled_size > compptr->DCT_v_scaled_size * 2) + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size * 2; + else if (compptr->DCT_v_scaled_size > compptr->DCT_h_scaled_size * 2) + compptr->DCT_v_scaled_size = compptr->DCT_h_scaled_size * 2; + + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_width * + (long) (compptr->h_samp_factor * compptr->DCT_h_scaled_size), + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_height * + (long) (compptr->v_samp_factor * compptr->DCT_v_scaled_size), + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + /* Mark component needed (this flag isn't actually used for compression) */ + compptr->component_needed = TRUE; + } + + /* Compute number of fully interleaved MCU rows (number of times that + * main controller will call coefficient controller). + */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_height, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); +} + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(void) +validate_script (j_compress_ptr cinfo) +/* Verify that the scan script in cinfo->scan_info[] is valid; also + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. + */ +{ + const jpeg_scan_info * scanptr; + int scanno, ncomps, ci, coefi, thisi; + int Ss, Se, Ah, Al; + boolean component_sent[MAX_COMPONENTS]; +#ifdef C_PROGRESSIVE_SUPPORTED + int * last_bitpos_ptr; + int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; + /* -1 until that coefficient has been seen; then last Al for it */ +#endif + + if (cinfo->num_scans <= 0) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); + + /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; + * for progressive JPEG, no scan can have this. + */ + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { +#ifdef C_PROGRESSIVE_SUPPORTED + cinfo->progressive_mode = TRUE; + last_bitpos_ptr = & last_bitpos[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (coefi = 0; coefi < DCTSIZE2; coefi++) + *last_bitpos_ptr++ = -1; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; + } + + for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) { + /* Validate component indexes */ + ncomps = scanptr->comps_in_scan; + if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (thisi < 0 || thisi >= cinfo->num_components) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + /* Components must appear in SOF order within each scan */ + if (ci > 0 && thisi <= scanptr->component_index[ci-1]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + } + /* Validate progression parameters */ + Ss = scanptr->Ss; + Se = scanptr->Se; + Ah = scanptr->Ah; + Al = scanptr->Al; + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N+1 for N-bit precision. + * Here we allow 0..10 for 8-bit data; Al larger than 10 results in + * out-of-range reconstructed DC values during the first DC scan, + * which might cause problems for some decoders. + */ +#if BITS_IN_JSAMPLE == 8 +#define MAX_AH_AL 10 +#else +#define MAX_AH_AL 13 +#endif + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || + Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + if (Ss == 0) { + if (Se != 0) /* DC and AC together not OK */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + if (ncomps != 1) /* AC scans must be for only one component */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + for (ci = 0; ci < ncomps; ci++) { + last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0]; + if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + for (coefi = Ss; coefi <= Se; coefi++) { + if (last_bitpos_ptr[coefi] < 0) { + /* first scan of this coefficient */ + if (Ah != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + /* not first scan */ + if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + last_bitpos_ptr[coefi] = Al; + } + } +#endif + } else { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + /* Make sure components are not sent twice */ + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (component_sent[thisi]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + component_sent[thisi] = TRUE; + } + } + } + + /* Now verify that everything got sent. */ + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* For progressive mode, we only check that at least some DC data + * got sent for each component; the spec does not require that all bits + * of all coefficients be transmitted. Would it be wiser to enforce + * transmission of all coefficient bits?? + */ + for (ci = 0; ci < cinfo->num_components; ci++) { + if (last_bitpos[ci][0] < 0) + ERREXIT(cinfo, JERR_MISSING_DATA); + } +#endif + } else { + for (ci = 0; ci < cinfo->num_components; ci++) { + if (! component_sent[ci]) + ERREXIT(cinfo, JERR_MISSING_DATA); + } + } +} + + +LOCAL(void) +reduce_script (j_compress_ptr cinfo) +/* Adapt scan script for use with reduced block size; + * assume that script has been validated before. + */ +{ + jpeg_scan_info * scanptr; + int idxout, idxin; + + /* Circumvent const declaration for this function */ + scanptr = (jpeg_scan_info *) cinfo->scan_info; + idxout = 0; + + for (idxin = 0; idxin < cinfo->num_scans; idxin++) { + /* After skipping, idxout becomes smaller than idxin */ + if (idxin != idxout) + /* Copy rest of data; + * note we stay in given chunk of allocated memory. + */ + scanptr[idxout] = scanptr[idxin]; + if (scanptr[idxout].Ss > cinfo->lim_Se) + /* Entire scan out of range - skip this entry */ + continue; + if (scanptr[idxout].Se > cinfo->lim_Se) + /* Limit scan to end of block */ + scanptr[idxout].Se = cinfo->lim_Se; + idxout++; + } + + cinfo->num_scans = idxout; +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + +LOCAL(void) +select_scan_parameters (j_compress_ptr cinfo) +/* Set up the scan parameters for the current scan */ +{ + int ci; + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->scan_info != NULL) { + /* Prepare for current scan --- the script is already validated */ + my_master_ptr master = (my_master_ptr) cinfo->master; + const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + + cinfo->comps_in_scan = scanptr->comps_in_scan; + for (ci = 0; ci < scanptr->comps_in_scan; ci++) { + cinfo->cur_comp_info[ci] = + &cinfo->comp_info[scanptr->component_index[ci]]; + } + if (cinfo->progressive_mode) { + cinfo->Ss = scanptr->Ss; + cinfo->Se = scanptr->Se; + cinfo->Ah = scanptr->Ah; + cinfo->Al = scanptr->Al; + return; + } + } + else +#endif + { + /* Prepare for single sequential-JPEG scan containing all components */ + if (cinfo->num_components > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPS_IN_SCAN); + cinfo->comps_in_scan = cinfo->num_components; + for (ci = 0; ci < cinfo->num_components; ci++) { + cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; + } + } + cinfo->Ss = 0; + cinfo->Se = cinfo->block_size * cinfo->block_size - 1; + cinfo->Ah = 0; + cinfo->Al = 0; +} + + +LOCAL(void) +per_scan_setup (j_compress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_h_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_width, + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->jpeg_height, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_h_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } + + /* Convert restart specified in rows to actual MCU count. */ + /* Note that count must fit in 16 bits, so we provide limiting. */ + if (cinfo->restart_in_rows > 0) { + long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row; + cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L); + } +} + + +/* + * Per-pass setup. + * This is called at the beginning of each pass. We determine which modules + * will be active during this pass and give them appropriate start_pass calls. + * We also set is_last_pass to indicate whether any more passes will be + * required. + */ + +METHODDEF(void) +prepare_for_pass (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + switch (master->pass_type) { + case main_pass: + /* Initial pass: will collect input data, and do either Huffman + * optimization or data output for the first scan. + */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (! cinfo->raw_data_in) { + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->downsample->start_pass) (cinfo); + (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); + } + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + if (cinfo->optimize_coding) { + /* No immediate data output; postpone writing frame/scan headers */ + master->pub.call_pass_startup = FALSE; + } else { + /* Will write frame/scan headers at first jpeg_write_scanlines call */ + master->pub.call_pass_startup = TRUE; + } + break; +#ifdef ENTROPY_OPT_SUPPORTED + case huff_opt_pass: + /* Do Huffman optimization for a scan after the first one. */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (cinfo->Ss != 0 || cinfo->Ah == 0) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + master->pub.call_pass_startup = FALSE; + break; + } + /* Special case: Huffman DC refinement scans need no Huffman table + * and therefore we can skip the optimization pass for them. + */ + master->pass_type = output_pass; + master->pass_number++; + /*FALLTHROUGH*/ +#endif + case output_pass: + /* Do a data-output pass. */ + /* We need not repeat per-scan setup if prior optimization pass did it. */ + if (! cinfo->optimize_coding) { + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + } + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + /* We emit frame/scan headers now */ + if (master->scan_number == 0) + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); + master->pub.call_pass_startup = FALSE; + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + } + + master->pub.is_last_pass = (master->pass_number == master->total_passes-1); + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->total_passes; + } +} + + +/* + * Special start-of-pass hook. + * This is called by jpeg_write_scanlines if call_pass_startup is TRUE. + * In single-pass processing, we need this hook because we don't want to + * write frame/scan headers during jpeg_start_compress; we want to let the + * application write COM markers etc. between jpeg_start_compress and the + * jpeg_write_scanlines loop. + * In multi-pass processing, this routine is not used. + */ + +METHODDEF(void) +pass_startup (j_compress_ptr cinfo) +{ + cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */ + + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); +} + + +/* + * Finish up at end of pass. + */ + +METHODDEF(void) +finish_pass_master (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* The entropy coder always needs an end-of-pass call, + * either to analyze statistics or to flush its output buffer. + */ + (*cinfo->entropy->finish_pass) (cinfo); + + /* Update state for next pass */ + switch (master->pass_type) { + case main_pass: + /* next pass is either output of scan 0 (after optimization) + * or output of scan 1 (if no optimization). + */ + master->pass_type = output_pass; + if (! cinfo->optimize_coding) + master->scan_number++; + break; + case huff_opt_pass: + /* next pass is always output of current scan */ + master->pass_type = output_pass; + break; + case output_pass: + /* next pass is either optimization or output of next scan */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + master->scan_number++; + break; + } + + master->pass_number++; +} + + +/* + * Initialize master compression control. + */ + +GLOBAL(void) +jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_comp_master)); + cinfo->master = (struct jpeg_comp_master *) master; + master->pub.prepare_for_pass = prepare_for_pass; + master->pub.pass_startup = pass_startup; + master->pub.finish_pass = finish_pass_master; + master->pub.is_last_pass = FALSE; + + /* Validate parameters, determine derived values */ + initial_setup(cinfo, transcode_only); + + if (cinfo->scan_info != NULL) { +#ifdef C_MULTISCAN_FILES_SUPPORTED + validate_script(cinfo); + if (cinfo->block_size < DCTSIZE) + reduce_script(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + cinfo->num_scans = 1; + } + + if ((cinfo->progressive_mode || cinfo->block_size < DCTSIZE) && + !cinfo->arith_code) /* TEMPORARY HACK ??? */ + /* assume default tables no good for progressive or downscale mode */ + cinfo->optimize_coding = TRUE; + + /* Initialize my private state */ + if (transcode_only) { + /* no main pass in transcoding */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + else + master->pass_type = output_pass; + } else { + /* for normal compression, first pass is always this type: */ + master->pass_type = main_pass; + } + master->scan_number = 0; + master->pass_number = 0; + if (cinfo->optimize_coding) + master->total_passes = cinfo->num_scans * 2; + else + master->total_passes = cinfo->num_scans; +} diff --git a/lib/jpeg/src/jcmaster.d b/lib/jpeg/src/jcmaster.d new file mode 100644 index 0000000..180486e --- /dev/null +++ b/lib/jpeg/src/jcmaster.d @@ -0,0 +1,14 @@ +jcmaster.o: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcomapi.c b/lib/jpeg/src/jcomapi.c new file mode 100644 index 0000000..1b1a340 --- /dev/null +++ b/lib/jpeg/src/jcomapi.c @@ -0,0 +1,106 @@ +/* + * jcomapi.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface routines that are used for both + * compression and decompression. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Do nothing if called on a not-initialized or destroyed JPEG object. */ + if (cinfo->mem == NULL) + return; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + if (cinfo->is_decompressor) { + cinfo->global_state = DSTATE_START; + /* Try to keep application from accessing now-deleted marker list. + * A bit kludgy to do it here, but this is the most central place. + */ + ((j_decompress_ptr) cinfo)->marker_list = NULL; + } else { + cinfo->global_state = CSTATE_START; + } +} + + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL(JQUANT_TBL *) +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + + +GLOBAL(JHUFF_TBL *) +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} diff --git a/lib/jpeg/src/jcomapi.d b/lib/jpeg/src/jcomapi.d new file mode 100644 index 0000000..73158cf --- /dev/null +++ b/lib/jpeg/src/jcomapi.d @@ -0,0 +1,14 @@ +jcomapi.o: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jconfig.h b/lib/jpeg/src/jconfig.h new file mode 100644 index 0000000..97ca026 --- /dev/null +++ b/lib/jpeg/src/jconfig.h @@ -0,0 +1,46 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/lib/jpeg/src/jcparam.c b/lib/jpeg/src/jcparam.c new file mode 100644 index 0000000..10c5c87 --- /dev/null +++ b/lib/jpeg/src/jcparam.c @@ -0,0 +1,632 @@ +/* + * jcparam.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2003-2008 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains optional default-setting code for the JPEG compressor. + * Applications do not have to use this file, but those that don't use it + * must know a lot more about the innards of the JPEG code. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Quantization table setup routines + */ + +GLOBAL(void) +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline) +/* Define a quantization table equal to the basic_table times + * a scale factor (given as a percentage). + * If force_baseline is TRUE, the computed quantization table entries + * are limited to 1..255 for JPEG baseline compatibility. + */ +{ + JQUANT_TBL ** qtblptr; + int i; + long temp; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (which_tbl < 0 || which_tbl >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, which_tbl); + + qtblptr = & cinfo->quant_tbl_ptrs[which_tbl]; + + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + for (i = 0; i < DCTSIZE2; i++) { + temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; + /* limit the values to the valid range */ + if (temp <= 0L) temp = 1L; + if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */ + if (force_baseline && temp > 255L) + temp = 255L; /* limit to baseline range if requested */ + (*qtblptr)->quantval[i] = (UINT16) temp; + } + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*qtblptr)->sent_table = FALSE; +} + + +/* These are the sample quantization tables given in JPEG spec section K.1. + * The spec says that the values given produce "good" quality, and + * when divided by 2, "very good" quality. + */ +static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; +static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + + +GLOBAL(void) +jpeg_default_qtables (j_compress_ptr cinfo, boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables + * and straight percentage-scaling quality scales. + * This entry point allows different scalings for luminance and chrominance. + */ +{ + /* Set up two quantization tables using the specified scaling */ + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + cinfo->q_scale_factor[0], force_baseline); + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + cinfo->q_scale_factor[1], force_baseline); +} + + +GLOBAL(void) +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables + * and a straight percentage-scaling quality scale. In most cases it's better + * to use jpeg_set_quality (below); this entry point is provided for + * applications that insist on a linear percentage scaling. + */ +{ + /* Set up two quantization tables using the specified scaling */ + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + scale_factor, force_baseline); + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + scale_factor, force_baseline); +} + + +GLOBAL(int) +jpeg_quality_scaling (int quality) +/* Convert a user-specified quality rating to a percentage scaling factor + * for an underlying quantization table, using our recommended scaling curve. + * The input 'quality' factor should be 0 (terrible) to 100 (very good). + */ +{ + /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ + if (quality <= 0) quality = 1; + if (quality > 100) quality = 100; + + /* The basic table is used as-is (scaling 100) for a quality of 50. + * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; + * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table + * to make all the table entries 1 (hence, minimum quantization loss). + * Qualities 1..50 are converted to scaling percentage 5000/Q. + */ + if (quality < 50) + quality = 5000 / quality; + else + quality = 200 - quality*2; + + return quality; +} + + +GLOBAL(void) +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables. + * This is the standard quality-adjusting entry point for typical user + * interfaces; only those who want detailed control over quantization tables + * would use the preceding three routines directly. + */ +{ + /* Convert user 0-100 rating to percentage scaling */ + quality = jpeg_quality_scaling(quality); + + /* Set up standard quality tables */ + jpeg_set_linear_quality(cinfo, quality, force_baseline); +} + + +/* + * Huffman table setup routines + */ + +LOCAL(void) +add_huff_table (j_compress_ptr cinfo, + JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) +/* Define a Huffman table */ +{ + int nsymbols, len; + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + /* Copy the number-of-symbols-of-each-code-length counts */ + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + /* Validate the counts. We do this here mainly so we can copy the right + * number of symbols from the val[] array, without risking marching off + * the end of memory. jchuff.c will do a more thorough test later. + */ + nsymbols = 0; + for (len = 1; len <= 16; len++) + nsymbols += bits[len]; + if (nsymbols < 1 || nsymbols > 256) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + MEMCOPY((*htblptr)->huffval, val, nsymbols * SIZEOF(UINT8)); + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*htblptr)->sent_table = FALSE; +} + + +LOCAL(void) +std_huff_tables (j_compress_ptr cinfo) +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +{ + static const UINT8 bits_dc_luminance[17] = + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_luminance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_dc_chrominance[17] = + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_chrominance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_ac_luminance[17] = + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; + static const UINT8 val_ac_luminance[] = + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + static const UINT8 bits_ac_chrominance[17] = + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + static const UINT8 val_ac_chrominance[] = + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + bits_dc_luminance, val_dc_luminance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + bits_ac_luminance, val_ac_luminance); + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + bits_dc_chrominance, val_dc_chrominance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + bits_ac_chrominance, val_ac_chrominance); +} + + +/* + * Default parameter setup for compression. + * + * Applications that don't choose to use this routine must do their + * own setup of all these parameters. Alternately, you can call this + * to establish defaults and then alter parameters selectively. This + * is the recommended approach since, if we add any new parameters, + * your code will still work (they'll be set to reasonable defaults). + */ + +GLOBAL(void) +jpeg_set_defaults (j_compress_ptr cinfo) +{ + int i; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Allocate comp_info array large enough for maximum component count. + * Array is made permanent in case application wants to compress + * multiple images at same param settings. + */ + if (cinfo->comp_info == NULL) + cinfo->comp_info = (jpeg_component_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + MAX_COMPONENTS * SIZEOF(jpeg_component_info)); + + /* Initialize everything not dependent on the color space */ + + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->data_precision = BITS_IN_JSAMPLE; + /* Set up two quantization tables using default quality of 75 */ + jpeg_set_quality(cinfo, 75, TRUE); + /* Set up two Huffman tables */ + std_huff_tables(cinfo); + + /* Initialize default arithmetic coding conditioning */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + + /* Default is no multiple-scan output */ + cinfo->scan_info = NULL; + cinfo->num_scans = 0; + + /* Expect normal source image, not raw downsampled data */ + cinfo->raw_data_in = FALSE; + + /* Use Huffman coding, not arithmetic coding, by default */ + cinfo->arith_code = FALSE; + + /* By default, don't do extra passes to optimize entropy coding */ + cinfo->optimize_coding = FALSE; + /* The standard Huffman tables are only valid for 8-bit data precision. + * If the precision is higher, force optimization on so that usable + * tables will be computed. This test can be removed if default tables + * are supplied that are valid for the desired precision. + */ + if (cinfo->data_precision > 8) + cinfo->optimize_coding = TRUE; + + /* By default, use the simpler non-cosited sampling alignment */ + cinfo->CCIR601_sampling = FALSE; + + /* By default, apply fancy downsampling */ + cinfo->do_fancy_downsampling = TRUE; + + /* No input smoothing */ + cinfo->smoothing_factor = 0; + + /* DCT algorithm preference */ + cinfo->dct_method = JDCT_DEFAULT; + + /* No restart markers */ + cinfo->restart_interval = 0; + cinfo->restart_in_rows = 0; + + /* Fill in default JFIF marker parameters. Note that whether the marker + * will actually be written is determined by jpeg_set_colorspace. + * + * By default, the library emits JFIF version code 1.01. + * An application that wants to emit JFIF 1.02 extension markers should set + * JFIF_minor_version to 2. We could probably get away with just defaulting + * to 1.02, but there may still be some decoders in use that will complain + * about that; saying 1.01 should minimize compatibility problems. + */ + cinfo->JFIF_major_version = 1; /* Default JFIF version = 1.01 */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; /* Pixel size is unknown by default */ + cinfo->X_density = 1; /* Pixel aspect ratio is square by default */ + cinfo->Y_density = 1; + + /* Choose JPEG colorspace based on input space, set defaults accordingly */ + + jpeg_default_colorspace(cinfo); +} + + +/* + * Select an appropriate JPEG colorspace for in_color_space. + */ + +GLOBAL(void) +jpeg_default_colorspace (j_compress_ptr cinfo) +{ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + } +} + + +/* + * Set the JPEG colorspace, and choose colorspace-dependent default values. + */ + +GLOBAL(void) +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) +{ + jpeg_component_info * compptr; + int ci; + +#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ + (compptr = &cinfo->comp_info[index], \ + compptr->component_id = (id), \ + compptr->h_samp_factor = (hsamp), \ + compptr->v_samp_factor = (vsamp), \ + compptr->quant_tbl_no = (quant), \ + compptr->dc_tbl_no = (dctbl), \ + compptr->ac_tbl_no = (actbl) ) + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* For all colorspaces, we use Q and Huff tables 0 for luminance components, + * tables 1 for chrominance components. + */ + + cinfo->jpeg_color_space = colorspace; + + cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */ + cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */ + + switch (colorspace) { + case JCS_GRAYSCALE: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 1; + /* JFIF specifies component ID 1 */ + SET_COMP(0, 1, 1,1, 0, 0,0); + break; + case JCS_RGB: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */ + cinfo->num_components = 3; + SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0); + SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0); + SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0); + break; + case JCS_YCbCr: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 3; + /* JFIF specifies component IDs 1,2,3 */ + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + break; + case JCS_CMYK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ + cinfo->num_components = 4; + SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0); + SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0); + SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0); + SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0); + break; + case JCS_YCCK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ + cinfo->num_components = 4; + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); + break; + case JCS_UNKNOWN: + cinfo->num_components = cinfo->input_components; + if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + for (ci = 0; ci < cinfo->num_components; ci++) { + SET_COMP(ci, ci, 1,1, 0, 0,0); + } + break; + default: + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + } +} + + +#ifdef C_PROGRESSIVE_SUPPORTED + +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_scans (jpeg_scan_info * scanptr, int ncomps, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for each component */ +{ + int ci; + + for (ci = 0; ci < ncomps; ci++) { + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) +/* Support routine: generate interleaved DC scan if possible, else N scans */ +{ + int ci; + + if (ncomps <= MAX_COMPS_IN_SCAN) { + /* Single interleaved DC scan */ + scanptr->comps_in_scan = ncomps; + for (ci = 0; ci < ncomps; ci++) + scanptr->component_index[ci] = ci; + scanptr->Ss = scanptr->Se = 0; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } else { + /* Noninterleaved DC scan for each component */ + scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + } + return scanptr; +} + + +/* + * Create a recommended progressive-JPEG script. + * cinfo->num_components and cinfo->jpeg_color_space must be correct. + */ + +GLOBAL(void) +jpeg_simple_progression (j_compress_ptr cinfo) +{ + int ncomps = cinfo->num_components; + int nscans; + jpeg_scan_info * scanptr; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Figure space needed for script. Calculation must match code below! */ + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + nscans = 10; + } else { + /* All-purpose script for other color spaces. */ + if (ncomps > MAX_COMPS_IN_SCAN) + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + else + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + } + + /* Allocate space for script. + * We need to put it in the permanent pool in case the application performs + * multiple compressions without changing the settings. To avoid a memory + * leak if jpeg_simple_progression is called repeatedly for the same JPEG + * object, we try to re-use previously allocated space, and we allocate + * enough space to handle YCbCr even if initially asked for grayscale. + */ + if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { + cinfo->script_space_size = MAX(nscans, 10); + cinfo->script_space = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + cinfo->script_space_size * SIZEOF(jpeg_scan_info)); + } + scanptr = cinfo->script_space; + cinfo->scan_info = scanptr; + cinfo->num_scans = nscans; + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + /* Initial DC scan */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + /* Initial AC scan: get some luma data out in a hurry */ + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + /* Chroma data is too small to be worth expending many scans on */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + /* Complete spectral selection for luma AC */ + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + /* Refine next bit of luma AC */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + /* Finish DC successive approximation */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + /* Finish AC successive approximation */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + /* Luma bottom bit comes last since it's usually largest scan */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + } else { + /* All-purpose script for other color spaces. */ + /* Successive approximation first pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + /* Successive approximation second pass */ + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + /* Successive approximation final pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); + } +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ diff --git a/lib/jpeg/src/jcparam.d b/lib/jpeg/src/jcparam.d new file mode 100644 index 0000000..6ba7aba --- /dev/null +++ b/lib/jpeg/src/jcparam.d @@ -0,0 +1,14 @@ +jcparam.o: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcprepct.c b/lib/jpeg/src/jcprepct.c new file mode 100644 index 0000000..00101e0 --- /dev/null +++ b/lib/jpeg/src/jcprepct.c @@ -0,0 +1,358 @@ +/* + * jcprepct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the compression preprocessing controller. + * This controller manages the color conversion, downsampling, + * and edge expansion steps. + * + * Most of the complexity here is associated with buffering input rows + * as required by the downsampler. See the comments at the head of + * jcsample.c for the downsampler's needs. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* At present, jcsample.c can request context rows only for smoothing. + * In the future, we might also need context rows for CCIR601 sampling + * or other more-complex downsampling procedures. The code to support + * context rows should be compiled only if needed. + */ +#ifdef INPUT_SMOOTHING_SUPPORTED +#define CONTEXT_ROWS_SUPPORTED +#endif + + +/* + * For the simple (no-context-row) case, we just need to buffer one + * row group's worth of pixels for the downsampling step. At the bottom of + * the image, we pad to a full row group by replicating the last pixel row. + * The downsampler's last output row is then replicated if needed to pad + * out to a full iMCU row. + * + * When providing context rows, we must buffer three row groups' worth of + * pixels. Three row groups are physically allocated, but the row pointer + * arrays are made five row groups high, with the extra pointers above and + * below "wrapping around" to point to the last and first real row groups. + * This allows the downsampler to access the proper context rows. + * At the top and bottom of the image, we create dummy context rows by + * copying the first or last real pixel row. This copying could be avoided + * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the + * trouble on the compression side. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_prep_controller pub; /* public fields */ + + /* Downsampling input buffer. This buffer holds color-converted data + * until we have enough to do a downsample step. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + JDIMENSION rows_to_go; /* counts rows remaining in source image */ + int next_buf_row; /* index of next row to store in color_buf */ + +#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */ + int this_row_group; /* starting row index of group to process */ + int next_buf_stop; /* downsample when we reach this index */ +#endif +} my_prep_controller; + +typedef my_prep_controller * my_prep_ptr; + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + if (pass_mode != JBUF_PASS_THRU) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Initialize total-height counter for detecting bottom of image */ + prep->rows_to_go = cinfo->image_height; + /* Mark the conversion buffer empty */ + prep->next_buf_row = 0; +#ifdef CONTEXT_ROWS_SUPPORTED + /* Preset additional state variables for context mode. + * These aren't used in non-context mode, so we needn't test which mode. + */ + prep->this_row_group = 0; + /* Set next_buf_stop to stop after two row groups have been read in. */ + prep->next_buf_stop = 2 * cinfo->max_v_samp_factor; +#endif +} + + +/* + * Expand an image vertically from height input_rows to height output_rows, + * by duplicating the bottom row. + */ + +LOCAL(void) +expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols, + int input_rows, int output_rows) +{ + register int row; + + for (row = input_rows; row < output_rows; row++) { + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + 1, num_cols); + } +} + + +/* + * Process some data in the simple no-context case. + * + * Preprocessor output data is counted in "row groups". A row group + * is defined to be v_samp_factor sample rows of each component. + * Downsampling will produce this much data from each max_v_samp_factor + * input rows. + */ + +METHODDEF(void) +pre_process_data (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + JDIMENSION inrows; + jpeg_component_info * compptr; + + while (*in_row_ctr < in_rows_avail && + *out_row_group_ctr < out_row_groups_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = cinfo->max_v_samp_factor - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + /* If at bottom of image, pad to fill the conversion buffer. */ + if (prep->rows_to_go == 0 && + prep->next_buf_row < cinfo->max_v_samp_factor) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, cinfo->max_v_samp_factor); + } + prep->next_buf_row = cinfo->max_v_samp_factor; + } + /* If we've filled the conversion buffer, empty it. */ + if (prep->next_buf_row == cinfo->max_v_samp_factor) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, (JDIMENSION) 0, + output_buf, *out_row_group_ctr); + prep->next_buf_row = 0; + (*out_row_group_ctr)++; + } + /* If at bottom of image, pad the output to a full iMCU height. + * Note we assume the caller is providing a one-iMCU-height output buffer! + */ + if (prep->rows_to_go == 0 && + *out_row_group_ctr < out_row_groups_avail) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + numrows = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * compptr->DCT_h_scaled_size, + (int) (*out_row_group_ctr * numrows), + (int) (out_row_groups_avail * numrows)); + } + *out_row_group_ctr = out_row_groups_avail; + break; /* can exit outer loop without test */ + } + } +} + + +#ifdef CONTEXT_ROWS_SUPPORTED + +/* + * Process some data in the context case. + */ + +METHODDEF(void) +pre_process_context (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + int buf_height = cinfo->max_v_samp_factor * 3; + JDIMENSION inrows; + + while (*out_row_group_ctr < out_row_groups_avail) { + if (*in_row_ctr < in_rows_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = prep->next_buf_stop - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + /* Pad at top of image, if first time through */ + if (prep->rows_to_go == cinfo->image_height) { + for (ci = 0; ci < cinfo->num_components; ci++) { + int row; + for (row = 1; row <= cinfo->max_v_samp_factor; row++) { + jcopy_sample_rows(prep->color_buf[ci], 0, + prep->color_buf[ci], -row, + 1, cinfo->image_width); + } + } + } + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + } else { + /* Return for more data, unless we are at the bottom of the image. */ + if (prep->rows_to_go != 0) + break; + /* When at bottom of image, pad to fill the conversion buffer. */ + if (prep->next_buf_row < prep->next_buf_stop) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, prep->next_buf_stop); + } + prep->next_buf_row = prep->next_buf_stop; + } + } + /* If we've gotten enough data, downsample a row group. */ + if (prep->next_buf_row == prep->next_buf_stop) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, + (JDIMENSION) prep->this_row_group, + output_buf, *out_row_group_ctr); + (*out_row_group_ctr)++; + /* Advance pointers with wraparound as necessary. */ + prep->this_row_group += cinfo->max_v_samp_factor; + if (prep->this_row_group >= buf_height) + prep->this_row_group = 0; + if (prep->next_buf_row >= buf_height) + prep->next_buf_row = 0; + prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor; + } + } +} + + +/* + * Create the wrapped-around downsampling input buffer needed for context mode. + */ + +LOCAL(void) +create_context_buffer (j_compress_ptr cinfo) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int rgroup_height = cinfo->max_v_samp_factor; + int ci, i; + jpeg_component_info * compptr; + JSAMPARRAY true_buffer, fake_buffer; + + /* Grab enough space for fake row pointers for all the components; + * we need five row groups' worth of pointers for each component. + */ + fake_buffer = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (cinfo->num_components * 5 * rgroup_height) * + SIZEOF(JSAMPROW)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate the actual buffer space (3 row groups) for this component. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + true_buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * + cinfo->min_DCT_h_scaled_size * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) (3 * rgroup_height)); + /* Copy true buffer row pointers into the middle of the fake row array */ + MEMCOPY(fake_buffer + rgroup_height, true_buffer, + 3 * rgroup_height * SIZEOF(JSAMPROW)); + /* Fill in the above and below wraparound pointers */ + for (i = 0; i < rgroup_height; i++) { + fake_buffer[i] = true_buffer[2 * rgroup_height + i]; + fake_buffer[4 * rgroup_height + i] = true_buffer[i]; + } + prep->color_buf[ci] = fake_buffer + rgroup_height; + fake_buffer += 5 * rgroup_height; /* point to space for next component */ + } +} + +#endif /* CONTEXT_ROWS_SUPPORTED */ + + +/* + * Initialize preprocessing controller. + */ + +GLOBAL(void) +jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_prep_ptr prep; + int ci; + jpeg_component_info * compptr; + + if (need_full_buffer) /* safety check */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + prep = (my_prep_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_prep_controller)); + cinfo->prep = (struct jpeg_c_prep_controller *) prep; + prep->pub.start_pass = start_pass_prep; + + /* Allocate the color conversion buffer. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + if (cinfo->downsample->need_context_rows) { + /* Set up to provide context rows */ +#ifdef CONTEXT_ROWS_SUPPORTED + prep->pub.pre_process_data = pre_process_context; + create_context_buffer(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* No context, just make it tall enough for one row group */ + prep->pub.pre_process_data = pre_process_data; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * + cinfo->min_DCT_h_scaled_size * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/lib/jpeg/src/jcprepct.d b/lib/jpeg/src/jcprepct.d new file mode 100644 index 0000000..ad04201 --- /dev/null +++ b/lib/jpeg/src/jcprepct.d @@ -0,0 +1,14 @@ +jcprepct.o: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jcsample.c b/lib/jpeg/src/jcsample.c new file mode 100644 index 0000000..1aef8a6 --- /dev/null +++ b/lib/jpeg/src/jcsample.c @@ -0,0 +1,545 @@ +/* + * jcsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains downsampling routines. + * + * Downsampling input data is counted in "row groups". A row group + * is defined to be max_v_samp_factor pixel rows of each component, + * from which the downsampler produces v_samp_factor sample rows. + * A single row group is processed in each call to the downsampler module. + * + * The downsampler is responsible for edge-expansion of its output data + * to fill an integral number of DCT blocks horizontally. The source buffer + * may be modified if it is helpful for this purpose (the source buffer is + * allocated wide enough to correspond to the desired output width). + * The caller (the prep controller) is responsible for vertical padding. + * + * The downsampler may request "context rows" by setting need_context_rows + * during startup. In this case, the input arrays will contain at least + * one row group's worth of pixels above and below the passed-in data; + * the caller will create dummy rows at image top and bottom by replicating + * the first or last real pixel row. + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + * + * The downsampling algorithm used here is a simple average of the source + * pixels covered by the output pixel. The hi-falutin sampling literature + * refers to this as a "box filter". In general the characteristics of a box + * filter are not very good, but for the specific cases we normally use (1:1 + * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not + * nearly so bad. If you intend to use other sampling ratios, you'd be well + * advised to improve this code. + * + * A simple input-smoothing capability is provided. This is mainly intended + * for cleaning up color-dithered GIF input files (if you find it inadequate, + * we suggest using an external filtering program such as pnmconvol). When + * enabled, each input pixel P is replaced by a weighted sum of itself and its + * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, + * where SF = (smoothing_factor / 1024). + * Currently, smoothing is only supported for 2h2v sampling factors. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to downsample a single component */ +typedef JMETHOD(void, downsample1_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data)); + +/* Private subobject */ + +typedef struct { + struct jpeg_downsampler pub; /* public fields */ + + /* Downsampling method pointers, one per component */ + downsample1_ptr methods[MAX_COMPONENTS]; + + /* Height of an output row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_downsample need not + * recompute them each time. They are unused for other downsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_downsampler; + +typedef my_downsampler * my_downsample_ptr; + + +/* + * Initialize for a downsampling pass. + */ + +METHODDEF(void) +start_pass_downsample (j_compress_ptr cinfo) +{ + /* no work for now */ +} + + +/* + * Expand a component horizontally from width input_cols to width output_cols, + * by duplicating the rightmost samples. + */ + +LOCAL(void) +expand_right_edge (JSAMPARRAY image_data, int num_rows, + JDIMENSION input_cols, JDIMENSION output_cols) +{ + register JSAMPROW ptr; + register JSAMPLE pixval; + register int count; + int row; + int numcols = (int) (output_cols - input_cols); + + if (numcols > 0) { + for (row = 0; row < num_rows; row++) { + ptr = image_data[row] + input_cols; + pixval = ptr[-1]; /* don't need GETJSAMPLE() here */ + for (count = numcols; count > 0; count--) + *ptr++ = pixval; + } + } +} + + +/* + * Do downsampling for a whole row group (all components). + * + * In this version we simply downsample each component independently. + */ + +METHODDEF(void) +sep_downsample (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) +{ + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + int ci; + jpeg_component_info * compptr; + JSAMPARRAY in_ptr, out_ptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + in_ptr = input_buf[ci] + in_row_index; + out_ptr = output_buf[ci] + + (out_row_group_index * downsample->rowgroup_height[ci]); + (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr); + } +} + + +/* + * Downsample pixel values of a single component. + * One row group is processed per call. + * This version handles arbitrary integral sampling ratios, without smoothing. + * Note that this version is not actually used for customary sampling ratios. + */ + +METHODDEF(void) +int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; + JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ + JDIMENSION output_cols = compptr->width_in_blocks * compptr->DCT_h_scaled_size; + JSAMPROW inptr, outptr; + INT32 outvalue; + + h_expand = downsample->h_expand[compptr->component_index]; + v_expand = downsample->v_expand[compptr->component_index]; + numpix = h_expand * v_expand; + numpix2 = numpix/2; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * h_expand); + + inrow = outrow = 0; + while (inrow < cinfo->max_v_samp_factor) { + outptr = output_data[outrow]; + for (outcol = 0, outcol_h = 0; outcol < output_cols; + outcol++, outcol_h += h_expand) { + outvalue = 0; + for (v = 0; v < v_expand; v++) { + inptr = input_data[inrow+v] + outcol_h; + for (h = 0; h < h_expand; h++) { + outvalue += (INT32) GETJSAMPLE(*inptr++); + } + } + *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix); + } + inrow += v_expand; + outrow++; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * without smoothing. + */ + +METHODDEF(void) +fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + /* Copy the data */ + jcopy_sample_rows(input_data, 0, output_data, 0, + cinfo->max_v_samp_factor, cinfo->image_width); + /* Edge-expand */ + expand_right_edge(output_data, cinfo->max_v_samp_factor, cinfo->image_width, + compptr->width_in_blocks * compptr->DCT_h_scaled_size); +} + + +/* + * Downsample pixel values of a single component. + * This version handles the common case of 2:1 horizontal and 1:1 vertical, + * without smoothing. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * compptr->DCT_h_scaled_size; + register JSAMPROW inptr, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + outptr = output_data[inrow]; + inptr = input_data[inrow]; + bias = 0; /* bias = 0,1,0,1,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1]) + + bias) >> 1); + bias ^= 1; /* 0=>1, 1=>0 */ + inptr += 2; + } + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * without smoothing. + */ + +METHODDEF(void) +h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * compptr->DCT_h_scaled_size; + register JSAMPROW inptr0, inptr1, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + inrow = outrow = 0; + while (inrow < cinfo->max_v_samp_factor) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + bias = 1; /* bias = 1,2,1,2,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]) + + bias) >> 2); + bias ^= 3; /* 1=>2, 2=>1 */ + inptr0 += 2; inptr1 += 2; + } + inrow += 2; + outrow++; + } +} + + +#ifdef INPUT_SMOOTHING_SUPPORTED + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * compptr->DCT_h_scaled_size; + register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols * 2); + + /* We don't bother to form the individual "smoothed" input pixel values; + * we can directly compute the output which is the average of the four + * smoothed values. Each of the four member pixels contributes a fraction + * (1-8*SF) to its own smoothed image and a fraction SF to each of the three + * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final + * output. The four corner-adjacent neighbor pixels contribute a fraction + * SF to just one smoothed pixel, or SF/4 to the final output; while the + * eight edge-adjacent neighbors contribute SF to each of two smoothed + * pixels, or SF/2 overall. In order to use integer arithmetic, these + * factors are scaled by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */ + neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */ + + inrow = outrow = 0; + while (inrow < cinfo->max_v_samp_factor) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + above_ptr = input_data[inrow-1]; + below_ptr = input_data[inrow+2]; + + /* Special case for first column: pretend column -1 is same as column 0 */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]); + neighsum += neighsum; + neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + /* sum of pixels directly mapped to this output element */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + /* sum of edge-neighbor pixels */ + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]); + /* The edge-neighbors count twice as much as corner-neighbors */ + neighsum += neighsum; + /* Add in the corner-neighbors */ + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]); + /* form final output scaled up by 2^16 */ + membersum = membersum * memberscale + neighsum * neighscale; + /* round, descale and output it */ + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]); + neighsum += neighsum; + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + inrow += 2; + outrow++; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * compptr->DCT_h_scaled_size; + register JSAMPROW inptr, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + int colsum, lastcolsum, nextcolsum; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols); + + /* Each of the eight neighbor pixels contributes a fraction SF to the + * smoothed pixel, while the main pixel contributes (1-8*SF). In order + * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */ + neighscale = cinfo->smoothing_factor * 64; /* scaled SF */ + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + outptr = output_data[inrow]; + inptr = input_data[inrow]; + above_ptr = input_data[inrow-1]; + below_ptr = input_data[inrow+1]; + + /* Special case for first column */ + colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) + + GETJSAMPLE(*inptr); + membersum = GETJSAMPLE(*inptr++); + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = colsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + membersum = GETJSAMPLE(*inptr++); + above_ptr++; below_ptr++; + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + colsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + } +} + +#endif /* INPUT_SMOOTHING_SUPPORTED */ + + +/* + * Module initialization routine for downsampling. + * Note that we must select a routine for each component. + */ + +GLOBAL(void) +jinit_downsampler (j_compress_ptr cinfo) +{ + my_downsample_ptr downsample; + int ci; + jpeg_component_info * compptr; + boolean smoothok = TRUE; + int h_in_group, v_in_group, h_out_group, v_out_group; + + downsample = (my_downsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_downsampler)); + cinfo->downsample = (struct jpeg_downsampler *) downsample; + downsample->pub.start_pass = start_pass_downsample; + downsample->pub.downsample = sep_downsample; + downsample->pub.need_context_rows = FALSE; + + if (cinfo->CCIR601_sampling) + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* Verify we can handle the sampling factors, and set up method pointers */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "output group" for DCT scaling. This many samples + * are to be converted from max_h_samp_factor * max_v_samp_factor pixels. + */ + h_out_group = (compptr->h_samp_factor * compptr->DCT_h_scaled_size) / + cinfo->min_DCT_h_scaled_size; + v_out_group = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; + h_in_group = cinfo->max_h_samp_factor; + v_in_group = cinfo->max_v_samp_factor; + downsample->rowgroup_height[ci] = v_out_group; /* save for use later */ + if (h_in_group == h_out_group && v_in_group == v_out_group) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = fullsize_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = fullsize_downsample; + } else if (h_in_group == h_out_group * 2 && + v_in_group == v_out_group) { + smoothok = FALSE; + downsample->methods[ci] = h2v1_downsample; + } else if (h_in_group == h_out_group * 2 && + v_in_group == v_out_group * 2) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = h2v2_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = h2v2_downsample; + } else if ((h_in_group % h_out_group) == 0 && + (v_in_group % v_out_group) == 0) { + smoothok = FALSE; + downsample->methods[ci] = int_downsample; + downsample->h_expand[ci] = (UINT8) (h_in_group / h_out_group); + downsample->v_expand[ci] = (UINT8) (v_in_group / v_out_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + } + +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor && !smoothok) + TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); +#endif +} diff --git a/lib/jpeg/src/jcsample.d b/lib/jpeg/src/jcsample.d new file mode 100644 index 0000000..31f6cb2 --- /dev/null +++ b/lib/jpeg/src/jcsample.d @@ -0,0 +1,14 @@ +jcsample.o: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jctrans.c b/lib/jpeg/src/jctrans.c new file mode 100644 index 0000000..7623790 --- /dev/null +++ b/lib/jpeg/src/jctrans.c @@ -0,0 +1,382 @@ +/* + * jctrans.c + * + * Copyright (C) 1995-1998, Thomas G. Lane. + * Modified 2000-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding compression, + * that is, writing raw DCT coefficient arrays to an output JPEG file. + * The routines in jcapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transencode_master_selection + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); +LOCAL(void) transencode_coef_controller + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + + +/* + * Compression initialization for writing raw-coefficient data. + * Before calling this, all parameters and a data destination must be set up. + * Call jpeg_finish_compress() to actually write the data. + * + * The number of passed virtual arrays must match cinfo->num_components. + * Note that the virtual arrays need not be filled or even realized at + * the time write_coefficients is called; indeed, if the virtual arrays + * were requested from this compression object's memory manager, they + * typically will be realized during this routine and filled afterwards. + */ + +GLOBAL(void) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Mark all tables to be written */ + jpeg_suppress_tables(cinfo, FALSE); + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + transencode_master_selection(cinfo, coef_arrays); + /* Wait for jpeg_finish_compress() call */ + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + cinfo->global_state = CSTATE_WRCOEFS; +} + + +/* + * Initialize the compression object with default parameters, + * then copy from the source object all parameters needed for lossless + * transcoding. Parameters that can be varied without loss (such as + * scan script and Huffman optimization) are left in their default states. + */ + +GLOBAL(void) +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo) +{ + JQUANT_TBL ** qtblptr; + jpeg_component_info *incomp, *outcomp; + JQUANT_TBL *c_quant, *slot_quant; + int tblno, ci, coefi; + + /* Safety check to ensure start_compress not called yet. */ + if (dstinfo->global_state != CSTATE_START) + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + /* Copy fundamental image dimensions */ + dstinfo->image_width = srcinfo->image_width; + dstinfo->image_height = srcinfo->image_height; + dstinfo->input_components = srcinfo->num_components; + dstinfo->in_color_space = srcinfo->jpeg_color_space; + dstinfo->jpeg_width = srcinfo->output_width; + dstinfo->jpeg_height = srcinfo->output_height; + dstinfo->min_DCT_h_scaled_size = srcinfo->min_DCT_h_scaled_size; + dstinfo->min_DCT_v_scaled_size = srcinfo->min_DCT_v_scaled_size; + /* Initialize all parameters to default values */ + jpeg_set_defaults(dstinfo); + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + * Fix it to get the right header markers for the image colorspace. + */ + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + dstinfo->data_precision = srcinfo->data_precision; + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + /* Copy the source's quantization tables. */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + MEMCOPY((*qtblptr)->quantval, + srcinfo->quant_tbl_ptrs[tblno]->quantval, + SIZEOF((*qtblptr)->quantval)); + (*qtblptr)->sent_table = FALSE; + } + } + /* Copy the source's per-component info. + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + */ + dstinfo->num_components = srcinfo->num_components; + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + MAX_COMPONENTS); + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + outcomp->component_id = incomp->component_id; + outcomp->h_samp_factor = incomp->h_samp_factor; + outcomp->v_samp_factor = incomp->v_samp_factor; + outcomp->quant_tbl_no = incomp->quant_tbl_no; + /* Make sure saved quantization table for component matches the qtable + * slot. If not, the input file re-used this qtable slot. + * IJG encoder currently cannot duplicate this. + */ + tblno = outcomp->quant_tbl_no; + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + srcinfo->quant_tbl_ptrs[tblno] == NULL) + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + c_quant = incomp->quant_table; + if (c_quant != NULL) { + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + } + } + /* Note: we do not copy the source's Huffman table assignments; + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + */ + } + /* Also copy JFIF version and resolution information, if available. + * Strictly speaking this isn't "critical" info, but it's nearly + * always appropriate to copy it if available. In particular, + * if the application chooses to copy JFIF 1.02 extension markers from + * the source file, we need to copy the version to make sure we don't + * emit a file that has 1.02 extensions but a claimed version of 1.01. + * We will *not*, however, copy version info from mislabeled "2.01" files. + */ + if (srcinfo->saw_JFIF_marker) { + if (srcinfo->JFIF_major_version == 1) { + dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; + dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; + } + dstinfo->density_unit = srcinfo->density_unit; + dstinfo->X_density = srcinfo->X_density; + dstinfo->Y_density = srcinfo->Y_density; + } +} + + +/* + * Master selection of compression modules for transcoding. + * This substitutes for jcinit.c's initialization of the full compressor. + */ + +LOCAL(void) +transencode_master_selection (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) + jinit_arith_encoder(cinfo); + else { + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI, JFIF) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} + + +/* + * The rest of this file is a special implementation of the coefficient + * buffer controller. This is similar to jccoefct.c, but it handles only + * output from presupplied virtual arrays. Furthermore, we generate any + * dummy padding blocks on-the-fly rather than expecting them to be present + * in the arrays. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* Virtual block array for each component. */ + jvirt_barray_ptr * whole_image; + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + if (pass_mode != JBUF_CRANK_DEST) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); +} + + +/* + * Process some data. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, blockcnt; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yindex+yoffset < compptr->last_row_height) { + /* Fill in pointers to real blocks in this row */ + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < blockcnt; xindex++) + MCU_buffer[blkn++] = buffer_ptr++; + } else { + /* At bottom of image, need a whole row of dummy blocks */ + xindex = 0; + } + /* Fill in any dummy blocks needed in this row. + * Dummy blocks are filled in the same way as in jccoefct.c: + * all zeroes in the AC entries, DC entries equal to previous + * block's DC value. The init routine has already zeroed the + * AC entries, so we need only set the DC entries correctly. + */ + for (; xindex < compptr->MCU_width; xindex++) { + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + blkn++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +/* + * Initialize coefficient buffer controller. + * + * Each passed coefficient array must be the right size for that + * coefficient: width_in_blocks wide and height_in_blocks high, + * with unitheight at least v_samp_factor. + */ + +LOCAL(void) +transencode_coef_controller (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + my_coef_ptr coef; + JBLOCKROW buffer; + int i; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; + + /* Save pointer to virtual arrays */ + coef->whole_image = coef_arrays; + + /* Allocate and pre-zero space for dummy DCT blocks. */ + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->dummy_buffer[i] = buffer + i; + } +} diff --git a/lib/jpeg/src/jctrans.d b/lib/jpeg/src/jctrans.d new file mode 100644 index 0000000..de51d4e --- /dev/null +++ b/lib/jpeg/src/jctrans.d @@ -0,0 +1,14 @@ +jctrans.o: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdapimin.c b/lib/jpeg/src/jdapimin.c new file mode 100644 index 0000000..65f8a49 --- /dev/null +++ b/lib/jpeg/src/jdapimin.c @@ -0,0 +1,396 @@ +/* + * jdapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-decompression case or the + * transcoding-only case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + * shared by compression and decompression, and jdtrans.c for the transcoding + * case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_decompress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + cinfo->marker_list = NULL; + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL(void) +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Set default decompression parameters. + */ + +LOCAL(void) +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = cinfo->block_size; /* 1:1 scaling */ + cinfo->scale_denom = cinfo->block_size; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL(int) +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL(int) +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + + +/* + * Have we finished reading the input file? + */ + +GLOBAL(boolean) +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + + +/* + * Is there more than one scan? + */ + +GLOBAL(boolean) +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} diff --git a/lib/jpeg/src/jdapimin.d b/lib/jpeg/src/jdapimin.d new file mode 100644 index 0000000..32c33e7 --- /dev/null +++ b/lib/jpeg/src/jdapimin.d @@ -0,0 +1,14 @@ +jdapimin.o: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdapistd.c b/lib/jpeg/src/jdapistd.c new file mode 100644 index 0000000..e81bd67 --- /dev/null +++ b/lib/jpeg/src/jdapistd.c @@ -0,0 +1,275 @@ +/* + * jdapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-decompression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_decompress, it will end up linking in the entire decompressor. + * We thus must separate this file from jdapimin.c to avoid linking the + * whole decompression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(boolean) output_pass_setup JPP((j_decompress_ptr cinfo)); + + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL(boolean) +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL(JDIMENSION) +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_v_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL(boolean) +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ diff --git a/lib/jpeg/src/jdapistd.d b/lib/jpeg/src/jdapistd.d new file mode 100644 index 0000000..a985b8a --- /dev/null +++ b/lib/jpeg/src/jdapistd.d @@ -0,0 +1,14 @@ +jdapistd.o: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdarith.c b/lib/jpeg/src/jdarith.c new file mode 100644 index 0000000..478c37d --- /dev/null +++ b/lib/jpeg/src/jdarith.c @@ -0,0 +1,772 @@ +/* + * jdarith.c + * + * Developed 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains portable arithmetic entropy decoding routines for JPEG + * (implementing the ISO/IEC IS 10918-1 and CCITT Recommendation ITU-T T.81). + * + * Both sequential and progressive modes are supported in this single module. + * + * Suspension is not currently supported in this module. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Expanded entropy decoder object for arithmetic decoding. */ + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + INT32 c; /* C register, base of coding interval + input bit buffer */ + INT32 a; /* A register, normalized size of coding interval */ + int ct; /* bit shift counter, # of bits left in bit buffer part of C */ + /* init: ct = -16 */ + /* run: ct = 0..7 */ + /* error: ct = -1 */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + int dc_context[MAX_COMPS_IN_SCAN]; /* context index for DC conditioning */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to statistics areas (these workspaces have image lifespan) */ + unsigned char * dc_stats[NUM_ARITH_TBLS]; + unsigned char * ac_stats[NUM_ARITH_TBLS]; + + /* Statistics bin for coding with fixed probability 0.5 */ + unsigned char fixed_bin[4]; +} arith_entropy_decoder; + +typedef arith_entropy_decoder * arith_entropy_ptr; + +/* The following two definitions specify the allocation chunk size + * for the statistics area. + * According to sections F.1.4.4.1.3 and F.1.4.4.2, we need at least + * 49 statistics bins for DC, and 245 statistics bins for AC coding. + * + * We use a compact representation with 1 byte per statistics bin, + * thus the numbers directly represent byte sizes. + * This 1 byte per statistics bin contains the meaning of the MPS + * (more probable symbol) in the highest bit (mask 0x80), and the + * index into the probability estimation state machine table + * in the lower bits (mask 0x7F). + */ + +#define DC_STAT_BINS 64 +#define AC_STAT_BINS 256 + + +LOCAL(int) +get_byte (j_decompress_ptr cinfo) +/* Read next input byte; we do not support suspension in this module. */ +{ + struct jpeg_source_mgr * src = cinfo->src; + + if (src->bytes_in_buffer == 0) + if (! (*src->fill_input_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + src->bytes_in_buffer--; + return GETJOCTET(*src->next_input_byte++); +} + + +/* + * The core arithmetic decoding routine (common in JPEG and JBIG). + * This needs to go as fast as possible. + * Machine-dependent optimization facilities + * are not utilized in this portable implementation. + * However, this code should be fairly efficient and + * may be a good base for further optimizations anyway. + * + * Return value is 0 or 1 (binary decision). + * + * Note: I've changed the handling of the code base & bit + * buffer register C compared to other implementations + * based on the standards layout & procedures. + * While it also contains both the actual base of the + * coding interval (16 bits) and the next-bits buffer, + * the cut-point between these two parts is floating + * (instead of fixed) with the bit shift counter CT. + * Thus, we also need only one (variable instead of + * fixed size) shift for the LPS/MPS decision, and + * we can get away with any renormalization update + * of C (except for new data insertion, of course). + * + * I've also introduced a new scheme for accessing + * the probability estimation state machine table, + * derived from Markus Kuhn's JBIG implementation. + */ + +LOCAL(int) +arith_decode (j_decompress_ptr cinfo, unsigned char *st) +{ + register arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; + register unsigned char nl, nm; + register INT32 qe, temp; + register int sv, data; + + /* Renormalization & data input per section D.2.6 */ + while (e->a < 0x8000L) { + if (--e->ct < 0) { + /* Need to fetch next data byte */ + if (cinfo->unread_marker) + data = 0; /* stuff zero data */ + else { + data = get_byte(cinfo); /* read next input byte */ + if (data == 0xFF) { /* zero stuff or marker code */ + do data = get_byte(cinfo); + while (data == 0xFF); /* swallow extra 0xFF bytes */ + if (data == 0) + data = 0xFF; /* discard stuffed zero byte */ + else { + /* Note: Different from the Huffman decoder, hitting + * a marker while processing the compressed data + * segment is legal in arithmetic coding. + * The convention is to supply zero data + * then until decoding is complete. + */ + cinfo->unread_marker = data; + data = 0; + } + } + } + e->c = (e->c << 8) | data; /* insert data into C register */ + if ((e->ct += 8) < 0) /* update bit shift counter */ + /* Need more initial bytes */ + if (++e->ct == 0) + /* Got 2 initial bytes -> re-init A and exit loop */ + e->a = 0x8000L; /* => e->a = 0x10000L after loop exit */ + } + e->a <<= 1; + } + + /* Fetch values from our compact representation of Table D.2: + * Qe values and probability estimation state machine + */ + sv = *st; + qe = jpeg_aritab[sv & 0x7F]; /* => Qe_Value */ + nl = qe & 0xFF; qe >>= 8; /* Next_Index_LPS + Switch_MPS */ + nm = qe & 0xFF; qe >>= 8; /* Next_Index_MPS */ + + /* Decode & estimation procedures per sections D.2.4 & D.2.5 */ + temp = e->a - qe; + e->a = temp; + temp <<= e->ct; + if (e->c >= temp) { + e->c -= temp; + /* Conditional LPS (less probable symbol) exchange */ + if (e->a < qe) { + e->a = qe; + *st = (sv & 0x80) ^ nm; /* Estimate_after_MPS */ + } else { + e->a = qe; + *st = (sv & 0x80) ^ nl; /* Estimate_after_LPS */ + sv ^= 0x80; /* Exchange LPS/MPS */ + } + } else if (e->a < 0x8000L) { + /* Conditional MPS (more probable symbol) exchange */ + if (e->a < qe) { + *st = (sv & 0x80) ^ nl; /* Estimate_after_LPS */ + sv ^= 0x80; /* Exchange LPS/MPS */ + } else { + *st = (sv & 0x80) ^ nm; /* Estimate_after_MPS */ + } + } + + return sv >> 7; +} + + +/* + * Check for a restart marker & resynchronize decoder. + */ + +LOCAL(void) +process_restart (j_decompress_ptr cinfo) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + int ci; + jpeg_component_info * compptr; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Re-initialize statistics areas */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + if (! cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { + MEMZERO(entropy->dc_stats[compptr->dc_tbl_no], DC_STAT_BINS); + /* Reset DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + entropy->dc_context[ci] = 0; + } + if ((! cinfo->progressive_mode && cinfo->lim_Se) || + (cinfo->progressive_mode && cinfo->Ss)) { + MEMZERO(entropy->ac_stats[compptr->ac_tbl_no], AC_STAT_BINS); + } + } + + /* Reset arithmetic decoding variables */ + entropy->c = 0; + entropy->a = 0; + entropy->ct = -16; /* force reading 2 initial bytes to fill C */ + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Arithmetic MCU decoding. + * Each of these routines decodes and returns one MCU's worth of + * arithmetic-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + */ + +/* + * MCU decoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + unsigned char *st; + int blkn, ci, tbl, sign; + int v, m; + + /* Process restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + process_restart(cinfo); + entropy->restarts_to_go--; + } + + if (entropy->ct == -1) return TRUE; /* if error do nothing */ + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + tbl = cinfo->cur_comp_info[ci]->dc_tbl_no; + + /* Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients */ + + /* Table F.4: Point to statistics bin S0 for DC coefficient coding */ + st = entropy->dc_stats[tbl] + entropy->dc_context[ci]; + + /* Figure F.19: Decode_DC_DIFF */ + if (arith_decode(cinfo, st) == 0) + entropy->dc_context[ci] = 0; + else { + /* Figure F.21: Decoding nonzero value v */ + /* Figure F.22: Decoding the sign of v */ + sign = arith_decode(cinfo, st + 1); + st += 2; st += sign; + /* Figure F.23: Decoding the magnitude category of v */ + if ((m = arith_decode(cinfo, st)) != 0) { + st = entropy->dc_stats[tbl] + 20; /* Table F.4: X1 = 20 */ + while (arith_decode(cinfo, st)) { + if ((m <<= 1) == 0x8000) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* magnitude overflow */ + return TRUE; + } + st += 1; + } + } + /* Section F.1.4.4.1.2: Establish dc_context conditioning category */ + if (m < (int) ((1L << cinfo->arith_dc_L[tbl]) >> 1)) + entropy->dc_context[ci] = 0; /* zero diff category */ + else if (m > (int) ((1L << cinfo->arith_dc_U[tbl]) >> 1)) + entropy->dc_context[ci] = 12 + (sign * 4); /* large diff category */ + else + entropy->dc_context[ci] = 4 + (sign * 4); /* small diff category */ + v = m; + /* Figure F.24: Decoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + if (arith_decode(cinfo, st)) v |= m; + v += 1; if (sign) v = -v; + entropy->last_dc_val[ci] += v; + } + + /* Scale and output the DC coefficient (assumes jpeg_natural_order[0]=0) */ + (*block)[0] = (JCOEF) (entropy->last_dc_val[ci] << cinfo->Al); + } + + return TRUE; +} + + +/* + * MCU decoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + unsigned char *st; + int tbl, sign, k; + int v, m; + const int * natural_order; + + /* Process restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + process_restart(cinfo); + entropy->restarts_to_go--; + } + + if (entropy->ct == -1) return TRUE; /* if error do nothing */ + + natural_order = cinfo->natural_order; + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = cinfo->cur_comp_info[0]->ac_tbl_no; + + /* Sections F.2.4.2 & F.1.4.4.2: Decoding of AC coefficients */ + + /* Figure F.20: Decode_AC_coefficients */ + for (k = cinfo->Ss; k <= cinfo->Se; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + if (arith_decode(cinfo, st)) break; /* EOB flag */ + while (arith_decode(cinfo, st + 1) == 0) { + st += 3; k++; + if (k > cinfo->Se) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* spectral overflow */ + return TRUE; + } + } + /* Figure F.21: Decoding nonzero value v */ + /* Figure F.22: Decoding the sign of v */ + sign = arith_decode(cinfo, entropy->fixed_bin); + st += 2; + /* Figure F.23: Decoding the magnitude category of v */ + if ((m = arith_decode(cinfo, st)) != 0) { + if (arith_decode(cinfo, st)) { + m <<= 1; + st = entropy->ac_stats[tbl] + + (k <= cinfo->arith_ac_K[tbl] ? 189 : 217); + while (arith_decode(cinfo, st)) { + if ((m <<= 1) == 0x8000) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* magnitude overflow */ + return TRUE; + } + st += 1; + } + } + } + v = m; + /* Figure F.24: Decoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + if (arith_decode(cinfo, st)) v |= m; + v += 1; if (sign) v = -v; + /* Scale and output coefficient in natural (dezigzagged) order */ + (*block)[natural_order[k]] = (JCOEF) (v << cinfo->Al); + } + + return TRUE; +} + + +/* + * MCU decoding for DC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + unsigned char *st; + int p1, blkn; + + /* Process restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + process_restart(cinfo); + entropy->restarts_to_go--; + } + + st = entropy->fixed_bin; /* use fixed probability estimation */ + p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + /* Encoded data is simply the next bit of the two's-complement DC value */ + if (arith_decode(cinfo, st)) + MCU_data[blkn][0][0] |= p1; + } + + return TRUE; +} + + +/* + * MCU decoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + JBLOCKROW block; + JCOEFPTR thiscoef; + unsigned char *st; + int tbl, k, kex; + int p1, m1; + const int * natural_order; + + /* Process restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + process_restart(cinfo); + entropy->restarts_to_go--; + } + + if (entropy->ct == -1) return TRUE; /* if error do nothing */ + + natural_order = cinfo->natural_order; + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = cinfo->cur_comp_info[0]->ac_tbl_no; + + p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + + /* Establish EOBx (previous stage end-of-block) index */ + for (kex = cinfo->Se; kex > 0; kex--) + if ((*block)[natural_order[kex]]) break; + + for (k = cinfo->Ss; k <= cinfo->Se; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + if (k > kex) + if (arith_decode(cinfo, st)) break; /* EOB flag */ + for (;;) { + thiscoef = *block + natural_order[k]; + if (*thiscoef) { /* previously nonzero coef */ + if (arith_decode(cinfo, st + 2)) { + if (*thiscoef < 0) + *thiscoef += m1; + else + *thiscoef += p1; + } + break; + } + if (arith_decode(cinfo, st + 1)) { /* newly nonzero coef */ + if (arith_decode(cinfo, entropy->fixed_bin)) + *thiscoef = m1; + else + *thiscoef = p1; + break; + } + st += 3; k++; + if (k > cinfo->Se) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* spectral overflow */ + return TRUE; + } + } + } + + return TRUE; +} + + +/* + * Decode one MCU's worth of arithmetic-compressed coefficients. + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + jpeg_component_info * compptr; + JBLOCKROW block; + unsigned char *st; + int blkn, ci, tbl, sign, k; + int v, m; + const int * natural_order; + + /* Process restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + process_restart(cinfo); + entropy->restarts_to_go--; + } + + if (entropy->ct == -1) return TRUE; /* if error do nothing */ + + natural_order = cinfo->natural_order; + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients */ + + tbl = compptr->dc_tbl_no; + + /* Table F.4: Point to statistics bin S0 for DC coefficient coding */ + st = entropy->dc_stats[tbl] + entropy->dc_context[ci]; + + /* Figure F.19: Decode_DC_DIFF */ + if (arith_decode(cinfo, st) == 0) + entropy->dc_context[ci] = 0; + else { + /* Figure F.21: Decoding nonzero value v */ + /* Figure F.22: Decoding the sign of v */ + sign = arith_decode(cinfo, st + 1); + st += 2; st += sign; + /* Figure F.23: Decoding the magnitude category of v */ + if ((m = arith_decode(cinfo, st)) != 0) { + st = entropy->dc_stats[tbl] + 20; /* Table F.4: X1 = 20 */ + while (arith_decode(cinfo, st)) { + if ((m <<= 1) == 0x8000) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* magnitude overflow */ + return TRUE; + } + st += 1; + } + } + /* Section F.1.4.4.1.2: Establish dc_context conditioning category */ + if (m < (int) ((1L << cinfo->arith_dc_L[tbl]) >> 1)) + entropy->dc_context[ci] = 0; /* zero diff category */ + else if (m > (int) ((1L << cinfo->arith_dc_U[tbl]) >> 1)) + entropy->dc_context[ci] = 12 + (sign * 4); /* large diff category */ + else + entropy->dc_context[ci] = 4 + (sign * 4); /* small diff category */ + v = m; + /* Figure F.24: Decoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + if (arith_decode(cinfo, st)) v |= m; + v += 1; if (sign) v = -v; + entropy->last_dc_val[ci] += v; + } + + (*block)[0] = (JCOEF) entropy->last_dc_val[ci]; + + /* Sections F.2.4.2 & F.1.4.4.2: Decoding of AC coefficients */ + + tbl = compptr->ac_tbl_no; + + /* Figure F.20: Decode_AC_coefficients */ + for (k = 1; k <= cinfo->lim_Se; k++) { + st = entropy->ac_stats[tbl] + 3 * (k - 1); + if (arith_decode(cinfo, st)) break; /* EOB flag */ + while (arith_decode(cinfo, st + 1) == 0) { + st += 3; k++; + if (k > cinfo->lim_Se) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* spectral overflow */ + return TRUE; + } + } + /* Figure F.21: Decoding nonzero value v */ + /* Figure F.22: Decoding the sign of v */ + sign = arith_decode(cinfo, entropy->fixed_bin); + st += 2; + /* Figure F.23: Decoding the magnitude category of v */ + if ((m = arith_decode(cinfo, st)) != 0) { + if (arith_decode(cinfo, st)) { + m <<= 1; + st = entropy->ac_stats[tbl] + + (k <= cinfo->arith_ac_K[tbl] ? 189 : 217); + while (arith_decode(cinfo, st)) { + if ((m <<= 1) == 0x8000) { + WARNMS(cinfo, JWRN_ARITH_BAD_CODE); + entropy->ct = -1; /* magnitude overflow */ + return TRUE; + } + st += 1; + } + } + } + v = m; + /* Figure F.24: Decoding the magnitude bit pattern of v */ + st += 14; + while (m >>= 1) + if (arith_decode(cinfo, st)) v |= m; + v += 1; if (sign) v = -v; + (*block)[natural_order[k]] = (JCOEF) v; + } + } + + return TRUE; +} + + +/* + * Initialize for an arithmetic-compressed scan. + */ + +METHODDEF(void) +start_pass (j_decompress_ptr cinfo) +{ + arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; + int ci, tbl; + jpeg_component_info * compptr; + + if (cinfo->progressive_mode) { + /* Validate progressive scan parameters */ + if (cinfo->Ss == 0) { + if (cinfo->Se != 0) + goto bad; + } else { + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + if (cinfo->Se < cinfo->Ss || cinfo->Se > cinfo->lim_Se) + goto bad; + /* AC scans may have only one component */ + if (cinfo->comps_in_scan != 1) + goto bad; + } + if (cinfo->Ah != 0) { + /* Successive approximation refinement scan: must have Al = Ah-1. */ + if (cinfo->Ah-1 != cinfo->Al) + goto bad; + } + if (cinfo->Al > 13) { /* need not check for < 0 */ + bad: + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + } + /* Update progression status, and verify that scan order is legal. + * Note that inter-scan inconsistencies are treated as warnings + * not fatal errors ... not clear if this is right way to behave. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + int coefi, cindex = cinfo->cur_comp_info[ci]->component_index; + int *coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + if (cinfo->Ss && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + if (cinfo->Ah != expected) + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr[coefi] = cinfo->Al; + } + } + /* Select MCU decoding routine */ + if (cinfo->Ah == 0) { + if (cinfo->Ss == 0) + entropy->pub.decode_mcu = decode_mcu_DC_first; + else + entropy->pub.decode_mcu = decode_mcu_AC_first; + } else { + if (cinfo->Ss == 0) + entropy->pub.decode_mcu = decode_mcu_DC_refine; + else + entropy->pub.decode_mcu = decode_mcu_AC_refine; + } + } else { + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning. + */ + if (cinfo->Ss != 0 || cinfo->Ah != 0 || cinfo->Al != 0 || + (cinfo->Se < DCTSIZE2 && cinfo->Se != cinfo->lim_Se)) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + /* Select MCU decoding routine */ + entropy->pub.decode_mcu = decode_mcu; + } + + /* Allocate & initialize requested statistics areas */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + if (! cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { + tbl = compptr->dc_tbl_no; + if (tbl < 0 || tbl >= NUM_ARITH_TBLS) + ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); + if (entropy->dc_stats[tbl] == NULL) + entropy->dc_stats[tbl] = (unsigned char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, DC_STAT_BINS); + MEMZERO(entropy->dc_stats[tbl], DC_STAT_BINS); + /* Initialize DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + entropy->dc_context[ci] = 0; + } + if ((! cinfo->progressive_mode && cinfo->lim_Se) || + (cinfo->progressive_mode && cinfo->Ss)) { + tbl = compptr->ac_tbl_no; + if (tbl < 0 || tbl >= NUM_ARITH_TBLS) + ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); + if (entropy->ac_stats[tbl] == NULL) + entropy->ac_stats[tbl] = (unsigned char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, AC_STAT_BINS); + MEMZERO(entropy->ac_stats[tbl], AC_STAT_BINS); + } + } + + /* Initialize arithmetic decoding variables */ + entropy->c = 0; + entropy->a = 0; + entropy->ct = -16; /* force reading 2 initial bytes to fill C */ + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Module initialization routine for arithmetic entropy decoding. + */ + +GLOBAL(void) +jinit_arith_decoder (j_decompress_ptr cinfo) +{ + arith_entropy_ptr entropy; + int i; + + entropy = (arith_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(arith_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + entropy->dc_stats[i] = NULL; + entropy->ac_stats[i] = NULL; + } + + /* Initialize index for fixed probability estimation */ + entropy->fixed_bin[0] = 113; + + if (cinfo->progressive_mode) { + /* Create progression status table */ + int *coef_bit_ptr, ci; + cinfo->coef_bits = (int (*)[DCTSIZE2]) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + coef_bit_ptr = & cinfo->coef_bits[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (i = 0; i < DCTSIZE2; i++) + *coef_bit_ptr++ = -1; + } +} diff --git a/lib/jpeg/src/jdarith.d b/lib/jpeg/src/jdarith.d new file mode 100644 index 0000000..4e7c7a2 --- /dev/null +++ b/lib/jpeg/src/jdarith.d @@ -0,0 +1,14 @@ +jdarith.o: jdarith.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdatadst.c b/lib/jpeg/src/jdatadst.c new file mode 100644 index 0000000..0e9f14b --- /dev/null +++ b/lib/jpeg/src/jdatadst.c @@ -0,0 +1,267 @@ +/* + * jdatadst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains compression data destination routines for the case of + * emitting JPEG data to memory or to a file (or any stdio stream). + * While these routines are sufficient for most applications, + * some will want to use a different destination manager. + * IMPORTANT: we assume that fwrite() will correctly transcribe an array of + * JOCTETs into 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + FILE * outfile; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + + +/* Expanded data destination object for memory output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + unsigned char ** outbuffer; /* target buffer */ + unsigned long * outsize; + unsigned char * newbuffer; /* newly allocated buffer */ + JOCTET * buffer; /* start of buffer */ + size_t bufsize; +} my_mem_destination_mgr; + +typedef my_mem_destination_mgr * my_mem_dest_ptr; + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + +METHODDEF(void) +init_mem_destination (j_compress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) != + (size_t) OUTPUT_BUF_SIZE) + ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + +METHODDEF(boolean) +empty_mem_output_buffer (j_compress_ptr cinfo) +{ + size_t nextsize; + JOCTET * nextbuffer; + my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest; + + /* Try to allocate new buffer with double size */ + nextsize = dest->bufsize * 2; + nextbuffer = malloc(nextsize); + + if (nextbuffer == NULL) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); + + MEMCOPY(nextbuffer, dest->buffer, dest->bufsize); + + if (dest->newbuffer != NULL) + free(dest->newbuffer); + + dest->newbuffer = nextbuffer; + + dest->pub.next_output_byte = nextbuffer + dest->bufsize; + dest->pub.free_in_buffer = dest->bufsize; + + dest->buffer = nextbuffer; + dest->bufsize = nextsize; + + return TRUE; +} + + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount) + ERREXIT(cinfo, JERR_FILE_WRITE); + } + fflush(dest->outfile); + /* Make sure we wrote the output file OK */ + if (ferror(dest->outfile)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + +METHODDEF(void) +term_mem_destination (j_compress_ptr cinfo) +{ + my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest; + + *dest->outbuffer = dest->buffer; + *dest->outsize = dest->bufsize - dest->pub.free_in_buffer; +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +GLOBAL(void) +jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} + + +/* + * Prepare for output to a memory buffer. + * The caller may supply an own initial buffer with appropriate size. + * Otherwise, or when the actual data output exceeds the given size, + * the library adapts the buffer size as necessary. + * The standard library functions malloc/free are used for allocating + * larger memory, so the buffer is available to the application after + * finishing compression, and then the application is responsible for + * freeing the requested memory. + */ + +GLOBAL(void) +jpeg_mem_dest (j_compress_ptr cinfo, + unsigned char ** outbuffer, unsigned long * outsize) +{ + my_mem_dest_ptr dest; + + if (outbuffer == NULL || outsize == NULL) /* sanity check */ + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same buffer without re-executing jpeg_mem_dest. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_mem_destination_mgr)); + } + + dest = (my_mem_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_mem_destination; + dest->pub.empty_output_buffer = empty_mem_output_buffer; + dest->pub.term_destination = term_mem_destination; + dest->outbuffer = outbuffer; + dest->outsize = outsize; + dest->newbuffer = NULL; + + if (*outbuffer == NULL || *outsize == 0) { + /* Allocate initial buffer */ + dest->newbuffer = *outbuffer = malloc(OUTPUT_BUF_SIZE); + if (dest->newbuffer == NULL) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); + *outsize = OUTPUT_BUF_SIZE; + } + + dest->pub.next_output_byte = dest->buffer = *outbuffer; + dest->pub.free_in_buffer = dest->bufsize = *outsize; +} diff --git a/lib/jpeg/src/jdatadst.d b/lib/jpeg/src/jdatadst.d new file mode 100644 index 0000000..c095c95 --- /dev/null +++ b/lib/jpeg/src/jdatadst.d @@ -0,0 +1,11 @@ +jdatadst.o: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: diff --git a/lib/jpeg/src/jdatasrc.c b/lib/jpeg/src/jdatasrc.c new file mode 100644 index 0000000..557fc96 --- /dev/null +++ b/lib/jpeg/src/jdatasrc.c @@ -0,0 +1,274 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2009-2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from memory or from a file (or any stdio stream). + * While these routines are sufficient for most applications, + * some will want to use a different source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + FILE * infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + +METHODDEF(void) +init_mem_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = JFREAD(src->infile, src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + +METHODDEF(boolean) +fill_mem_input_buffer (j_decompress_ptr cinfo) +{ + static JOCTET mybuffer[4]; + + /* The whole JPEG data is expected to reside in the supplied memory + * buffer, so any request for more data beyond the given buffer size + * is treated as an error. + */ + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + mybuffer[0] = (JOCTET) 0xFF; + mybuffer[1] = (JOCTET) JPEG_EOI; + + cinfo->src->next_input_byte = mybuffer; + cinfo->src->bytes_in_buffer = 2; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_source_mgr * src = cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->bytes_in_buffer) { + num_bytes -= (long) src->bytes_in_buffer; + (void) (*src->fill_input_buffer) (cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->next_input_byte += (size_t) num_bytes; + src->bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL(void) +jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + + +/* + * Prepare for input from a supplied memory buffer. + * The buffer must contain the whole JPEG data. + */ + +GLOBAL(void) +jpeg_mem_src (j_decompress_ptr cinfo, + unsigned char * inbuffer, unsigned long insize) +{ + struct jpeg_source_mgr * src; + + if (inbuffer == NULL || insize == 0) /* Treat empty input as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + + /* The source object is made permanent so that a series of JPEG images + * can be read from the same buffer by calling jpeg_mem_src only before + * the first one. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(struct jpeg_source_mgr)); + } + + src = cinfo->src; + src->init_source = init_mem_source; + src->fill_input_buffer = fill_mem_input_buffer; + src->skip_input_data = skip_input_data; + src->resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->term_source = term_source; + src->bytes_in_buffer = (size_t) insize; + src->next_input_byte = (JOCTET *) inbuffer; +} diff --git a/lib/jpeg/src/jdatasrc.d b/lib/jpeg/src/jdatasrc.d new file mode 100644 index 0000000..156130f --- /dev/null +++ b/lib/jpeg/src/jdatasrc.d @@ -0,0 +1,11 @@ +jdatasrc.o: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: diff --git a/lib/jpeg/src/jdcoefct.c b/lib/jpeg/src/jdcoefct.c new file mode 100644 index 0000000..8c81f8f --- /dev/null +++ b/lib/jpeg/src/jdcoefct.c @@ -0,0 +1,736 @@ +/* + * jdcoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for decompression. + * This controller is the top level of the JPEG decompressor proper. + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + * Also, the input side (only) is used when reading a file for transcoding. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF(int) decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL(boolean) smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF(int) decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->DCT_v_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_h_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_v_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data (j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_h_scaled_size; + } + output_ptr += compptr->DCT_v_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* Natural-order array positions of the first 5 zigzag-order coefficients */ +#define Q01_POS 1 +#define Q10_POS 8 +#define Q20_POS 16 +#define Q11_POS 9 +#define Q02_POS 2 + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL(boolean) +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + if (qtable->quantval[0] == 0 || + qtable->quantval[Q01_POS] == 0 || + qtable->quantval[Q10_POS] == 0 || + qtable->quantval[Q20_POS] == 0 || + qtable->quantval[Q11_POS] == 0 || + qtable->quantval[Q02_POS] == 0) + return FALSE; + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF(int) +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[Q01_POS]; + Q10 = quanttbl->quantval[Q10_POS]; + Q20 = quanttbl->quantval[Q20_POS]; + Q11 = quanttbl->quantval[Q11_POS]; + Q02 = quanttbl->quantval[Q02_POS]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_h_scaled_size; + } + output_ptr += compptr->DCT_v_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} diff --git a/lib/jpeg/src/jdcoefct.d b/lib/jpeg/src/jdcoefct.d new file mode 100644 index 0000000..459bd5e --- /dev/null +++ b/lib/jpeg/src/jdcoefct.d @@ -0,0 +1,14 @@ +jdcoefct.o: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdcolor.c b/lib/jpeg/src/jdcolor.c new file mode 100644 index 0000000..fd7b138 --- /dev/null +++ b/lib/jpeg/src/jdcolor.c @@ -0,0 +1,396 @@ +/* + * jdcolor.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter; + +typedef my_color_deconverter * my_cconvert_ptr; + + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/**************** Cases other than YCbCr -> RGB **************/ + + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF(void) +null_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF(void) +grayscale_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + + +/* + * Convert grayscale to RGB: just duplicate the graylevel three times. + * This is provided to support applications that don't want to cope + * with grayscale as a separate case. + */ + +METHODDEF(void) +gray_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + + while (--num_rows >= 0) { + inptr = input_buf[0][input_row++]; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + /* We can dispense with GETJSAMPLE() here */ + outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF(void) +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +start_pass_dcolor (j_decompress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL(void) +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + int ci; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { + cconvert->pub.color_convert = gray_rgb_convert; + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} diff --git a/lib/jpeg/src/jdcolor.d b/lib/jpeg/src/jdcolor.d new file mode 100644 index 0000000..c790644 --- /dev/null +++ b/lib/jpeg/src/jdcolor.d @@ -0,0 +1,14 @@ +jdcolor.o: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdct.h b/lib/jpeg/src/jdct.h new file mode 100644 index 0000000..b1ff912 --- /dev/null +++ b/lib/jpeg/src/jdct.h @@ -0,0 +1,393 @@ +/* + * jdct.h + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file contains common declarations for the forward and + * inverse DCT modules. These declarations are private to the DCT managers + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + * The individual DCT algorithms are kept in separate files to ease + * machine-dependent tuning (e.g., assembly coding). + */ + + +/* + * A forward DCT routine is given a pointer to an input sample array and + * a pointer to a work area of type DCTELEM[]; the DCT is to be performed + * in-place in that buffer. Type DCTELEM is int for 8-bit samples, INT32 + * for 12-bit samples. (NOTE: Floating-point DCT implementations use an + * array of type FAST_FLOAT, instead.) + * The input data is to be fetched from the sample array starting at a + * specified column. (Any row offset needed will be applied to the array + * pointer before it is passed to the FDCT code.) + * Note that the number of samples fetched by the FDCT routine is + * DCT_h_scaled_size * DCT_v_scaled_size. + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data, + JSAMPARRAY sample_data, + JDIMENSION start_col)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data, + JSAMPARRAY sample_data, + JDIMENSION start_col)); + + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_h_scaled_size * DCT_v_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_fdct_7x7 jFD7x7 +#define jpeg_fdct_6x6 jFD6x6 +#define jpeg_fdct_5x5 jFD5x5 +#define jpeg_fdct_4x4 jFD4x4 +#define jpeg_fdct_3x3 jFD3x3 +#define jpeg_fdct_2x2 jFD2x2 +#define jpeg_fdct_1x1 jFD1x1 +#define jpeg_fdct_9x9 jFD9x9 +#define jpeg_fdct_10x10 jFD10x10 +#define jpeg_fdct_11x11 jFD11x11 +#define jpeg_fdct_12x12 jFD12x12 +#define jpeg_fdct_13x13 jFD13x13 +#define jpeg_fdct_14x14 jFD14x14 +#define jpeg_fdct_15x15 jFD15x15 +#define jpeg_fdct_16x16 jFD16x16 +#define jpeg_fdct_16x8 jFD16x8 +#define jpeg_fdct_14x7 jFD14x7 +#define jpeg_fdct_12x6 jFD12x6 +#define jpeg_fdct_10x5 jFD10x5 +#define jpeg_fdct_8x4 jFD8x4 +#define jpeg_fdct_6x3 jFD6x3 +#define jpeg_fdct_4x2 jFD4x2 +#define jpeg_fdct_2x1 jFD2x1 +#define jpeg_fdct_8x16 jFD8x16 +#define jpeg_fdct_7x14 jFD7x14 +#define jpeg_fdct_6x12 jFD6x12 +#define jpeg_fdct_5x10 jFD5x10 +#define jpeg_fdct_4x8 jFD4x8 +#define jpeg_fdct_3x6 jFD3x6 +#define jpeg_fdct_2x4 jFD2x4 +#define jpeg_fdct_1x2 jFD1x2 +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_7x7 jRD7x7 +#define jpeg_idct_6x6 jRD6x6 +#define jpeg_idct_5x5 jRD5x5 +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_3x3 jRD3x3 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#define jpeg_idct_9x9 jRD9x9 +#define jpeg_idct_10x10 jRD10x10 +#define jpeg_idct_11x11 jRD11x11 +#define jpeg_idct_12x12 jRD12x12 +#define jpeg_idct_13x13 jRD13x13 +#define jpeg_idct_14x14 jRD14x14 +#define jpeg_idct_15x15 jRD15x15 +#define jpeg_idct_16x16 jRD16x16 +#define jpeg_idct_16x8 jRD16x8 +#define jpeg_idct_14x7 jRD14x7 +#define jpeg_idct_12x6 jRD12x6 +#define jpeg_idct_10x5 jRD10x5 +#define jpeg_idct_8x4 jRD8x4 +#define jpeg_idct_6x3 jRD6x3 +#define jpeg_idct_4x2 jRD4x2 +#define jpeg_idct_2x1 jRD2x1 +#define jpeg_idct_8x16 jRD8x16 +#define jpeg_idct_7x14 jRD7x14 +#define jpeg_idct_6x12 jRD6x12 +#define jpeg_idct_5x10 jRD5x10 +#define jpeg_idct_4x8 jRD4x8 +#define jpeg_idct_3x6 jRD3x8 +#define jpeg_idct_2x4 jRD2x4 +#define jpeg_idct_1x2 jRD1x2 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN(void) jpeg_fdct_islow + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_ifast + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_float + JPP((FAST_FLOAT * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_7x7 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_6x6 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_5x5 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_4x4 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_3x3 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_2x2 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_1x1 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_9x9 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_10x10 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_11x11 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_12x12 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_13x13 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_14x14 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_15x15 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_16x16 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_16x8 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_14x7 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_12x6 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_10x5 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_8x4 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_6x3 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_4x2 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_2x1 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_8x16 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_7x14 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_6x12 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_5x10 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_4x8 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_3x6 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_2x4 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); +EXTERN(void) jpeg_fdct_1x2 + JPP((DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col)); + +EXTERN(void) jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_7x7 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_6x6 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_5x5 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_3x3 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_9x9 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_10x10 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_11x11 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_12x12 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_13x13 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_14x14 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_15x15 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_16x16 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_16x8 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_14x7 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_12x6 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_10x5 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_8x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_6x3 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_8x16 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_7x14 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_6x12 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_5x10 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x8 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_3x6 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_1x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif diff --git a/lib/jpeg/src/jddctmgr.c b/lib/jpeg/src/jddctmgr.c new file mode 100644 index 0000000..5e4f1dc --- /dev/null +++ b/lib/jpeg/src/jddctmgr.c @@ -0,0 +1,384 @@ +/* + * jddctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2002-2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the inverse-DCT management logic. + * This code selects a particular IDCT implementation to be used, + * and it performs related housekeeping chores. No code in this file + * is executed per IDCT step, only during output pass setup. + * + * Note that the IDCT routines are responsible for performing coefficient + * dequantization as well as the IDCT proper. This module sets up the + * dequantization multiplier table needed by the IDCT routine. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF(void) +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch ((compptr->DCT_h_scaled_size << 8) + compptr->DCT_v_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case ((1 << 8) + 1): + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((2 << 8) + 2): + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((3 << 8) + 3): + method_ptr = jpeg_idct_3x3; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((4 << 8) + 4): + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((5 << 8) + 5): + method_ptr = jpeg_idct_5x5; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((6 << 8) + 6): + method_ptr = jpeg_idct_6x6; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((7 << 8) + 7): + method_ptr = jpeg_idct_7x7; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((9 << 8) + 9): + method_ptr = jpeg_idct_9x9; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((10 << 8) + 10): + method_ptr = jpeg_idct_10x10; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((11 << 8) + 11): + method_ptr = jpeg_idct_11x11; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((12 << 8) + 12): + method_ptr = jpeg_idct_12x12; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((13 << 8) + 13): + method_ptr = jpeg_idct_13x13; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((14 << 8) + 14): + method_ptr = jpeg_idct_14x14; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((15 << 8) + 15): + method_ptr = jpeg_idct_15x15; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((16 << 8) + 16): + method_ptr = jpeg_idct_16x16; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((16 << 8) + 8): + method_ptr = jpeg_idct_16x8; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((14 << 8) + 7): + method_ptr = jpeg_idct_14x7; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((12 << 8) + 6): + method_ptr = jpeg_idct_12x6; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((10 << 8) + 5): + method_ptr = jpeg_idct_10x5; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((8 << 8) + 4): + method_ptr = jpeg_idct_8x4; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((6 << 8) + 3): + method_ptr = jpeg_idct_6x3; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((4 << 8) + 2): + method_ptr = jpeg_idct_4x2; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((2 << 8) + 1): + method_ptr = jpeg_idct_2x1; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((8 << 8) + 16): + method_ptr = jpeg_idct_8x16; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((7 << 8) + 14): + method_ptr = jpeg_idct_7x14; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((6 << 8) + 12): + method_ptr = jpeg_idct_6x12; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((5 << 8) + 10): + method_ptr = jpeg_idct_5x10; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((4 << 8) + 8): + method_ptr = jpeg_idct_4x8; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((3 << 8) + 6): + method_ptr = jpeg_idct_3x6; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((2 << 8) + 4): + method_ptr = jpeg_idct_2x4; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; + case ((1 << 8) + 2): + method_ptr = jpeg_idct_1x2; + method = JDCT_ISLOW; /* jidctint uses islow-style table */ + break; +#endif + case ((DCTSIZE << 8) + DCTSIZE): + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT2(cinfo, JERR_BAD_DCTSIZE, + compptr->DCT_h_scaled_size, compptr->DCT_v_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored as ints to ensure access efficiency. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 1/8. + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col] * 0.125); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize IDCT manager. + */ + +GLOBAL(void) +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} diff --git a/lib/jpeg/src/jddctmgr.d b/lib/jpeg/src/jddctmgr.d new file mode 100644 index 0000000..3b3d8ee --- /dev/null +++ b/lib/jpeg/src/jddctmgr.d @@ -0,0 +1,16 @@ +jddctmgr.o: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jdhuff.c b/lib/jpeg/src/jdhuff.c new file mode 100644 index 0000000..9694117 --- /dev/null +++ b/lib/jpeg/src/jdhuff.c @@ -0,0 +1,1541 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2006-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * Both sequential and progressive modes are supported in this single module. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + INT32 valoffset[17]; /* huffval[] offset for codes of length k */ + /* valoffset[k] = huffval[] index of 1st symbol of code length k, less + * the smallest code of length k; so given a code of length k, the + * corresponding symbol is huffval[code + valoffset[k]] + */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't define the size + * with something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* Current data source location */ + /* We need a copy, rather than munging the original, in case of suspension */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + /* Bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* Pointer needed by jpeg_fill_bit_buffer. */ + j_decompress_ptr cinfo; /* back link to decompress master record */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & BIT_MASK(nbits)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & BIT_MASK(nbits)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + unsigned int EOBRUN; /* remaining EOBs in EOBRUN */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).EOBRUN = (src).EOBRUN, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + boolean insufficient_data; /* set TRUE after emitting warning */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Following two fields used only in progressive mode */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ + + /* Following fields used only in sequential mode */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + int coef_limit[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +static const int jpeg_zigzag_order[8][8] = { + { 0, 1, 5, 6, 14, 15, 27, 28 }, + { 2, 4, 7, 13, 16, 26, 29, 42 }, + { 3, 8, 12, 17, 25, 30, 41, 43 }, + { 9, 11, 18, 24, 31, 40, 44, 53 }, + { 10, 19, 23, 32, 39, 45, 52, 54 }, + { 20, 22, 33, 38, 46, 51, 55, 60 }, + { 21, 34, 37, 47, 50, 56, 59, 61 }, + { 35, 36, 48, 49, 57, 58, 62, 63 } +}; + +static const int jpeg_zigzag_order7[7][7] = { + { 0, 1, 5, 6, 14, 15, 27 }, + { 2, 4, 7, 13, 16, 26, 28 }, + { 3, 8, 12, 17, 25, 29, 38 }, + { 9, 11, 18, 24, 30, 37, 39 }, + { 10, 19, 23, 31, 36, 40, 45 }, + { 20, 22, 32, 35, 41, 44, 46 }, + { 21, 33, 34, 42, 43, 47, 48 } +}; + +static const int jpeg_zigzag_order6[6][6] = { + { 0, 1, 5, 6, 14, 15 }, + { 2, 4, 7, 13, 16, 25 }, + { 3, 8, 12, 17, 24, 26 }, + { 9, 11, 18, 23, 27, 32 }, + { 10, 19, 22, 28, 31, 33 }, + { 20, 21, 29, 30, 34, 35 } +}; + +static const int jpeg_zigzag_order5[5][5] = { + { 0, 1, 5, 6, 14 }, + { 2, 4, 7, 13, 15 }, + { 3, 8, 12, 16, 21 }, + { 9, 11, 17, 20, 22 }, + { 10, 18, 19, 23, 24 } +}; + +static const int jpeg_zigzag_order4[4][4] = { + { 0, 1, 5, 6 }, + { 2, 4, 7, 12 }, + { 3, 8, 11, 13 }, + { 9, 10, 14, 15 } +}; + +static const int jpeg_zigzag_order3[3][3] = { + { 0, 1, 5 }, + { 2, 4, 6 }, + { 3, 7, 8 } +}; + +static const int jpeg_zigzag_order2[2][2] = { + { 0, 1 }, + { 2, 3 } +}; + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + */ + +LOCAL(void) +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, + d_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + d_derived_tbl *dtbl; + int p, i, l, si, numsymbols; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + numsymbols = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + /* valoffset[l] = huffval[] index of 1st symbol of code length l, + * minus the minimum code of length l + */ + dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p]; + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } + + /* Validate symbols as being reasonable. + * For AC tables, we make no check, but accept all byte values 0..255. + * For DC tables, we require the symbols to be in range 0..15. + * (Tighter bounds could be applied depending on the data depth and mode, + * but this is sufficient to ensure safe decoding.) + */ + if (isDC) { + for (i = 0; i < numsymbols; i++) { + int sym = htbl->huffval[i]; + if (sym < 0 || sym > 15) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + } + } +} + + +/* + * Out-of-line code for bit fetching. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +LOCAL(boolean) +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + j_decompress_ptr cinfo = state->cinfo; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + /* We fail to do so only if we hit a marker or are forced to suspend. */ + + if (cinfo->unread_marker == 0) { /* cannot advance past a marker */ + while (bits_left < MIN_GET_BITS) { + register int c; + + /* Attempt to read a byte */ + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + /* Loop here to discard any padding FF's on terminating marker, + * so that we can save a valid unread_marker value. NOTE: we will + * accept multiple FF's followed by a 0 as meaning a single FF data + * byte. This data pattern is not valid according to the standard. + */ + do { + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. + * Save the marker code for later use. + * Fine point: it might appear that we should save the marker into + * bitread working state, not straight into permanent state. But + * once we have hit a marker, we cannot need to suspend within the + * current MCU, because we will read no more bytes from the data + * source. So it is OK to update permanent state right away. + */ + cinfo->unread_marker = c; + /* See if we need to insert some fake zero bits. */ + goto no_more_bytes; + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } /* end while */ + } else { + no_more_bytes: + /* We get here if we've read the marker that terminates the compressed + * data segment. There should be enough bits in the buffer register + * to satisfy the request; if so, no problem. + */ + if (nbits > bits_left) { + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * We use a nonvolatile flag to ensure that only one warning message + * appears per data segment. + */ + if (! ((huff_entropy_ptr) cinfo->entropy)->insufficient_data) { + WARNMS(cinfo, JWRN_HIT_MARKER); + ((huff_entropy_ptr) cinfo->entropy)->insufficient_data = TRUE; + } + /* Fill the buffer with zero bits */ + get_buffer <<= MIN_GET_BITS - bits_left; + bits_left = MIN_GET_BITS; + } + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and sub will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define BIT_MASK(nbits) ((1<<(nbits))-1) +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) - ((1<<(s))-1) : (x)) + +#else + +#define BIT_MASK(nbits) bmask[nbits] +#define HUFF_EXTEND(x,s) ((x) <= bmask[(s) - 1] ? (x) - bmask[s] : (x)) + +static const int bmask[16] = /* bmask[n] is mask for n rightmost bits */ + { 0, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF }; + +#endif /* AVOID_TABLES */ + + +/* + * Out-of-line code for Huffman code decoding. + */ + +LOCAL(int) +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; +} + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Re-init EOB run count, too */ + entropy->saved.EOBRUN = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Huffman MCU decoding. + * Each of these routines decodes and returns one MCU's worth of + * Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * We return FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * spectral selection, since we'll just re-assign them on the next call. + * Successive approximation AC refinement has to be more careful, however.) + */ + +/* + * MCU decoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int Al = cinfo->Al; + register int s, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * tbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + tbl = entropy->derived_tbls[compptr->dc_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, tbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ + (*block)[0] = (JCOEF) (s << Al); + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int s, k, r; + unsigned int EOBRUN; + int Se, Al; + const int * natural_order; + JBLOCKROW block; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->insufficient_data) { + + Se = cinfo->Se; + Al = cinfo->Al; + natural_order = cinfo->natural_order; + + /* Load up working state. + * We can avoid loading/saving bitread state if in an EOB run. + */ + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + + if (EOBRUN > 0) /* if it's a band of zeroes... */ + EOBRUN--; /* ...process it now (we do nothing) */ + else { + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + for (k = cinfo->Ss; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, return FALSE, label2); + r = s >> 4; + s &= 15; + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Scale and output coefficient in natural (dezigzagged) order */ + (*block)[natural_order[k]] = (JCOEF) (s << Al); + } else { + if (r == 15) { /* ZRL */ + k += 15; /* skip 15 zeroes in band */ + } else { /* EOBr, run length is 2^r + appended bits */ + EOBRUN = 1 << r; + if (r) { /* EOBr, r > 0 */ + CHECK_BIT_BUFFER(br_state, r, return FALSE); + r = GET_BITS(r); + EOBRUN += r; + } + EOBRUN--; /* this band is processed at this moment */ + break; /* force end-of-band */ + } + } + } + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + } + + /* Completed MCU, so update state */ + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int blkn; + JBLOCKROW block; + BITREAD_STATE_VARS; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Not worth the cycles to check insufficient_data here, + * since we will not change the data anyway if we read zeroes. + */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* Encoded data is simply the next bit of the two's-complement DC value */ + CHECK_BIT_BUFFER(br_state, 1, return FALSE); + if (GET_BITS(1)) + (*block)[0] |= p1; + /* Note: since we use |=, repeating the assignment later is safe */ + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int s, k, r; + unsigned int EOBRUN; + int Se, p1, m1; + const int * natural_order; + JBLOCKROW block; + JCOEFPTR thiscoef; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + int num_newnz; + int newnz_pos[DCTSIZE2]; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, don't modify the MCU. + */ + if (! entropy->insufficient_data) { + + Se = cinfo->Se; + p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + natural_order = cinfo->natural_order; + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + /* If we are forced to suspend, we must undo the assignments to any newly + * nonzero coefficients in the block, because otherwise we'd get confused + * next time about which coefficients were already nonzero. + * But we need not undo addition of bits to already-nonzero coefficients; + * instead, we can test the current bit to see if we already did it. + */ + num_newnz = 0; + + /* initialize coefficient loop counter to start of band */ + k = cinfo->Ss; + + if (EOBRUN == 0) { + for (; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, goto undoit, label3); + r = s >> 4; + s &= 15; + if (s) { + if (s != 1) /* size of new coef should always be 1 */ + WARNMS(cinfo, JWRN_HUFF_BAD_CODE); + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) + s = p1; /* newly nonzero coef is positive */ + else + s = m1; /* newly nonzero coef is negative */ + } else { + if (r != 15) { + EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ + if (r) { + CHECK_BIT_BUFFER(br_state, r, goto undoit); + r = GET_BITS(r); + EOBRUN += r; + } + break; /* rest of block is handled by EOB logic */ + } + /* note s = 0 for processing ZRL */ + } + /* Advance over already-nonzero coefs and r still-zero coefs, + * appending correction bits to the nonzeroes. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + do { + thiscoef = *block + natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already set it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } else { + if (--r < 0) + break; /* reached target zero coefficient */ + } + k++; + } while (k <= Se); + if (s) { + int pos = natural_order[k]; + /* Output newly nonzero coefficient */ + (*block)[pos] = (JCOEF) s; + /* Remember its position in case we have to suspend */ + newnz_pos[num_newnz++] = pos; + } + } + } + + if (EOBRUN > 0) { + /* Scan any remaining coefficient positions after the end-of-band + * (the last newly nonzero coefficient, if any). Append a correction + * bit to each already-nonzero coefficient. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + for (; k <= Se; k++) { + thiscoef = *block + natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } + } + /* Count one block completed in EOB run */ + EOBRUN--; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; + +undoit: + /* Re-zero any output coefficients that we made newly nonzero */ + while (num_newnz > 0) + (*block)[newnz_pos[--num_newnz]] = 0; + + return FALSE; +} + + +/* + * Decode one MCU's worth of Huffman-compressed coefficients, + * partial blocks. + */ + +METHODDEF(boolean) +decode_mcu_sub (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + const int * natural_order; + int Se, blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->insufficient_data) { + + natural_order = cinfo->natural_order; + Se = cinfo->lim_Se; + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * htbl; + register int s, k, r; + int coef_limit, ci; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + htbl = entropy->dc_cur_tbls[blkn]; + HUFF_DECODE(s, br_state, htbl, return FALSE, label1); + + htbl = entropy->ac_cur_tbls[blkn]; + k = 1; + coef_limit = entropy->coef_limit[blkn]; + if (coef_limit) { + /* Convert DC difference to actual value, update last_dc_val */ + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient */ + (*block)[0] = (JCOEF) s; + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (; k < coef_limit; k++) { + HUFF_DECODE(s, br_state, htbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in natural_order[] will save us + * if k > Se, which could happen if the data is corrupted. + */ + (*block)[natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + goto EndOfBlock; + k += 15; + } + } + } else { + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } + } + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (; k <= Se; k++) { + HUFF_DECODE(s, br_state, htbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + EndOfBlock: ; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Decode one MCU's worth of Huffman-compressed coefficients, + * full-size blocks. + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * htbl; + register int s, k, r; + int coef_limit, ci; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + htbl = entropy->dc_cur_tbls[blkn]; + HUFF_DECODE(s, br_state, htbl, return FALSE, label1); + + htbl = entropy->ac_cur_tbls[blkn]; + k = 1; + coef_limit = entropy->coef_limit[blkn]; + if (coef_limit) { + /* Convert DC difference to actual value, update last_dc_val */ + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient */ + (*block)[0] = (JCOEF) s; + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (; k < coef_limit; k++) { + HUFF_DECODE(s, br_state, htbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + goto EndOfBlock; + k += 15; + } + } + } else { + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } + } + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, htbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + EndOfBlock: ; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, blkn, tbl, i; + jpeg_component_info * compptr; + + if (cinfo->progressive_mode) { + /* Validate progressive scan parameters */ + if (cinfo->Ss == 0) { + if (cinfo->Se != 0) + goto bad; + } else { + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + if (cinfo->Se < cinfo->Ss || cinfo->Se > cinfo->lim_Se) + goto bad; + /* AC scans may have only one component */ + if (cinfo->comps_in_scan != 1) + goto bad; + } + if (cinfo->Ah != 0) { + /* Successive approximation refinement scan: must have Al = Ah-1. */ + if (cinfo->Ah-1 != cinfo->Al) + goto bad; + } + if (cinfo->Al > 13) { /* need not check for < 0 */ + /* Arguably the maximum Al value should be less than 13 for 8-bit precision, + * but the spec doesn't say so, and we try to be liberal about what we + * accept. Note: large Al values could result in out-of-range DC + * coefficients during early scans, leading to bizarre displays due to + * overflows in the IDCT math. But we won't crash. + */ + bad: + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + } + /* Update progression status, and verify that scan order is legal. + * Note that inter-scan inconsistencies are treated as warnings + * not fatal errors ... not clear if this is right way to behave. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + int coefi, cindex = cinfo->cur_comp_info[ci]->component_index; + int *coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + if (cinfo->Ss && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + if (cinfo->Ah != expected) + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr[coefi] = cinfo->Al; + } + } + + /* Select MCU decoding routine */ + if (cinfo->Ah == 0) { + if (cinfo->Ss == 0) + entropy->pub.decode_mcu = decode_mcu_DC_first; + else + entropy->pub.decode_mcu = decode_mcu_AC_first; + } else { + if (cinfo->Ss == 0) + entropy->pub.decode_mcu = decode_mcu_DC_refine; + else + entropy->pub.decode_mcu = decode_mcu_AC_refine; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Make sure requested tables are present, and compute derived tables. + * We may build same derived table more than once, but it's not expensive. + */ + if (cinfo->Ss == 0) { + if (cinfo->Ah == 0) { /* DC refinement needs no table */ + tbl = compptr->dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + & entropy->derived_tbls[tbl]); + } + } else { + tbl = compptr->ac_tbl_no; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, + & entropy->derived_tbls[tbl]); + /* remember the single active table */ + entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize private state variables */ + entropy->saved.EOBRUN = 0; + } else { + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Ah != 0 || cinfo->Al != 0 || + ((cinfo->is_baseline || cinfo->Se < DCTSIZE2) && + cinfo->Se != cinfo->lim_Se)) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + /* Select MCU decoding routine */ + /* We retain the hard-coded case for full-size blocks. + * This is not necessary, but it appears that this version is slightly + * more performant in the given implementation. + * With an improved implementation we would prefer a single optimized + * function. + */ + if (cinfo->lim_Se != DCTSIZE2-1) + entropy->pub.decode_mcu = decode_mcu_sub; + else + entropy->pub.decode_mcu = decode_mcu; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + tbl = compptr->dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + & entropy->dc_derived_tbls[tbl]); + if (cinfo->lim_Se) { /* AC needs no table when not present */ + tbl = compptr->ac_tbl_no; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, + & entropy->ac_derived_tbls[tbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + ci = compptr->DCT_v_scaled_size; + i = compptr->DCT_h_scaled_size; + switch (cinfo->lim_Se) { + case (1*1-1): + entropy->coef_limit[blkn] = 1; + break; + case (2*2-1): + if (ci <= 0 || ci > 2) ci = 2; + if (i <= 0 || i > 2) i = 2; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order2[ci - 1][i - 1]; + break; + case (3*3-1): + if (ci <= 0 || ci > 3) ci = 3; + if (i <= 0 || i > 3) i = 3; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order3[ci - 1][i - 1]; + break; + case (4*4-1): + if (ci <= 0 || ci > 4) ci = 4; + if (i <= 0 || i > 4) i = 4; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order4[ci - 1][i - 1]; + break; + case (5*5-1): + if (ci <= 0 || ci > 5) ci = 5; + if (i <= 0 || i > 5) i = 5; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order5[ci - 1][i - 1]; + break; + case (6*6-1): + if (ci <= 0 || ci > 6) ci = 6; + if (i <= 0 || i > 6) i = 6; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order6[ci - 1][i - 1]; + break; + case (7*7-1): + if (ci <= 0 || ci > 7) ci = 7; + if (i <= 0 || i > 7) i = 7; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order7[ci - 1][i - 1]; + break; + default: + if (ci <= 0 || ci > 8) ci = 8; + if (i <= 0 || i > 8) i = 8; + entropy->coef_limit[blkn] = 1 + jpeg_zigzag_order[ci - 1][i - 1]; + break; + } + } else { + entropy->coef_limit[blkn] = 0; + } + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + + if (cinfo->progressive_mode) { + /* Create progression status table */ + int *coef_bit_ptr, ci; + cinfo->coef_bits = (int (*)[DCTSIZE2]) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + coef_bit_ptr = & cinfo->coef_bits[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (i = 0; i < DCTSIZE2; i++) + *coef_bit_ptr++ = -1; + + /* Mark derived tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } + } else { + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } + } +} diff --git a/lib/jpeg/src/jdhuff.d b/lib/jpeg/src/jdhuff.d new file mode 100644 index 0000000..f612175 --- /dev/null +++ b/lib/jpeg/src/jdhuff.d @@ -0,0 +1,14 @@ +jdhuff.o: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdinput.c b/lib/jpeg/src/jdinput.c new file mode 100644 index 0000000..de6f7ed --- /dev/null +++ b/lib/jpeg/src/jdinput.c @@ -0,0 +1,661 @@ +/* + * jdinput.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input control logic for the JPEG decompressor. + * These routines are concerned with controlling the decompressor's input + * processing (marker reading and coefficient decoding). The actual input + * reading is done in jdmarker.c, jdhuff.c, and jdarith.c. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + int inheaders; /* Nonzero until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + + +/* Forward declarations */ +METHODDEF(int) consume_markers JPP((j_decompress_ptr cinfo)); + + +/* + * Routines to calculate various quantities related to the size of the image. + */ + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + */ + +GLOBAL(void) +jpeg_core_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase. + * This function is used for transcoding and full decompression. + */ +{ +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom) { + /* Provide 1/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 1; + cinfo->min_DCT_v_scaled_size = 1; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 2) { + /* Provide 2/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 2L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 2L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 2; + cinfo->min_DCT_v_scaled_size = 2; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 3) { + /* Provide 3/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 3L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 3L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 3; + cinfo->min_DCT_v_scaled_size = 3; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 4) { + /* Provide 4/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 4L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 4L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 4; + cinfo->min_DCT_v_scaled_size = 4; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 5) { + /* Provide 5/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 5L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 5L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 5; + cinfo->min_DCT_v_scaled_size = 5; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 6) { + /* Provide 6/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 6L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 6L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 6; + cinfo->min_DCT_v_scaled_size = 6; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 7) { + /* Provide 7/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 7L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 7L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 7; + cinfo->min_DCT_v_scaled_size = 7; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 8) { + /* Provide 8/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 8L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 8L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 8; + cinfo->min_DCT_v_scaled_size = 8; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 9) { + /* Provide 9/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 9L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 9L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 9; + cinfo->min_DCT_v_scaled_size = 9; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 10) { + /* Provide 10/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 10L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 10L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 10; + cinfo->min_DCT_v_scaled_size = 10; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 11) { + /* Provide 11/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 11L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 11L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 11; + cinfo->min_DCT_v_scaled_size = 11; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 12) { + /* Provide 12/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 12L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 12L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 12; + cinfo->min_DCT_v_scaled_size = 12; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 13) { + /* Provide 13/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 13L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 13L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 13; + cinfo->min_DCT_v_scaled_size = 13; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 14) { + /* Provide 14/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 14L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 14L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 14; + cinfo->min_DCT_v_scaled_size = 14; + } else if (cinfo->scale_num * cinfo->block_size <= cinfo->scale_denom * 15) { + /* Provide 15/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 15L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 15L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 15; + cinfo->min_DCT_v_scaled_size = 15; + } else { + /* Provide 16/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * 16L, (long) cinfo->block_size); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * 16L, (long) cinfo->block_size); + cinfo->min_DCT_h_scaled_size = 16; + cinfo->min_DCT_v_scaled_size = 16; + } + + /* Recompute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_h_scaled_size = cinfo->min_DCT_h_scaled_size; + compptr->DCT_v_scaled_size = cinfo->min_DCT_v_scaled_size; + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ +} + + +LOCAL(void) +initial_setup (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* Derive block_size, natural_order, and lim_Se */ + if (cinfo->is_baseline || (cinfo->progressive_mode && + cinfo->comps_in_scan)) { /* no pseudo SOS marker */ + cinfo->block_size = DCTSIZE; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + } else + switch (cinfo->Se) { + case (1*1-1): + cinfo->block_size = 1; + cinfo->natural_order = jpeg_natural_order; /* not needed */ + cinfo->lim_Se = cinfo->Se; + break; + case (2*2-1): + cinfo->block_size = 2; + cinfo->natural_order = jpeg_natural_order2; + cinfo->lim_Se = cinfo->Se; + break; + case (3*3-1): + cinfo->block_size = 3; + cinfo->natural_order = jpeg_natural_order3; + cinfo->lim_Se = cinfo->Se; + break; + case (4*4-1): + cinfo->block_size = 4; + cinfo->natural_order = jpeg_natural_order4; + cinfo->lim_Se = cinfo->Se; + break; + case (5*5-1): + cinfo->block_size = 5; + cinfo->natural_order = jpeg_natural_order5; + cinfo->lim_Se = cinfo->Se; + break; + case (6*6-1): + cinfo->block_size = 6; + cinfo->natural_order = jpeg_natural_order6; + cinfo->lim_Se = cinfo->Se; + break; + case (7*7-1): + cinfo->block_size = 7; + cinfo->natural_order = jpeg_natural_order7; + cinfo->lim_Se = cinfo->Se; + break; + case (8*8-1): + cinfo->block_size = 8; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (9*9-1): + cinfo->block_size = 9; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (10*10-1): + cinfo->block_size = 10; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (11*11-1): + cinfo->block_size = 11; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (12*12-1): + cinfo->block_size = 12; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (13*13-1): + cinfo->block_size = 13; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (14*14-1): + cinfo->block_size = 14; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (15*15-1): + cinfo->block_size = 15; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + case (16*16-1): + cinfo->block_size = 16; + cinfo->natural_order = jpeg_natural_order; + cinfo->lim_Se = DCTSIZE2-1; + break; + default: + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + break; + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to block_size. + * In the full decompressor, + * this will be overridden by jpeg_calc_output_dimensions in jdmaster.c; + * but in the transcoder, + * jpeg_calc_output_dimensions is not used, so we must do it here. + */ + cinfo->min_DCT_h_scaled_size = cinfo->block_size; + cinfo->min_DCT_v_scaled_size = cinfo->block_size; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_h_scaled_size = cinfo->block_size; + compptr->DCT_v_scaled_size = cinfo->block_size; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + + +LOCAL(void) +per_scan_setup (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_h_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_h_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + per_scan_setup(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF(void) +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + * + * Note: This function should NOT return a pseudo SOS marker (with zero + * component number) to the caller. A pseudo marker received by + * read_markers is processed and then skipped for other markers. + */ + +METHODDEF(int) +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + for (;;) { /* Loop to pass pseudo SOS marker */ + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + if (inputctl->inheaders == 1) + initial_setup(cinfo); + if (cinfo->comps_in_scan == 0) { /* pseudo SOS marker */ + inputctl->inheaders = 2; + break; + } + inputctl->inheaders = 0; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapimin.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + if (cinfo->comps_in_scan == 0) /* unexpected pseudo SOS marker */ + break; + start_input_pass(cinfo); + } + return val; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + return val; + case JPEG_SUSPENDED: + return val; + default: + return val; + } + } +} + + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = 1; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = 1; +} diff --git a/lib/jpeg/src/jdinput.d b/lib/jpeg/src/jdinput.d new file mode 100644 index 0000000..35f39d8 --- /dev/null +++ b/lib/jpeg/src/jdinput.d @@ -0,0 +1,14 @@ +jdinput.o: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdmainct.c b/lib/jpeg/src/jdmainct.c new file mode 100644 index 0000000..995aa39 --- /dev/null +++ b/lib/jpeg/src/jdmainct.c @@ -0,0 +1,512 @@ +/* + * jdmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for decompression. + * The main buffer lies between the JPEG decompressor proper and the + * post-processor; it holds downsampled data in the JPEG colorspace. + * + * Note that this code is bypassed in raw-data mode, since the application + * supplies the equivalent of the main buffer in that case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF(void) process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + + +LOCAL(void) +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_v_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + main->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + main->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + main->xbuffer[1][ci] = xbuf; + } +} + + +LOCAL(void) +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in main->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_v_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = main->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + + +LOCAL(void) +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_v_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + + +LOCAL(void) +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_v_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_v_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = main->xbuffer[main->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + main->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main->context_state = CTX_PREPARE_FOR_IMCU; + main->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + main->pub.process_data = process_data_simple_main; + } + main->buffer_full = FALSE; /* Mark buffer empty */ + main->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + main->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF(void) +process_data_simple_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_v_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, main->buffer, + &main->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (main->rowgroup_ctr >= rowgroups_avail) { + main->buffer_full = FALSE; + main->rowgroup_ctr = 0; + } +} + + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF(void) +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + main->xbuffer[main->whichptr])) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (main->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + main->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + main->rowgroup_ctr = 0; + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_v_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + main->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (main->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_v_scaled_size + 1); + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_v_scaled_size + 2); + main->context_state = CTX_POSTPONED_ROW; + } +} + + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF(void) +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_d_main_controller *) main; + main->pub.start_pass = start_pass_main; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_v_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_v_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_v_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; /* height of a row group of component */ + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_h_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} diff --git a/lib/jpeg/src/jdmainct.d b/lib/jpeg/src/jdmainct.d new file mode 100644 index 0000000..55b8b56 --- /dev/null +++ b/lib/jpeg/src/jdmainct.d @@ -0,0 +1,14 @@ +jdmainct.o: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdmarker.c b/lib/jpeg/src/jdmarker.c new file mode 100644 index 0000000..943e6e3 --- /dev/null +++ b/lib/jpeg/src/jdmarker.c @@ -0,0 +1,1406 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_reader pub; /* public fields */ + + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* Limit on marker data length to save for each marker type */ + unsigned int length_limit_COM; + unsigned int length_limit_APPn[16]; + + /* Status of COM/APPn marker saving */ + jpeg_saved_marker_ptr cur_marker; /* NULL if not processing a marker */ + unsigned int bytes_read; /* data bytes read so far in marker */ + /* Note: cur_marker is not linked into marker_list until it's all read. */ +} my_marker_reader; + +typedef my_marker_reader * my_marker_ptr; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- used only in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters + * can fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments + * that might not fit. If we are simply dropping such a marker, we use + * skip_input_data to get past it, and thereby put the problem on the + * source manager's shoulders. If we are saving the marker's contents + * into memory, we use a slightly different convention: when forced to + * suspend, the marker processor updates the restart point to the end of + * what it's consumed (ie, the end of the buffer) before returning FALSE. + * On resumption, cinfo->unread_marker still contains the marker code, + * but the data source will point to the next chunk of marker data. + * The marker processor must retain internal state to deal with this. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL(boolean) +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->JFIF_major_version = 1; /* set default JFIF APP0 values */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL(boolean) +get_sof (j_decompress_ptr cinfo, boolean is_baseline, boolean is_prog, + boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->is_baseline = is_baseline; + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + if (length != (n * 2 + 6) || n > MAX_COMPS_IN_SCAN || + (n == 0 && !cinfo->progressive_mode)) + /* pseudo SOS marker only allowed in progressive mode */ + ERREXIT(cinfo, JERR_BAD_LENGTH); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another (non-pseudo) SOS marker */ + if (n) cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +#ifdef D_ARITH_CODING_SUPPORTED + +LOCAL(boolean) +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +#else /* ! D_ARITH_CODING_SUPPORTED */ + +#define get_dac(cinfo) skip_variable(cinfo) + +#endif /* D_ARITH_CODING_SUPPORTED */ + + +LOCAL(boolean) +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 16) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + /* Here we just do minimal validation of the counts to avoid walking + * off the end of our table space. jdhuff.c will check more carefully. + */ + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length, count, i; + int n, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + const int *natural_order; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + length--; + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + if (prec) { + if (length < DCTSIZE2 * 2) { + /* Initialize full table for safety. */ + for (i = 0; i < DCTSIZE2; i++) { + quant_ptr->quantval[i] = 1; + } + count = length >> 1; + } else + count = DCTSIZE2; + } else { + if (length < DCTSIZE2) { + /* Initialize full table for safety. */ + for (i = 0; i < DCTSIZE2; i++) { + quant_ptr->quantval[i] = 1; + } + count = length; + } else + count = DCTSIZE2; + } + + switch (count) { + case (2*2): natural_order = jpeg_natural_order2; break; + case (3*3): natural_order = jpeg_natural_order3; break; + case (4*4): natural_order = jpeg_natural_order4; break; + case (5*5): natural_order = jpeg_natural_order5; break; + case (6*6): natural_order = jpeg_natural_order6; break; + case (7*7): natural_order = jpeg_natural_order7; break; + default: natural_order = jpeg_natural_order; break; + } + + for (i = 0; i < count; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + /* We convert the zigzag-order table to natural array order. */ + quant_ptr->quantval[natural_order[i]] = (UINT16) tmp; + } + + if (cinfo->err->trace_level >= 2) { + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + } + + length -= count; + if (prec) length -= count; + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Routines for processing APPn and COM markers. + * These are either saved in memory or discarded, per application request. + * APP0 and APP14 are specially checked to see if they are + * JFIF and Adobe markers, respectively. + */ + +#define APP0_DATA_LEN 14 /* Length of interesting data in APP0 */ +#define APP14_DATA_LEN 12 /* Length of interesting data in APP14 */ +#define APPN_DATA_LEN 14 /* Must be the largest of the above!! */ + + +LOCAL(void) +examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP0. + * Take appropriate action if it is a JFIF marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + INT32 totallen = (INT32) datalen + remaining; + + if (datalen >= APP0_DATA_LEN && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x49 && + GETJOCTET(data[3]) == 0x46 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF APP0 marker: save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->JFIF_major_version = GETJOCTET(data[5]); + cinfo->JFIF_minor_version = GETJOCTET(data[6]); + cinfo->density_unit = GETJOCTET(data[7]); + cinfo->X_density = (GETJOCTET(data[8]) << 8) + GETJOCTET(data[9]); + cinfo->Y_density = (GETJOCTET(data[10]) << 8) + GETJOCTET(data[11]); + /* Check version. + * Major version must be 1, anything else signals an incompatible change. + * (We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec.) + * Minor version should be 0..2, but process anyway if newer. + */ + if (cinfo->JFIF_major_version != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version); + /* Generate trace messages */ + TRACEMS5(cinfo, 1, JTRC_JFIF, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + /* Validate thumbnail dimensions and issue appropriate messages */ + if (GETJOCTET(data[12]) | GETJOCTET(data[13])) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, + GETJOCTET(data[12]), GETJOCTET(data[13])); + totallen -= APP0_DATA_LEN; + if (totallen != + ((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen); + } else if (datalen >= 6 && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x58 && + GETJOCTET(data[3]) == 0x58 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF "JFXX" extension APP0 marker */ + /* The library doesn't actually do anything with these, + * but we try to produce a helpful trace message. + */ + switch (GETJOCTET(data[5])) { + case 0x10: + TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, (int) totallen); + break; + case 0x11: + TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, (int) totallen); + break; + case 0x13: + TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, (int) totallen); + break; + default: + TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION, + GETJOCTET(data[5]), (int) totallen); + break; + } + } else { + /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen); + } +} + + +LOCAL(void) +examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP14. + * Take appropriate action if it is an Adobe marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + unsigned int version, flags0, flags1, transform; + + if (datalen >= APP14_DATA_LEN && + GETJOCTET(data[0]) == 0x41 && + GETJOCTET(data[1]) == 0x64 && + GETJOCTET(data[2]) == 0x6F && + GETJOCTET(data[3]) == 0x62 && + GETJOCTET(data[4]) == 0x65) { + /* Found Adobe APP14 marker */ + version = (GETJOCTET(data[5]) << 8) + GETJOCTET(data[6]); + flags0 = (GETJOCTET(data[7]) << 8) + GETJOCTET(data[8]); + flags1 = (GETJOCTET(data[9]) << 8) + GETJOCTET(data[10]); + transform = GETJOCTET(data[11]); + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) (datalen + remaining)); + } +} + + +METHODDEF(boolean) +get_interesting_appn (j_decompress_ptr cinfo) +/* Process an APP0 or APP14 marker without saving it */ +{ + INT32 length; + JOCTET b[APPN_DATA_LEN]; + unsigned int i, numtoread; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* get the interesting part of the marker data */ + if (length >= APPN_DATA_LEN) + numtoread = APPN_DATA_LEN; + else if (length > 0) + numtoread = (unsigned int) length; + else + numtoread = 0; + for (i = 0; i < numtoread; i++) + INPUT_BYTE(cinfo, b[i], return FALSE); + length -= numtoread; + + /* process it */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + case M_APP14: + examine_app14(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + default: + /* can't get here unless jpeg_save_markers chooses wrong processor */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +#ifdef SAVE_MARKERS_SUPPORTED + +METHODDEF(boolean) +save_marker (j_decompress_ptr cinfo) +/* Save an APPn or COM marker into the marker list */ +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + jpeg_saved_marker_ptr cur_marker = marker->cur_marker; + unsigned int bytes_read, data_length; + JOCTET FAR * data; + INT32 length = 0; + INPUT_VARS(cinfo); + + if (cur_marker == NULL) { + /* begin reading a marker */ + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + if (length >= 0) { /* watch out for bogus length word */ + /* figure out how much we want to save */ + unsigned int limit; + if (cinfo->unread_marker == (int) M_COM) + limit = marker->length_limit_COM; + else + limit = marker->length_limit_APPn[cinfo->unread_marker - (int) M_APP0]; + if ((unsigned int) length < limit) + limit = (unsigned int) length; + /* allocate and initialize the marker item */ + cur_marker = (jpeg_saved_marker_ptr) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(struct jpeg_marker_struct) + limit); + cur_marker->next = NULL; + cur_marker->marker = (UINT8) cinfo->unread_marker; + cur_marker->original_length = (unsigned int) length; + cur_marker->data_length = limit; + /* data area is just beyond the jpeg_marker_struct */ + data = cur_marker->data = (JOCTET FAR *) (cur_marker + 1); + marker->cur_marker = cur_marker; + marker->bytes_read = 0; + bytes_read = 0; + data_length = limit; + } else { + /* deal with bogus length word */ + bytes_read = data_length = 0; + data = NULL; + } + } else { + /* resume reading a marker */ + bytes_read = marker->bytes_read; + data_length = cur_marker->data_length; + data = cur_marker->data + bytes_read; + } + + while (bytes_read < data_length) { + INPUT_SYNC(cinfo); /* move the restart point to here */ + marker->bytes_read = bytes_read; + /* If there's not at least one byte in buffer, suspend */ + MAKE_BYTE_AVAIL(cinfo, return FALSE); + /* Copy bytes with reasonable rapidity */ + while (bytes_read < data_length && bytes_in_buffer > 0) { + *data++ = *next_input_byte++; + bytes_in_buffer--; + bytes_read++; + } + } + + /* Done reading what we want to read */ + if (cur_marker != NULL) { /* will be NULL if bogus length word */ + /* Add new marker to end of list */ + if (cinfo->marker_list == NULL) { + cinfo->marker_list = cur_marker; + } else { + jpeg_saved_marker_ptr prev = cinfo->marker_list; + while (prev->next != NULL) + prev = prev->next; + prev->next = cur_marker; + } + /* Reset pointer & calc remaining data length */ + data = cur_marker->data; + length = cur_marker->original_length - data_length; + } + /* Reset to initial state for next marker */ + marker->cur_marker = NULL; + + /* Process the marker if interesting; else just make a generic trace msg */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, data, data_length, length); + break; + case M_APP14: + examine_app14(cinfo, data, data_length, length); + break; + default: + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, + (int) (data_length + length)); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +METHODDEF(boolean) +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL(boolean) +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * Note: This function may return a pseudo SOS marker (with zero + * component number) for treat by input controller's consume_input. + * consume_input itself should filter out (skip) the pseudo marker + * after processing for the caller. + */ + +METHODDEF(int) +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + if (! get_sof(cinfo, TRUE, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, FALSE, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*((my_marker_ptr) cinfo->marker)->process_APPn[ + cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*((my_marker_ptr) cinfo->marker)->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF(boolean) +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 3, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL(boolean) +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + marker->pub.saw_SOI = FALSE; /* set internal state too */ + marker->pub.saw_SOF = FALSE; + marker->pub.discarded_bytes = 0; + marker->cur_marker = NULL; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker; + int i; + + /* Create subobject in permanent pool */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_marker_reader)); + cinfo->marker = (struct jpeg_marker_reader *) marker; + /* Initialize public method pointers */ + marker->pub.reset_marker_reader = reset_marker_reader; + marker->pub.read_markers = read_markers; + marker->pub.read_restart_marker = read_restart_marker; + /* Initialize COM/APPn processing. + * By default, we examine and then discard APP0 and APP14, + * but simply discard COM and all other APPn. + */ + marker->process_COM = skip_variable; + marker->length_limit_COM = 0; + for (i = 0; i < 16; i++) { + marker->process_APPn[i] = skip_variable; + marker->length_limit_APPn[i] = 0; + } + marker->process_APPn[0] = get_interesting_appn; + marker->process_APPn[14] = get_interesting_appn; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} + + +/* + * Control saving of COM and APPn markers into marker_list. + */ + +#ifdef SAVE_MARKERS_SUPPORTED + +GLOBAL(void) +jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + long maxlength; + jpeg_marker_parser_method processor; + + /* Length limit mustn't be larger than what we can allocate + * (should only be a concern in a 16-bit environment). + */ + maxlength = cinfo->mem->max_alloc_chunk - SIZEOF(struct jpeg_marker_struct); + if (((long) length_limit) > maxlength) + length_limit = (unsigned int) maxlength; + + /* Choose processor routine to use. + * APP0/APP14 have special requirements. + */ + if (length_limit) { + processor = save_marker; + /* If saving APP0/APP14, save at least enough for our internal use. */ + if (marker_code == (int) M_APP0 && length_limit < APP0_DATA_LEN) + length_limit = APP0_DATA_LEN; + else if (marker_code == (int) M_APP14 && length_limit < APP14_DATA_LEN) + length_limit = APP14_DATA_LEN; + } else { + processor = skip_variable; + /* If discarding APP0/APP14, use our regular on-the-fly processor. */ + if (marker_code == (int) M_APP0 || marker_code == (int) M_APP14) + processor = get_interesting_appn; + } + + if (marker_code == (int) M_COM) { + marker->process_COM = processor; + marker->length_limit_COM = length_limit; + } else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) { + marker->process_APPn[marker_code - (int) M_APP0] = processor; + marker->length_limit_APPn[marker_code - (int) M_APP0] = length_limit; + } else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL(void) +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + if (marker_code == (int) M_COM) + marker->process_COM = routine; + else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) + marker->process_APPn[marker_code - (int) M_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} diff --git a/lib/jpeg/src/jdmarker.d b/lib/jpeg/src/jdmarker.d new file mode 100644 index 0000000..43f4fc1 --- /dev/null +++ b/lib/jpeg/src/jdmarker.d @@ -0,0 +1,14 @@ +jdmarker.o: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdmaster.c b/lib/jpeg/src/jdmaster.c new file mode 100644 index 0000000..01feb05 --- /dev/null +++ b/lib/jpeg/src/jdmaster.c @@ -0,0 +1,533 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL(boolean) +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_h_scaled_size != cinfo->min_DCT_h_scaled_size || + cinfo->comp_info[1].DCT_h_scaled_size != cinfo->min_DCT_h_scaled_size || + cinfo->comp_info[2].DCT_h_scaled_size != cinfo->min_DCT_h_scaled_size || + cinfo->comp_info[0].DCT_v_scaled_size != cinfo->min_DCT_v_scaled_size || + cinfo->comp_info[1].DCT_v_scaled_size != cinfo->min_DCT_v_scaled_size || + cinfo->comp_info[2].DCT_v_scaled_size != cinfo->min_DCT_v_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL(void) +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase. + * This function is used for full decompression. + */ +{ +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Compute core output image dimensions and DCT scaling choices. */ + jpeg_core_output_dimensions(cinfo); + +#ifdef IDCT_SCALING_SUPPORTED + + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code adapts subsampling ratios which are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = 1; + while (cinfo->min_DCT_h_scaled_size * ssize <= + (cinfo->do_fancy_upsampling ? DCTSIZE : DCTSIZE / 2) && + (cinfo->max_h_samp_factor % (compptr->h_samp_factor * ssize * 2)) == 0) { + ssize = ssize * 2; + } + compptr->DCT_h_scaled_size = cinfo->min_DCT_h_scaled_size * ssize; + ssize = 1; + while (cinfo->min_DCT_v_scaled_size * ssize <= + (cinfo->do_fancy_upsampling ? DCTSIZE : DCTSIZE / 2) && + (cinfo->max_v_samp_factor % (compptr->v_samp_factor * ssize * 2)) == 0) { + ssize = ssize * 2; + } + compptr->DCT_v_scaled_size = cinfo->min_DCT_v_scaled_size * ssize; + + /* We don't support IDCT ratios larger than 2. */ + if (compptr->DCT_h_scaled_size > compptr->DCT_v_scaled_size * 2) + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size * 2; + else if (compptr->DCT_v_scaled_size > compptr->DCT_h_scaled_size * 2) + compptr->DCT_v_scaled_size = compptr->DCT_h_scaled_size * 2; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_h_scaled_size), + (long) (cinfo->max_h_samp_factor * cinfo->block_size)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_v_scaled_size), + (long) (cinfo->max_v_samp_factor * cinfo->block_size)); + } + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL(void) +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL(void) +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) + jinit_arith_decoder(cinfo); + else { + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapistd.c will crank the pass to completion.) + */ + +METHODDEF(void) +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF(void) +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL(void) +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL(void) +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} diff --git a/lib/jpeg/src/jdmaster.d b/lib/jpeg/src/jdmaster.d new file mode 100644 index 0000000..7f35242 --- /dev/null +++ b/lib/jpeg/src/jdmaster.d @@ -0,0 +1,14 @@ +jdmaster.o: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdmerge.c b/lib/jpeg/src/jdmerge.c new file mode 100644 index 0000000..9e3a595 --- /dev/null +++ b/lib/jpeg/src/jdmerge.c @@ -0,0 +1,400 @@ +/* + * jdmerge.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains code for merged upsampling/color conversion. + * + * This file combines functions from jdsample.c and jdcolor.c; + * read those files first to understand what's going on. + * + * When the chroma components are to be upsampled by simple replication + * (ie, box filtering), we can save some work in color conversion by + * calculating all the output pixels corresponding to a pair of chroma + * samples at one time. In the conversion equations + * R = Y + K1 * Cr + * G = Y + K2 * Cb + K3 * Cr + * B = Y + K4 * Cb + * only the Y term varies among the group of pixels corresponding to a pair + * of chroma samples, so the rest of the terms can be calculated just once. + * At typical sampling ratios, this eliminates half or three-quarters of the + * multiplications needed for color conversion. + * + * This file currently provides implementations for the following cases: + * YCbCr => RGB color conversion only. + * Sampling ratios of 2h1v or 2h2v. + * No scaling needed at upsample time. + * Corner-aligned (non-CCIR601) sampling alignment. + * Other special cases could be added, but in most applications these are + * the only common cases. (For uncommon cases we fall back on the more + * general code in jdsample.c and jdcolor.c.) + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + JMETHOD(void, upmethod, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf)); + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + + /* For 2:1 vertical sampling, we produce two output rows at a time. + * We need a "spare" row buffer to hold the second output row if the + * application provides just a one-row buffer; we also use the spare + * to discard the dummy last row if the image height is odd. + */ + JSAMPROW spare_row; + boolean spare_full; /* T if spare buffer is occupied */ + + JDIMENSION out_row_width; /* samples per output row */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + * This is taken directly from jdcolor.c; see that file for more info. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int i; + INT32 x; + SHIFT_TEMPS + + upsample->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + upsample->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + upsample->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + upsample->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_merged_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the spare buffer empty */ + upsample->spare_full = FALSE; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * The control routine just handles the row buffering considerations. + */ + +METHODDEF(void) +merged_2v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 2:1 vertical sampling case: may need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPROW work_ptrs[2]; + JDIMENSION num_rows; /* number of rows returned to caller */ + + if (upsample->spare_full) { + /* If we have a spare row saved from a previous cycle, just return it. */ + jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + 1, upsample->out_row_width); + num_rows = 1; + upsample->spare_full = FALSE; + } else { + /* Figure number of rows to return to caller. */ + num_rows = 2; + /* Not more than the distance to the end of the image. */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + /* Create output pointer array for upsampler. */ + work_ptrs[0] = output_buf[*out_row_ctr]; + if (num_rows > 1) { + work_ptrs[1] = output_buf[*out_row_ctr + 1]; + } else { + work_ptrs[1] = upsample->spare_row; + upsample->spare_full = TRUE; + } + /* Now do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs); + } + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (! upsample->spare_full) + (*in_row_group_ctr)++; +} + + +METHODDEF(void) +merged_1v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 1:1 vertical sampling case: much easier, never need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Just do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, + output_buf + *out_row_ctr); + /* Adjust counts */ + (*out_row_ctr)++; + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by the control routines to do + * the actual upsampling/conversion. One row group is processed per call. + * + * Note: since we may be writing directly into application-supplied buffers, + * we have to be honest about the output width; we can't assume the buffer + * has been rounded up to an even width. + */ + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + */ + +METHODDEF(void) +h2v1_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr; + JSAMPROW inptr0, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr0 = input_buf[0][in_row_group_ctr]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr = output_buf[0]; + /* Loop for each pair of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 2 Y values and emit 2 pixels */ + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr0); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + */ + +METHODDEF(void) +h2v2_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr0, outptr1; + JSAMPROW inptr00, inptr01, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr00 = input_buf[0][in_row_group_ctr*2]; + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr0 = output_buf[0]; + outptr1 = output_buf[1]; + /* Loop for each group of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 4 Y values and emit 4 pixels */ + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr00); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + y = GETJSAMPLE(*inptr01); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Module initialization routine for merged upsampling/color conversion. + * + * NB: this is called under the conditions determined by use_merged_upsample() + * in jdmaster.c. That routine MUST correspond to the actual capabilities + * of this module; no safety checks are made here. + */ + +GLOBAL(void) +jinit_merged_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_merged_upsample; + upsample->pub.need_context_rows = FALSE; + + upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; + + if (cinfo->max_v_samp_factor == 2) { + upsample->pub.upsample = merged_2v_upsample; + upsample->upmethod = h2v2_merged_upsample; + /* Allocate a spare row buffer */ + upsample->spare_row = (JSAMPROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (upsample->out_row_width * SIZEOF(JSAMPLE))); + } else { + upsample->pub.upsample = merged_1v_upsample; + upsample->upmethod = h2v1_merged_upsample; + /* No spare row needed */ + upsample->spare_row = NULL; + } + + build_ycc_rgb_table(cinfo); +} + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ diff --git a/lib/jpeg/src/jdmerge.d b/lib/jpeg/src/jdmerge.d new file mode 100644 index 0000000..b9208fc --- /dev/null +++ b/lib/jpeg/src/jdmerge.d @@ -0,0 +1,14 @@ +jdmerge.o: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdpostct.c b/lib/jpeg/src/jdpostct.c new file mode 100644 index 0000000..7ba9eed --- /dev/null +++ b/lib/jpeg/src/jdpostct.c @@ -0,0 +1,290 @@ +/* + * jdpostct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the decompression postprocessing controller. + * This controller manages the upsampling, color conversion, and color + * quantization/reduction steps; specifically, it controls the buffering + * between upsample/color conversion and color quantization/reduction. + * + * If no color quantization/reduction is required, then this module has no + * work to do, and it just hands off to the upsample/color conversion code. + * An integrated upsample/convert/quantize process would replace this module + * entirely. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + + +/* Forward declarations */ +METHODDEF(void) post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF(void) post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF(void) +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_2pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize postprocessing controller. + */ + +GLOBAL(void) +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} diff --git a/lib/jpeg/src/jdpostct.d b/lib/jpeg/src/jdpostct.d new file mode 100644 index 0000000..1500272 --- /dev/null +++ b/lib/jpeg/src/jdpostct.d @@ -0,0 +1,14 @@ +jdpostct.o: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdsample.c b/lib/jpeg/src/jdsample.c new file mode 100644 index 0000000..94f9599 --- /dev/null +++ b/lib/jpeg/src/jdsample.c @@ -0,0 +1,361 @@ +/* + * jdsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * Modified 2002-2008 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains upsampling routines. + * + * Upsampling input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_v_scaled_size / min_DCT_v_scaled_size) + * sample rows of each component. Upsampling will normally produce + * max_v_samp_factor pixel rows from each row group (but this could vary + * if the upsampler is applying a scale factor of its own). + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF(void) +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF(void) +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF(void) +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF(void) +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int outrow; + + for (outrow = 0; outrow < cinfo->max_v_samp_factor; outrow++) { + inptr = input_data[outrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL(void) +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_h_scaled_size) / + cinfo->min_DCT_h_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_v_scaled_size) / + cinfo->min_DCT_v_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special case for 2h1v upsampling */ + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special case for 2h2v upsampling */ + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/lib/jpeg/src/jdsample.d b/lib/jpeg/src/jdsample.d new file mode 100644 index 0000000..420628d --- /dev/null +++ b/lib/jpeg/src/jdsample.d @@ -0,0 +1,14 @@ +jdsample.o: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jdtrans.c b/lib/jpeg/src/jdtrans.c new file mode 100644 index 0000000..a51d69d --- /dev/null +++ b/lib/jpeg/src/jdtrans.c @@ -0,0 +1,140 @@ +/* + * jdtrans.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * Modified 2000-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding decompression, + * that is, reading raw DCT coefficient arrays from an input JPEG file. + * The routines in jdapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * An alternative usage is to simply obtain access to the coefficient arrays + * during a buffered-image-mode decompression operation. This is allowed + * after any jpeg_finish_output() call. The arrays can be accessed until + * jpeg_finish_decompress() is called. (Note that any call to the library + * may reposition the arrays, so don't rely on access_virt_barray() results + * to stay valid across library calls.) + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL(jvirt_barray_ptr *) +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } + if (cinfo->global_state == DSTATE_RDCOEFS) { + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + } + /* At this point we should be in state DSTATE_STOPPING if being used + * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access + * to the coefficients during a full buffered-image-mode decompression. + */ + if ((cinfo->global_state == DSTATE_STOPPING || + cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { + return cinfo->coef->coef_arrays; + } + /* Oops, improper usage */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return NULL; /* keep compiler happy */ +} + + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL(void) +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* This is effectively a buffered-image operation. */ + cinfo->buffered_image = TRUE; + + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(cinfo); + + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) + jinit_arith_decoder(cinfo); + else { + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} diff --git a/lib/jpeg/src/jdtrans.d b/lib/jpeg/src/jdtrans.d new file mode 100644 index 0000000..f265c62 --- /dev/null +++ b/lib/jpeg/src/jdtrans.d @@ -0,0 +1,14 @@ +jdtrans.o: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jerror.c b/lib/jpeg/src/jerror.c new file mode 100644 index 0000000..c98aed7 --- /dev/null +++ b/lib/jpeg/src/jerror.c @@ -0,0 +1,252 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile, + * you get a Windows-specific hack to display error messages in a dialog box. + * It ain't much, but it beats dropping error messages into the bit bucket, + * which is what happens to output to stderr under most Windows C compilers. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +#ifdef USE_WINDOWS_MESSAGEBOX +#include +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF(void) +error_exit (j_common_ptr cinfo) +{ + /* Always display the message */ + (*cinfo->err->output_message) (cinfo); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + exit(EXIT_FAILURE); +} + + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + * + * On Windows, printing to stderr is generally completely useless, + * so we provide optional code to produce an error-dialog popup. + * Most Windows applications will still prefer to override this routine, + * but if they don't, it'll do something at least marginally useful. + * + * NOTE: to use the library in an environment that doesn't support the + * C stdio library, you may have to delete the call to fprintf() entirely, + * not just not use this routine. + */ + +METHODDEF(void) +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + +#ifdef USE_WINDOWS_MESSAGEBOX + /* Display it in a message dialog box */ + MessageBox(GetActiveWindow(), buffer, "JPEG Library Error", + MB_OK | MB_ICONERROR); +#else + /* Send it to stderr, adding a newline */ + fprintf(stderr, "%s\n", buffer); +#endif +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF(void) +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF(void) +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF(void) +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL(struct jpeg_error_mgr *) +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/lib/jpeg/src/jerror.d b/lib/jpeg/src/jerror.d new file mode 100644 index 0000000..a4332eb --- /dev/null +++ b/lib/jpeg/src/jerror.d @@ -0,0 +1,14 @@ +jerror.o: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jversion.h: + +jerror.h: diff --git a/lib/jpeg/src/jerror.h b/lib/jpeg/src/jerror.h new file mode 100644 index 0000000..478b74d --- /dev/null +++ b/lib/jpeg/src/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/lib/jpeg/src/jfdctflt.c b/lib/jpeg/src/jfdctflt.c new file mode 100644 index 0000000..3c1b174 --- /dev/null +++ b/lib/jpeg/src/jfdctflt.c @@ -0,0 +1,174 @@ +/* + * jfdctflt.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2003-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * forward DCT (Discrete Cosine Transform). + * + * This implementation should be more accurate than either of the integer + * DCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_float (FAST_FLOAT * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + FAST_FLOAT *dataptr; + JSAMPROW elemptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Load data into workspace */ + tmp0 = (FAST_FLOAT) (GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[7])); + tmp7 = (FAST_FLOAT) (GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[7])); + tmp1 = (FAST_FLOAT) (GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[6])); + tmp6 = (FAST_FLOAT) (GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[6])); + tmp2 = (FAST_FLOAT) (GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[5])); + tmp5 = (FAST_FLOAT) (GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[5])); + tmp3 = (FAST_FLOAT) (GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[4])); + tmp4 = (FAST_FLOAT) (GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[4])); + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Apply unsigned->signed conversion */ + dataptr[0] = tmp10 + tmp11 - 8 * CENTERJSAMPLE; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/lib/jpeg/src/jfdctflt.d b/lib/jpeg/src/jfdctflt.d new file mode 100644 index 0000000..b959482 --- /dev/null +++ b/lib/jpeg/src/jfdctflt.d @@ -0,0 +1,16 @@ +jfdctflt.o: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jfdctfst.c b/lib/jpeg/src/jfdctfst.c new file mode 100644 index 0000000..82b9231 --- /dev/null +++ b/lib/jpeg/src/jfdctfst.c @@ -0,0 +1,230 @@ +/* + * jfdctfst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2003-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jfdctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * Again to save a few shifts, the intermediate results between pass 1 and + * pass 2 are not upscaled, but are represented only to integral precision. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#define CONST_BITS 8 + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ +#else +#define FIX_0_382683433 FIX(0.382683433) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_707106781 FIX(0.707106781) +#define FIX_1_306562965 FIX(1.306562965) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_ifast (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z1, z2, z3, z4, z5, z11, z13; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Load data into workspace */ + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[7]); + tmp7 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[6]); + tmp6 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[5]); + tmp5 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[4]); + tmp4 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[4]); + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Apply unsigned->signed conversion */ + dataptr[0] = tmp10 + tmp11 - 8 * CENTERJSAMPLE; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/lib/jpeg/src/jfdctfst.d b/lib/jpeg/src/jfdctfst.d new file mode 100644 index 0000000..aa73c70 --- /dev/null +++ b/lib/jpeg/src/jfdctfst.d @@ -0,0 +1,16 @@ +jfdctfst.o: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jfdctint.c b/lib/jpeg/src/jfdctint.c new file mode 100644 index 0000000..529eaf8 --- /dev/null +++ b/lib/jpeg/src/jfdctint.c @@ -0,0 +1,4348 @@ +/* + * jfdctint.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * Modification developed 2003-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + * + * We also provide FDCT routines with various input sample block sizes for + * direct resolution reduction or enlargement and for direct resolving the + * common 2x1 and 1x2 subsampling cases without additional resampling: NxN + * (N=1...16), 2NxN, and Nx2N (N=1...8) pixels for one 8x8 output DCT block. + * + * For N<8 we fill the remaining block coefficients with zero. + * For N>8 we apply a partial N-point FDCT on the input samples, computing + * just the lower 8 frequency coefficients and discarding the rest. + * + * We must scale the output coefficients of the N-point FDCT appropriately + * to the standard 8-point FDCT level by 8/N per 1-D pass. This scaling + * is folded into the constant multipliers (pass 2) and/or final/initial + * shifting. + * + * CAUTION: We rely on the FIX() macro except for the N=1,2,4,8 cases + * since there would be too many additional constants to pre-calculate. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCT blocks. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D DCT step produces outputs which are a factor of sqrt(N) + * larger than the true DCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D DCT, + * because the y0 and y4 outputs need not be divided by sqrt(N). + * In the IJG code, this factor of 8 is removed by the quantization step + * (in jcdctmgr.c), NOT in this module. + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (For 12-bit sample data, the intermediate + * array is INT32 anyway.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_islow (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[4]); + + tmp10 = tmp0 + tmp3; + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[4]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) ((tmp10 + tmp11 - 8 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + dataptr[2] = (DCTELEM) RIGHT_SHIFT(z1 + MULTIPLY(tmp12, FIX_0_765366865), + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) RIGHT_SHIFT(z1 - MULTIPLY(tmp13, FIX_1_847759065), + CONST_BITS-PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + tmp10 + tmp12, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) + RIGHT_SHIFT(tmp1 + tmp11 + tmp13, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) + RIGHT_SHIFT(tmp2 + tmp11 + tmp12, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) + RIGHT_SHIFT(tmp3 + tmp10 + tmp13, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + /* Add fudge factor here for final descale. */ + tmp10 = tmp0 + tmp3 + (ONE << (PASS1_BITS-1)); + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + dataptr[DCTSIZE*0] = (DCTELEM) RIGHT_SHIFT(tmp10 + tmp11, PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) RIGHT_SHIFT(tmp10 - tmp11, PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS+PASS1_BITS-1); + dataptr[DCTSIZE*2] = (DCTELEM) + RIGHT_SHIFT(z1 + MULTIPLY(tmp12, FIX_0_765366865), CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) + RIGHT_SHIFT(z1 - MULTIPLY(tmp13, FIX_1_847759065), CONST_BITS+PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS+PASS1_BITS-1); + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[DCTSIZE*1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + tmp10 + tmp12, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + RIGHT_SHIFT(tmp1 + tmp11 + tmp13, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) + RIGHT_SHIFT(tmp2 + tmp11 + tmp12, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*7] = (DCTELEM) + RIGHT_SHIFT(tmp3 + tmp10 + tmp13, CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + +#ifdef DCT_SCALING_SUPPORTED + + +/* + * Perform the forward DCT on a 7x7 sample block. + */ + +GLOBAL(void) +jpeg_fdct_7x7 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12; + INT32 z1, z2, z3; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* cK represents sqrt(2) * cos(K*pi/14). */ + + dataptr = data; + for (ctr = 0; ctr < 7; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[6]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[5]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[4]); + tmp3 = GETJSAMPLE(elemptr[3]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[6]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[5]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[4]); + + z1 = tmp0 + tmp2; + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((z1 + tmp1 + tmp3 - 7 * CENTERJSAMPLE) << PASS1_BITS); + tmp3 += tmp3; + z1 -= tmp3; + z1 -= tmp3; + z1 = MULTIPLY(z1, FIX(0.353553391)); /* (c2+c6-c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp2, FIX(0.920609002)); /* (c2+c4-c6)/2 */ + z3 = MULTIPLY(tmp1 - tmp2, FIX(0.314692123)); /* c6 */ + dataptr[2] = (DCTELEM) DESCALE(z1 + z2 + z3, CONST_BITS-PASS1_BITS); + z1 -= z2; + z2 = MULTIPLY(tmp0 - tmp1, FIX(0.881747734)); /* c4 */ + dataptr[4] = (DCTELEM) + DESCALE(z2 + z3 - MULTIPLY(tmp1 - tmp3, FIX(0.707106781)), /* c2+c6-c4 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(tmp10 - tmp11, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(tmp11 + tmp12, - FIX(1.378756276)); /* -c1 */ + tmp1 += tmp2; + tmp3 = MULTIPLY(tmp10 + tmp12, FIX(0.613604268)); /* c5 */ + tmp0 += tmp3; + tmp2 += tmp3 + MULTIPLY(tmp12, FIX(1.870828693)); /* c3+c1-c5 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp0, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp1, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp2, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/7)**2 = 64/49, which we fold + * into the constant multipliers: + * cK now represents sqrt(2) * cos(K*pi/14) * 64/49. + */ + + dataptr = data; + for (ctr = 0; ctr < 7; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*6]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*5]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*4]; + tmp3 = dataptr[DCTSIZE*3]; + + tmp10 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*6]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*5]; + tmp12 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*4]; + + z1 = tmp0 + tmp2; + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(z1 + tmp1 + tmp3, FIX(1.306122449)), /* 64/49 */ + CONST_BITS+PASS1_BITS); + tmp3 += tmp3; + z1 -= tmp3; + z1 -= tmp3; + z1 = MULTIPLY(z1, FIX(0.461784020)); /* (c2+c6-c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp2, FIX(1.202428084)); /* (c2+c4-c6)/2 */ + z3 = MULTIPLY(tmp1 - tmp2, FIX(0.411026446)); /* c6 */ + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + z2 + z3, CONST_BITS+PASS1_BITS); + z1 -= z2; + z2 = MULTIPLY(tmp0 - tmp1, FIX(1.151670509)); /* c4 */ + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(z2 + z3 - MULTIPLY(tmp1 - tmp3, FIX(0.923568041)), /* c2+c6-c4 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.221765677)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(tmp10 - tmp11, FIX(0.222383464)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(tmp11 + tmp12, - FIX(1.800824523)); /* -c1 */ + tmp1 += tmp2; + tmp3 = MULTIPLY(tmp10 + tmp12, FIX(0.801442310)); /* c5 */ + tmp0 += tmp3; + tmp2 += tmp3 + MULTIPLY(tmp12, FIX(2.443531355)); /* c3+c1-c5 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2, CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 6x6 sample block. + */ + +GLOBAL(void) +jpeg_fdct_6x6 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2; + INT32 tmp10, tmp11, tmp12; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* cK represents sqrt(2) * cos(K*pi/12). */ + + dataptr = data; + for (ctr = 0; ctr < 6; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[5]); + tmp11 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[3]); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[5]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[3]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 - 6 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(1.224744871)), /* c2 */ + CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(0.707106781)), /* c4 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = DESCALE(MULTIPLY(tmp0 + tmp2, FIX(0.366025404)), /* c5 */ + CONST_BITS-PASS1_BITS); + + dataptr[1] = (DCTELEM) (tmp10 + ((tmp0 + tmp1) << PASS1_BITS)); + dataptr[3] = (DCTELEM) ((tmp0 - tmp1 - tmp2) << PASS1_BITS); + dataptr[5] = (DCTELEM) (tmp10 + ((tmp2 - tmp1) << PASS1_BITS)); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/6)**2 = 16/9, which we fold + * into the constant multipliers: + * cK now represents sqrt(2) * cos(K*pi/12) * 16/9. + */ + + dataptr = data; + for (ctr = 0; ctr < 6; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*5]; + tmp11 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*3]; + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*3]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(2.177324216)), /* c2 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(1.257078722)), /* c4 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp2, FIX(0.650711829)); /* c5 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0 + tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp2, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp2 - tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 5x5 sample block. + */ + +GLOBAL(void) +jpeg_fdct_5x5 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2; + INT32 tmp10, tmp11; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* cK represents sqrt(2) * cos(K*pi/10). */ + + dataptr = data; + for (ctr = 0; ctr < 5; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[4]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[3]); + tmp2 = GETJSAMPLE(elemptr[2]); + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[4]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[3]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp2 - 5 * CENTERJSAMPLE) << (PASS1_BITS+1)); + tmp11 = MULTIPLY(tmp11, FIX(0.790569415)); /* (c2+c4)/2 */ + tmp10 -= tmp2 << 2; + tmp10 = MULTIPLY(tmp10, FIX(0.353553391)); /* (c2-c4)/2 */ + dataptr[2] = (DCTELEM) DESCALE(tmp11 + tmp10, CONST_BITS-PASS1_BITS-1); + dataptr[4] = (DCTELEM) DESCALE(tmp11 - tmp10, CONST_BITS-PASS1_BITS-1); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp1, FIX(0.831253876)); /* c3 */ + + dataptr[1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0, FIX(0.513743148)), /* c1-c3 */ + CONST_BITS-PASS1_BITS-1); + dataptr[3] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp1, FIX(2.176250899)), /* c1+c3 */ + CONST_BITS-PASS1_BITS-1); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/5)**2 = 64/25, which we partially + * fold into the constant multipliers (other part was done in pass 1): + * cK now represents sqrt(2) * cos(K*pi/10) * 32/25. + */ + + dataptr = data; + for (ctr = 0; ctr < 5; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*4]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*3]; + tmp2 = dataptr[DCTSIZE*2]; + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*4]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*3]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp2, FIX(1.28)), /* 32/25 */ + CONST_BITS+PASS1_BITS); + tmp11 = MULTIPLY(tmp11, FIX(1.011928851)); /* (c2+c4)/2 */ + tmp10 -= tmp2 << 2; + tmp10 = MULTIPLY(tmp10, FIX(0.452548340)); /* (c2-c4)/2 */ + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp11 + tmp10, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp11 - tmp10, CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp1, FIX(1.064004961)); /* c3 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0, FIX(0.657591230)), /* c1-c3 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp1, FIX(2.785601151)), /* c1+c3 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 4x4 sample block. + */ + +GLOBAL(void) +jpeg_fdct_4x4 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1; + INT32 tmp10, tmp11; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We must also scale the output by (8/4)**2 = 2**2, which we add here. */ + /* cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point FDCT]. */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[3]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[2]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[3]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[2]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 - 4 * CENTERJSAMPLE) << (PASS1_BITS+2)); + dataptr[2] = (DCTELEM) ((tmp0 - tmp1) << (PASS1_BITS+2)); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-3); + + dataptr[1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS-PASS1_BITS-2); + dataptr[3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS-PASS1_BITS-2); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*3] + (ONE << (PASS1_BITS-1)); + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*2]; + + tmp10 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*3]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*2]; + + dataptr[DCTSIZE*0] = (DCTELEM) RIGHT_SHIFT(tmp0 + tmp1, PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) RIGHT_SHIFT(tmp0 - tmp1, PASS1_BITS); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS+PASS1_BITS-1); + + dataptr[DCTSIZE*1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 3x3 sample block. + */ + +GLOBAL(void) +jpeg_fdct_3x3 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We scale the results further by 2**2 as part of output adaption */ + /* scaling for different DCT size. */ + /* cK represents sqrt(2) * cos(K*pi/6). */ + + dataptr = data; + for (ctr = 0; ctr < 3; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[2]); + tmp1 = GETJSAMPLE(elemptr[1]); + + tmp2 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[2]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 - 3 * CENTERJSAMPLE) << (PASS1_BITS+2)); + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp1, FIX(0.707106781)), /* c2 */ + CONST_BITS-PASS1_BITS-2); + + /* Odd part */ + + dataptr[1] = (DCTELEM) + DESCALE(MULTIPLY(tmp2, FIX(1.224744871)), /* c1 */ + CONST_BITS-PASS1_BITS-2); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/3)**2 = 64/9, which we partially + * fold into the constant multipliers (other part was done in pass 1): + * cK now represents sqrt(2) * cos(K*pi/6) * 16/9. + */ + + dataptr = data; + for (ctr = 0; ctr < 3; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*2]; + tmp1 = dataptr[DCTSIZE*1]; + + tmp2 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*2]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 + tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp1, FIX(1.257078722)), /* c2 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(MULTIPLY(tmp2, FIX(2.177324216)), /* c1 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 2x2 sample block. + */ + +GLOBAL(void) +jpeg_fdct_2x2 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + JSAMPROW elemptr; + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + + /* Row 0 */ + elemptr = sample_data[0] + start_col; + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[1]); + tmp1 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[1]); + + /* Row 1 */ + elemptr = sample_data[1] + start_col; + + tmp2 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[1]); + tmp3 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[1]); + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/2)**2 = 2**4. + */ + + /* Column 0 */ + /* Apply unsigned->signed conversion */ + data[DCTSIZE*0] = (DCTELEM) ((tmp0 + tmp2 - 4 * CENTERJSAMPLE) << 4); + data[DCTSIZE*1] = (DCTELEM) ((tmp0 - tmp2) << 4); + + /* Column 1 */ + data[DCTSIZE*0+1] = (DCTELEM) ((tmp1 + tmp3) << 4); + data[DCTSIZE*1+1] = (DCTELEM) ((tmp1 - tmp3) << 4); +} + + +/* + * Perform the forward DCT on a 1x1 sample block. + */ + +GLOBAL(void) +jpeg_fdct_1x1 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* We leave the result scaled up by an overall factor of 8. */ + /* We must also scale the output by (8/1)**2 = 2**6. */ + /* Apply unsigned->signed conversion */ + data[0] = (DCTELEM) + ((GETJSAMPLE(sample_data[0][start_col]) - CENTERJSAMPLE) << 6); +} + + +/* + * Perform the forward DCT on a 9x9 sample block. + */ + +GLOBAL(void) +jpeg_fdct_9x9 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2; + DCTELEM workspace[8]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* we scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* cK represents sqrt(2) * cos(K*pi/18). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[8]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[7]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[6]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[5]); + tmp4 = GETJSAMPLE(elemptr[4]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[8]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[7]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[6]); + tmp13 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[5]); + + z1 = tmp0 + tmp2 + tmp3; + z2 = tmp1 + tmp4; + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) ((z1 + z2 - 9 * CENTERJSAMPLE) << 1); + dataptr[6] = (DCTELEM) + DESCALE(MULTIPLY(z1 - z2 - z2, FIX(0.707106781)), /* c6 */ + CONST_BITS-1); + z1 = MULTIPLY(tmp0 - tmp2, FIX(1.328926049)); /* c2 */ + z2 = MULTIPLY(tmp1 - tmp4 - tmp4, FIX(0.707106781)); /* c6 */ + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp2 - tmp3, FIX(1.083350441)) /* c4 */ + + z1 + z2, CONST_BITS-1); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp3 - tmp0, FIX(0.245575608)) /* c8 */ + + z1 - z2, CONST_BITS-1); + + /* Odd part */ + + dataptr[3] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12 - tmp13, FIX(1.224744871)), /* c3 */ + CONST_BITS-1); + + tmp11 = MULTIPLY(tmp11, FIX(1.224744871)); /* c3 */ + tmp0 = MULTIPLY(tmp10 + tmp12, FIX(0.909038955)); /* c5 */ + tmp1 = MULTIPLY(tmp10 + tmp13, FIX(0.483689525)); /* c7 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp0 + tmp1, CONST_BITS-1); + + tmp2 = MULTIPLY(tmp12 - tmp13, FIX(1.392728481)); /* c1 */ + + dataptr[5] = (DCTELEM) DESCALE(tmp0 - tmp11 - tmp2, CONST_BITS-1); + dataptr[7] = (DCTELEM) DESCALE(tmp1 - tmp11 + tmp2, CONST_BITS-1); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 9) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/9)**2 = 64/81, which we partially + * fold into the constant multipliers and final/initial shifting: + * cK now represents sqrt(2) * cos(K*pi/18) * 128/81. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*0]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*7]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*6]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*5]; + tmp4 = dataptr[DCTSIZE*4]; + + tmp10 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*0]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*7]; + tmp12 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*6]; + tmp13 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*5]; + + z1 = tmp0 + tmp2 + tmp3; + z2 = tmp1 + tmp4; + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(z1 + z2, FIX(1.580246914)), /* 128/81 */ + CONST_BITS+2); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(MULTIPLY(z1 - z2 - z2, FIX(1.117403309)), /* c6 */ + CONST_BITS+2); + z1 = MULTIPLY(tmp0 - tmp2, FIX(2.100031287)); /* c2 */ + z2 = MULTIPLY(tmp1 - tmp4 - tmp4, FIX(1.117403309)); /* c6 */ + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp2 - tmp3, FIX(1.711961190)) /* c4 */ + + z1 + z2, CONST_BITS+2); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp3 - tmp0, FIX(0.388070096)) /* c8 */ + + z1 - z2, CONST_BITS+2); + + /* Odd part */ + + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12 - tmp13, FIX(1.935399303)), /* c3 */ + CONST_BITS+2); + + tmp11 = MULTIPLY(tmp11, FIX(1.935399303)); /* c3 */ + tmp0 = MULTIPLY(tmp10 + tmp12, FIX(1.436506004)); /* c5 */ + tmp1 = MULTIPLY(tmp10 + tmp13, FIX(0.764348879)); /* c7 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp11 + tmp0 + tmp1, CONST_BITS+2); + + tmp2 = MULTIPLY(tmp12 - tmp13, FIX(2.200854883)); /* c1 */ + + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp0 - tmp11 - tmp2, CONST_BITS+2); + dataptr[DCTSIZE*7] = (DCTELEM) + DESCALE(tmp1 - tmp11 + tmp2, CONST_BITS+2); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 10x10 sample block. + */ + +GLOBAL(void) +jpeg_fdct_10x10 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + DCTELEM workspace[8*2]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* we scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* cK represents sqrt(2) * cos(K*pi/20). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[9]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[8]); + tmp12 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[7]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[6]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[5]); + + tmp10 = tmp0 + tmp4; + tmp13 = tmp0 - tmp4; + tmp11 = tmp1 + tmp3; + tmp14 = tmp1 - tmp3; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[9]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[8]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[7]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[6]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[5]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 - 10 * CENTERJSAMPLE) << 1); + tmp12 += tmp12; + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.144122806)) - /* c4 */ + MULTIPLY(tmp11 - tmp12, FIX(0.437016024)), /* c8 */ + CONST_BITS-1); + tmp10 = MULTIPLY(tmp13 + tmp14, FIX(0.831253876)); /* c6 */ + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp13, FIX(0.513743148)), /* c2-c6 */ + CONST_BITS-1); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(2.176250899)), /* c2+c6 */ + CONST_BITS-1); + + /* Odd part */ + + tmp10 = tmp0 + tmp4; + tmp11 = tmp1 - tmp3; + dataptr[5] = (DCTELEM) ((tmp10 - tmp11 - tmp2) << 1); + tmp2 <<= CONST_BITS; + dataptr[1] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.396802247)) + /* c1 */ + MULTIPLY(tmp1, FIX(1.260073511)) + tmp2 + /* c3 */ + MULTIPLY(tmp3, FIX(0.642039522)) + /* c7 */ + MULTIPLY(tmp4, FIX(0.221231742)), /* c9 */ + CONST_BITS-1); + tmp12 = MULTIPLY(tmp0 - tmp4, FIX(0.951056516)) - /* (c3+c7)/2 */ + MULTIPLY(tmp1 + tmp3, FIX(0.587785252)); /* (c1-c9)/2 */ + tmp13 = MULTIPLY(tmp10 + tmp11, FIX(0.309016994)) + /* (c3-c7)/2 */ + (tmp11 << (CONST_BITS - 1)) - tmp2; + dataptr[3] = (DCTELEM) DESCALE(tmp12 + tmp13, CONST_BITS-1); + dataptr[7] = (DCTELEM) DESCALE(tmp12 - tmp13, CONST_BITS-1); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 10) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/10)**2 = 16/25, which we partially + * fold into the constant multipliers and final/initial shifting: + * cK now represents sqrt(2) * cos(K*pi/20) * 32/25. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*1]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*0]; + tmp12 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*7]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*6]; + tmp4 = dataptr[DCTSIZE*4] + dataptr[DCTSIZE*5]; + + tmp10 = tmp0 + tmp4; + tmp13 = tmp0 - tmp4; + tmp11 = tmp1 + tmp3; + tmp14 = tmp1 - tmp3; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*1]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*0]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*7]; + tmp3 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*6]; + tmp4 = dataptr[DCTSIZE*4] - dataptr[DCTSIZE*5]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12, FIX(1.28)), /* 32/25 */ + CONST_BITS+2); + tmp12 += tmp12; + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.464477191)) - /* c4 */ + MULTIPLY(tmp11 - tmp12, FIX(0.559380511)), /* c8 */ + CONST_BITS+2); + tmp10 = MULTIPLY(tmp13 + tmp14, FIX(1.064004961)); /* c6 */ + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp13, FIX(0.657591230)), /* c2-c6 */ + CONST_BITS+2); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(2.785601151)), /* c2+c6 */ + CONST_BITS+2); + + /* Odd part */ + + tmp10 = tmp0 + tmp4; + tmp11 = tmp1 - tmp3; + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp2, FIX(1.28)), /* 32/25 */ + CONST_BITS+2); + tmp2 = MULTIPLY(tmp2, FIX(1.28)); /* 32/25 */ + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.787906876)) + /* c1 */ + MULTIPLY(tmp1, FIX(1.612894094)) + tmp2 + /* c3 */ + MULTIPLY(tmp3, FIX(0.821810588)) + /* c7 */ + MULTIPLY(tmp4, FIX(0.283176630)), /* c9 */ + CONST_BITS+2); + tmp12 = MULTIPLY(tmp0 - tmp4, FIX(1.217352341)) - /* (c3+c7)/2 */ + MULTIPLY(tmp1 + tmp3, FIX(0.752365123)); /* (c1-c9)/2 */ + tmp13 = MULTIPLY(tmp10 + tmp11, FIX(0.395541753)) + /* (c3-c7)/2 */ + MULTIPLY(tmp11, FIX(0.64)) - tmp2; /* 16/25 */ + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp12 + tmp13, CONST_BITS+2); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp12 - tmp13, CONST_BITS+2); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on an 11x11 sample block. + */ + +GLOBAL(void) +jpeg_fdct_11x11 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 z1, z2, z3; + DCTELEM workspace[8*3]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* we scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* cK represents sqrt(2) * cos(K*pi/22). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[10]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[9]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[8]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[7]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[6]); + tmp5 = GETJSAMPLE(elemptr[5]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[10]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[9]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[8]); + tmp13 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[7]); + tmp14 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[6]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 + tmp2 + tmp3 + tmp4 + tmp5 - 11 * CENTERJSAMPLE) << 1); + tmp5 += tmp5; + tmp0 -= tmp5; + tmp1 -= tmp5; + tmp2 -= tmp5; + tmp3 -= tmp5; + tmp4 -= tmp5; + z1 = MULTIPLY(tmp0 + tmp3, FIX(1.356927976)) + /* c2 */ + MULTIPLY(tmp2 + tmp4, FIX(0.201263574)); /* c10 */ + z2 = MULTIPLY(tmp1 - tmp3, FIX(0.926112931)); /* c6 */ + z3 = MULTIPLY(tmp0 - tmp1, FIX(1.189712156)); /* c4 */ + dataptr[2] = (DCTELEM) + DESCALE(z1 + z2 - MULTIPLY(tmp3, FIX(1.018300590)) /* c2+c8-c6 */ + - MULTIPLY(tmp4, FIX(1.390975730)), /* c4+c10 */ + CONST_BITS-1); + dataptr[4] = (DCTELEM) + DESCALE(z2 + z3 + MULTIPLY(tmp1, FIX(0.062335650)) /* c4-c6-c10 */ + - MULTIPLY(tmp2, FIX(1.356927976)) /* c2 */ + + MULTIPLY(tmp4, FIX(0.587485545)), /* c8 */ + CONST_BITS-1); + dataptr[6] = (DCTELEM) + DESCALE(z1 + z3 - MULTIPLY(tmp0, FIX(1.620527200)) /* c2+c4-c6 */ + - MULTIPLY(tmp2, FIX(0.788749120)), /* c8+c10 */ + CONST_BITS-1); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.286413905)); /* c3 */ + tmp2 = MULTIPLY(tmp10 + tmp12, FIX(1.068791298)); /* c5 */ + tmp3 = MULTIPLY(tmp10 + tmp13, FIX(0.764581576)); /* c7 */ + tmp0 = tmp1 + tmp2 + tmp3 - MULTIPLY(tmp10, FIX(1.719967871)) /* c7+c5+c3-c1 */ + + MULTIPLY(tmp14, FIX(0.398430003)); /* c9 */ + tmp4 = MULTIPLY(tmp11 + tmp12, - FIX(0.764581576)); /* -c7 */ + tmp5 = MULTIPLY(tmp11 + tmp13, - FIX(1.399818907)); /* -c1 */ + tmp1 += tmp4 + tmp5 + MULTIPLY(tmp11, FIX(1.276416582)) /* c9+c7+c1-c3 */ + - MULTIPLY(tmp14, FIX(1.068791298)); /* c5 */ + tmp10 = MULTIPLY(tmp12 + tmp13, FIX(0.398430003)); /* c9 */ + tmp2 += tmp4 + tmp10 - MULTIPLY(tmp12, FIX(1.989053629)) /* c9+c5+c3-c7 */ + + MULTIPLY(tmp14, FIX(1.399818907)); /* c1 */ + tmp3 += tmp5 + tmp10 + MULTIPLY(tmp13, FIX(1.305598626)) /* c1+c5-c9-c7 */ + - MULTIPLY(tmp14, FIX(1.286413905)); /* c3 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp0, CONST_BITS-1); + dataptr[3] = (DCTELEM) DESCALE(tmp1, CONST_BITS-1); + dataptr[5] = (DCTELEM) DESCALE(tmp2, CONST_BITS-1); + dataptr[7] = (DCTELEM) DESCALE(tmp3, CONST_BITS-1); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 11) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/11)**2 = 64/121, which we partially + * fold into the constant multipliers and final/initial shifting: + * cK now represents sqrt(2) * cos(K*pi/22) * 128/121. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*2]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*1]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*0]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*7]; + tmp4 = dataptr[DCTSIZE*4] + dataptr[DCTSIZE*6]; + tmp5 = dataptr[DCTSIZE*5]; + + tmp10 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*2]; + tmp11 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*1]; + tmp12 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*0]; + tmp13 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*7]; + tmp14 = dataptr[DCTSIZE*4] - dataptr[DCTSIZE*6]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 + tmp1 + tmp2 + tmp3 + tmp4 + tmp5, + FIX(1.057851240)), /* 128/121 */ + CONST_BITS+2); + tmp5 += tmp5; + tmp0 -= tmp5; + tmp1 -= tmp5; + tmp2 -= tmp5; + tmp3 -= tmp5; + tmp4 -= tmp5; + z1 = MULTIPLY(tmp0 + tmp3, FIX(1.435427942)) + /* c2 */ + MULTIPLY(tmp2 + tmp4, FIX(0.212906922)); /* c10 */ + z2 = MULTIPLY(tmp1 - tmp3, FIX(0.979689713)); /* c6 */ + z3 = MULTIPLY(tmp0 - tmp1, FIX(1.258538479)); /* c4 */ + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(z1 + z2 - MULTIPLY(tmp3, FIX(1.077210542)) /* c2+c8-c6 */ + - MULTIPLY(tmp4, FIX(1.471445400)), /* c4+c10 */ + CONST_BITS+2); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(z2 + z3 + MULTIPLY(tmp1, FIX(0.065941844)) /* c4-c6-c10 */ + - MULTIPLY(tmp2, FIX(1.435427942)) /* c2 */ + + MULTIPLY(tmp4, FIX(0.621472312)), /* c8 */ + CONST_BITS+2); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(z1 + z3 - MULTIPLY(tmp0, FIX(1.714276708)) /* c2+c4-c6 */ + - MULTIPLY(tmp2, FIX(0.834379234)), /* c8+c10 */ + CONST_BITS+2); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.360834544)); /* c3 */ + tmp2 = MULTIPLY(tmp10 + tmp12, FIX(1.130622199)); /* c5 */ + tmp3 = MULTIPLY(tmp10 + tmp13, FIX(0.808813568)); /* c7 */ + tmp0 = tmp1 + tmp2 + tmp3 - MULTIPLY(tmp10, FIX(1.819470145)) /* c7+c5+c3-c1 */ + + MULTIPLY(tmp14, FIX(0.421479672)); /* c9 */ + tmp4 = MULTIPLY(tmp11 + tmp12, - FIX(0.808813568)); /* -c7 */ + tmp5 = MULTIPLY(tmp11 + tmp13, - FIX(1.480800167)); /* -c1 */ + tmp1 += tmp4 + tmp5 + MULTIPLY(tmp11, FIX(1.350258864)) /* c9+c7+c1-c3 */ + - MULTIPLY(tmp14, FIX(1.130622199)); /* c5 */ + tmp10 = MULTIPLY(tmp12 + tmp13, FIX(0.421479672)); /* c9 */ + tmp2 += tmp4 + tmp10 - MULTIPLY(tmp12, FIX(2.104122847)) /* c9+c5+c3-c7 */ + + MULTIPLY(tmp14, FIX(1.480800167)); /* c1 */ + tmp3 += tmp5 + tmp10 + MULTIPLY(tmp13, FIX(1.381129125)) /* c1+c5-c9-c7 */ + - MULTIPLY(tmp14, FIX(1.360834544)); /* c3 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0, CONST_BITS+2); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1, CONST_BITS+2); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2, CONST_BITS+2); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp3, CONST_BITS+2); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 12x12 sample block. + */ + +GLOBAL(void) +jpeg_fdct_12x12 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + DCTELEM workspace[8*4]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + /* cK represents sqrt(2) * cos(K*pi/24). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[11]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[10]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[9]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[8]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[7]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[6]); + + tmp10 = tmp0 + tmp5; + tmp13 = tmp0 - tmp5; + tmp11 = tmp1 + tmp4; + tmp14 = tmp1 - tmp4; + tmp12 = tmp2 + tmp3; + tmp15 = tmp2 - tmp3; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[11]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[10]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[9]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[8]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[7]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[6]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) (tmp10 + tmp11 + tmp12 - 12 * CENTERJSAMPLE); + dataptr[6] = (DCTELEM) (tmp13 - tmp14 - tmp15); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.224744871)), /* c4 */ + CONST_BITS); + dataptr[2] = (DCTELEM) + DESCALE(tmp14 - tmp15 + MULTIPLY(tmp13 + tmp15, FIX(1.366025404)), /* c2 */ + CONST_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp1 + tmp4, FIX_0_541196100); /* c9 */ + tmp14 = tmp10 + MULTIPLY(tmp1, FIX_0_765366865); /* c3-c9 */ + tmp15 = tmp10 - MULTIPLY(tmp4, FIX_1_847759065); /* c3+c9 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.121971054)); /* c5 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(0.860918669)); /* c7 */ + tmp10 = tmp12 + tmp13 + tmp14 - MULTIPLY(tmp0, FIX(0.580774953)) /* c5+c7-c1 */ + + MULTIPLY(tmp5, FIX(0.184591911)); /* c11 */ + tmp11 = MULTIPLY(tmp2 + tmp3, - FIX(0.184591911)); /* -c11 */ + tmp12 += tmp11 - tmp15 - MULTIPLY(tmp2, FIX(2.339493912)) /* c1+c5-c11 */ + + MULTIPLY(tmp5, FIX(0.860918669)); /* c7 */ + tmp13 += tmp11 - tmp14 + MULTIPLY(tmp3, FIX(0.725788011)) /* c1+c11-c7 */ + - MULTIPLY(tmp5, FIX(1.121971054)); /* c5 */ + tmp11 = tmp15 + MULTIPLY(tmp0 - tmp3, FIX(1.306562965)) /* c3 */ + - MULTIPLY(tmp2 + tmp5, FIX_0_541196100); /* c9 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp10, CONST_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp11, CONST_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12, CONST_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp13, CONST_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 12) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/12)**2 = 4/9, which we partially + * fold into the constant multipliers and final shifting: + * cK now represents sqrt(2) * cos(K*pi/24) * 8/9. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*3]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*2]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*1]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*0]; + tmp4 = dataptr[DCTSIZE*4] + dataptr[DCTSIZE*7]; + tmp5 = dataptr[DCTSIZE*5] + dataptr[DCTSIZE*6]; + + tmp10 = tmp0 + tmp5; + tmp13 = tmp0 - tmp5; + tmp11 = tmp1 + tmp4; + tmp14 = tmp1 - tmp4; + tmp12 = tmp2 + tmp3; + tmp15 = tmp2 - tmp3; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*3]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*2]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*1]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*0]; + tmp4 = dataptr[DCTSIZE*4] - dataptr[DCTSIZE*7]; + tmp5 = dataptr[DCTSIZE*5] - dataptr[DCTSIZE*6]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12, FIX(0.888888889)), /* 8/9 */ + CONST_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(MULTIPLY(tmp13 - tmp14 - tmp15, FIX(0.888888889)), /* 8/9 */ + CONST_BITS+1); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.088662108)), /* c4 */ + CONST_BITS+1); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp14 - tmp15, FIX(0.888888889)) + /* 8/9 */ + MULTIPLY(tmp13 + tmp15, FIX(1.214244803)), /* c2 */ + CONST_BITS+1); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp1 + tmp4, FIX(0.481063200)); /* c9 */ + tmp14 = tmp10 + MULTIPLY(tmp1, FIX(0.680326102)); /* c3-c9 */ + tmp15 = tmp10 - MULTIPLY(tmp4, FIX(1.642452502)); /* c3+c9 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(0.997307603)); /* c5 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(0.765261039)); /* c7 */ + tmp10 = tmp12 + tmp13 + tmp14 - MULTIPLY(tmp0, FIX(0.516244403)) /* c5+c7-c1 */ + + MULTIPLY(tmp5, FIX(0.164081699)); /* c11 */ + tmp11 = MULTIPLY(tmp2 + tmp3, - FIX(0.164081699)); /* -c11 */ + tmp12 += tmp11 - tmp15 - MULTIPLY(tmp2, FIX(2.079550144)) /* c1+c5-c11 */ + + MULTIPLY(tmp5, FIX(0.765261039)); /* c7 */ + tmp13 += tmp11 - tmp14 + MULTIPLY(tmp3, FIX(0.645144899)) /* c1+c11-c7 */ + - MULTIPLY(tmp5, FIX(0.997307603)); /* c5 */ + tmp11 = tmp15 + MULTIPLY(tmp0 - tmp3, FIX(1.161389302)) /* c3 */ + - MULTIPLY(tmp2 + tmp5, FIX(0.481063200)); /* c9 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp10, CONST_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp11, CONST_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12, CONST_BITS+1); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp13, CONST_BITS+1); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 13x13 sample block. + */ + +GLOBAL(void) +jpeg_fdct_13x13 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + INT32 z1, z2; + DCTELEM workspace[8*5]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + /* cK represents sqrt(2) * cos(K*pi/26). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[12]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[11]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[10]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[9]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[8]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[7]); + tmp6 = GETJSAMPLE(elemptr[6]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[12]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[11]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[10]); + tmp13 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[9]); + tmp14 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[8]); + tmp15 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[7]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + (tmp0 + tmp1 + tmp2 + tmp3 + tmp4 + tmp5 + tmp6 - 13 * CENTERJSAMPLE); + tmp6 += tmp6; + tmp0 -= tmp6; + tmp1 -= tmp6; + tmp2 -= tmp6; + tmp3 -= tmp6; + tmp4 -= tmp6; + tmp5 -= tmp6; + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.373119086)) + /* c2 */ + MULTIPLY(tmp1, FIX(1.058554052)) + /* c6 */ + MULTIPLY(tmp2, FIX(0.501487041)) - /* c10 */ + MULTIPLY(tmp3, FIX(0.170464608)) - /* c12 */ + MULTIPLY(tmp4, FIX(0.803364869)) - /* c8 */ + MULTIPLY(tmp5, FIX(1.252223920)), /* c4 */ + CONST_BITS); + z1 = MULTIPLY(tmp0 - tmp2, FIX(1.155388986)) - /* (c4+c6)/2 */ + MULTIPLY(tmp3 - tmp4, FIX(0.435816023)) - /* (c2-c10)/2 */ + MULTIPLY(tmp1 - tmp5, FIX(0.316450131)); /* (c8-c12)/2 */ + z2 = MULTIPLY(tmp0 + tmp2, FIX(0.096834934)) - /* (c4-c6)/2 */ + MULTIPLY(tmp3 + tmp4, FIX(0.937303064)) + /* (c2+c10)/2 */ + MULTIPLY(tmp1 + tmp5, FIX(0.486914739)); /* (c8+c12)/2 */ + + dataptr[4] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 - z2, CONST_BITS); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.322312651)); /* c3 */ + tmp2 = MULTIPLY(tmp10 + tmp12, FIX(1.163874945)); /* c5 */ + tmp3 = MULTIPLY(tmp10 + tmp13, FIX(0.937797057)) + /* c7 */ + MULTIPLY(tmp14 + tmp15, FIX(0.338443458)); /* c11 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(tmp10, FIX(2.020082300)) + /* c3+c5+c7-c1 */ + MULTIPLY(tmp14, FIX(0.318774355)); /* c9-c11 */ + tmp4 = MULTIPLY(tmp14 - tmp15, FIX(0.937797057)) - /* c7 */ + MULTIPLY(tmp11 + tmp12, FIX(0.338443458)); /* c11 */ + tmp5 = MULTIPLY(tmp11 + tmp13, - FIX(1.163874945)); /* -c5 */ + tmp1 += tmp4 + tmp5 + + MULTIPLY(tmp11, FIX(0.837223564)) - /* c5+c9+c11-c3 */ + MULTIPLY(tmp14, FIX(2.341699410)); /* c1+c7 */ + tmp6 = MULTIPLY(tmp12 + tmp13, - FIX(0.657217813)); /* -c9 */ + tmp2 += tmp4 + tmp6 - + MULTIPLY(tmp12, FIX(1.572116027)) + /* c1+c5-c9-c11 */ + MULTIPLY(tmp15, FIX(2.260109708)); /* c3+c7 */ + tmp3 += tmp5 + tmp6 + + MULTIPLY(tmp13, FIX(2.205608352)) - /* c3+c5+c9-c7 */ + MULTIPLY(tmp15, FIX(1.742345811)); /* c1+c11 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp0, CONST_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp1, CONST_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp2, CONST_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp3, CONST_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 13) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/13)**2 = 64/169, which we partially + * fold into the constant multipliers and final shifting: + * cK now represents sqrt(2) * cos(K*pi/26) * 128/169. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*4]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*3]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*2]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*1]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*0]; + tmp5 = dataptr[DCTSIZE*5] + dataptr[DCTSIZE*7]; + tmp6 = dataptr[DCTSIZE*6]; + + tmp10 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*4]; + tmp11 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*3]; + tmp12 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*2]; + tmp13 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*1]; + tmp14 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*0]; + tmp15 = dataptr[DCTSIZE*5] - dataptr[DCTSIZE*7]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 + tmp1 + tmp2 + tmp3 + tmp4 + tmp5 + tmp6, + FIX(0.757396450)), /* 128/169 */ + CONST_BITS+1); + tmp6 += tmp6; + tmp0 -= tmp6; + tmp1 -= tmp6; + tmp2 -= tmp6; + tmp3 -= tmp6; + tmp4 -= tmp6; + tmp5 -= tmp6; + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.039995521)) + /* c2 */ + MULTIPLY(tmp1, FIX(0.801745081)) + /* c6 */ + MULTIPLY(tmp2, FIX(0.379824504)) - /* c10 */ + MULTIPLY(tmp3, FIX(0.129109289)) - /* c12 */ + MULTIPLY(tmp4, FIX(0.608465700)) - /* c8 */ + MULTIPLY(tmp5, FIX(0.948429952)), /* c4 */ + CONST_BITS+1); + z1 = MULTIPLY(tmp0 - tmp2, FIX(0.875087516)) - /* (c4+c6)/2 */ + MULTIPLY(tmp3 - tmp4, FIX(0.330085509)) - /* (c2-c10)/2 */ + MULTIPLY(tmp1 - tmp5, FIX(0.239678205)); /* (c8-c12)/2 */ + z2 = MULTIPLY(tmp0 + tmp2, FIX(0.073342435)) - /* (c4-c6)/2 */ + MULTIPLY(tmp3 + tmp4, FIX(0.709910013)) + /* (c2+c10)/2 */ + MULTIPLY(tmp1 + tmp5, FIX(0.368787494)); /* (c8+c12)/2 */ + + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 - z2, CONST_BITS+1); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.001514908)); /* c3 */ + tmp2 = MULTIPLY(tmp10 + tmp12, FIX(0.881514751)); /* c5 */ + tmp3 = MULTIPLY(tmp10 + tmp13, FIX(0.710284161)) + /* c7 */ + MULTIPLY(tmp14 + tmp15, FIX(0.256335874)); /* c11 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(tmp10, FIX(1.530003162)) + /* c3+c5+c7-c1 */ + MULTIPLY(tmp14, FIX(0.241438564)); /* c9-c11 */ + tmp4 = MULTIPLY(tmp14 - tmp15, FIX(0.710284161)) - /* c7 */ + MULTIPLY(tmp11 + tmp12, FIX(0.256335874)); /* c11 */ + tmp5 = MULTIPLY(tmp11 + tmp13, - FIX(0.881514751)); /* -c5 */ + tmp1 += tmp4 + tmp5 + + MULTIPLY(tmp11, FIX(0.634110155)) - /* c5+c9+c11-c3 */ + MULTIPLY(tmp14, FIX(1.773594819)); /* c1+c7 */ + tmp6 = MULTIPLY(tmp12 + tmp13, - FIX(0.497774438)); /* -c9 */ + tmp2 += tmp4 + tmp6 - + MULTIPLY(tmp12, FIX(1.190715098)) + /* c1+c5-c9-c11 */ + MULTIPLY(tmp15, FIX(1.711799069)); /* c3+c7 */ + tmp3 += tmp5 + tmp6 + + MULTIPLY(tmp13, FIX(1.670519935)) - /* c3+c5+c9-c7 */ + MULTIPLY(tmp15, FIX(1.319646532)); /* c1+c11 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0, CONST_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1, CONST_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2, CONST_BITS+1); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp3, CONST_BITS+1); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 14x14 sample block. + */ + +GLOBAL(void) +jpeg_fdct_14x14 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + DCTELEM workspace[8*6]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + /* cK represents sqrt(2) * cos(K*pi/28). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[13]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[12]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[11]); + tmp13 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[10]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[9]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[8]); + tmp6 = GETJSAMPLE(elemptr[6]) + GETJSAMPLE(elemptr[7]); + + tmp10 = tmp0 + tmp6; + tmp14 = tmp0 - tmp6; + tmp11 = tmp1 + tmp5; + tmp15 = tmp1 - tmp5; + tmp12 = tmp2 + tmp4; + tmp16 = tmp2 - tmp4; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[13]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[12]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[11]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[10]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[9]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[8]); + tmp6 = GETJSAMPLE(elemptr[6]) - GETJSAMPLE(elemptr[7]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + (tmp10 + tmp11 + tmp12 + tmp13 - 14 * CENTERJSAMPLE); + tmp13 += tmp13; + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.274162392)) + /* c4 */ + MULTIPLY(tmp11 - tmp13, FIX(0.314692123)) - /* c12 */ + MULTIPLY(tmp12 - tmp13, FIX(0.881747734)), /* c8 */ + CONST_BITS); + + tmp10 = MULTIPLY(tmp14 + tmp15, FIX(1.105676686)); /* c6 */ + + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp14, FIX(0.273079590)) /* c2-c6 */ + + MULTIPLY(tmp16, FIX(0.613604268)), /* c10 */ + CONST_BITS); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp15, FIX(1.719280954)) /* c6+c10 */ + - MULTIPLY(tmp16, FIX(1.378756276)), /* c2 */ + CONST_BITS); + + /* Odd part */ + + tmp10 = tmp1 + tmp2; + tmp11 = tmp5 - tmp4; + dataptr[7] = (DCTELEM) (tmp0 - tmp10 + tmp3 - tmp11 - tmp6); + tmp3 <<= CONST_BITS; + tmp10 = MULTIPLY(tmp10, - FIX(0.158341681)); /* -c13 */ + tmp11 = MULTIPLY(tmp11, FIX(1.405321284)); /* c1 */ + tmp10 += tmp11 - tmp3; + tmp11 = MULTIPLY(tmp0 + tmp2, FIX(1.197448846)) + /* c5 */ + MULTIPLY(tmp4 + tmp6, FIX(0.752406978)); /* c9 */ + dataptr[5] = (DCTELEM) + DESCALE(tmp10 + tmp11 - MULTIPLY(tmp2, FIX(2.373959773)) /* c3+c5-c13 */ + + MULTIPLY(tmp4, FIX(1.119999435)), /* c1+c11-c9 */ + CONST_BITS); + tmp12 = MULTIPLY(tmp0 + tmp1, FIX(1.334852607)) + /* c3 */ + MULTIPLY(tmp5 - tmp6, FIX(0.467085129)); /* c11 */ + dataptr[3] = (DCTELEM) + DESCALE(tmp10 + tmp12 - MULTIPLY(tmp1, FIX(0.424103948)) /* c3-c9-c13 */ + - MULTIPLY(tmp5, FIX(3.069855259)), /* c1+c5+c11 */ + CONST_BITS); + dataptr[1] = (DCTELEM) + DESCALE(tmp11 + tmp12 + tmp3 + tmp6 - + MULTIPLY(tmp0 + tmp6, FIX(1.126980169)), /* c3+c5-c1 */ + CONST_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 14) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/14)**2 = 16/49, which we partially + * fold into the constant multipliers and final shifting: + * cK now represents sqrt(2) * cos(K*pi/28) * 32/49. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*3]; + tmp13 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*2]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*1]; + tmp5 = dataptr[DCTSIZE*5] + wsptr[DCTSIZE*0]; + tmp6 = dataptr[DCTSIZE*6] + dataptr[DCTSIZE*7]; + + tmp10 = tmp0 + tmp6; + tmp14 = tmp0 - tmp6; + tmp11 = tmp1 + tmp5; + tmp15 = tmp1 - tmp5; + tmp12 = tmp2 + tmp4; + tmp16 = tmp2 - tmp4; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*3]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*2]; + tmp4 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*1]; + tmp5 = dataptr[DCTSIZE*5] - wsptr[DCTSIZE*0]; + tmp6 = dataptr[DCTSIZE*6] - dataptr[DCTSIZE*7]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12 + tmp13, + FIX(0.653061224)), /* 32/49 */ + CONST_BITS+1); + tmp13 += tmp13; + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(0.832106052)) + /* c4 */ + MULTIPLY(tmp11 - tmp13, FIX(0.205513223)) - /* c12 */ + MULTIPLY(tmp12 - tmp13, FIX(0.575835255)), /* c8 */ + CONST_BITS+1); + + tmp10 = MULTIPLY(tmp14 + tmp15, FIX(0.722074570)); /* c6 */ + + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp14, FIX(0.178337691)) /* c2-c6 */ + + MULTIPLY(tmp16, FIX(0.400721155)), /* c10 */ + CONST_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp15, FIX(1.122795725)) /* c6+c10 */ + - MULTIPLY(tmp16, FIX(0.900412262)), /* c2 */ + CONST_BITS+1); + + /* Odd part */ + + tmp10 = tmp1 + tmp2; + tmp11 = tmp5 - tmp4; + dataptr[DCTSIZE*7] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp10 + tmp3 - tmp11 - tmp6, + FIX(0.653061224)), /* 32/49 */ + CONST_BITS+1); + tmp3 = MULTIPLY(tmp3 , FIX(0.653061224)); /* 32/49 */ + tmp10 = MULTIPLY(tmp10, - FIX(0.103406812)); /* -c13 */ + tmp11 = MULTIPLY(tmp11, FIX(0.917760839)); /* c1 */ + tmp10 += tmp11 - tmp3; + tmp11 = MULTIPLY(tmp0 + tmp2, FIX(0.782007410)) + /* c5 */ + MULTIPLY(tmp4 + tmp6, FIX(0.491367823)); /* c9 */ + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp10 + tmp11 - MULTIPLY(tmp2, FIX(1.550341076)) /* c3+c5-c13 */ + + MULTIPLY(tmp4, FIX(0.731428202)), /* c1+c11-c9 */ + CONST_BITS+1); + tmp12 = MULTIPLY(tmp0 + tmp1, FIX(0.871740478)) + /* c3 */ + MULTIPLY(tmp5 - tmp6, FIX(0.305035186)); /* c11 */ + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(tmp10 + tmp12 - MULTIPLY(tmp1, FIX(0.276965844)) /* c3-c9-c13 */ + - MULTIPLY(tmp5, FIX(2.004803435)), /* c1+c5+c11 */ + CONST_BITS+1); + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp11 + tmp12 + tmp3 + - MULTIPLY(tmp0, FIX(0.735987049)) /* c3+c5-c1 */ + - MULTIPLY(tmp6, FIX(0.082925825)), /* c9-c11-c13 */ + CONST_BITS+1); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 15x15 sample block. + */ + +GLOBAL(void) +jpeg_fdct_15x15 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 z1, z2, z3; + DCTELEM workspace[8*7]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + /* cK represents sqrt(2) * cos(K*pi/30). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[14]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[13]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[12]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[11]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[10]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[9]); + tmp6 = GETJSAMPLE(elemptr[6]) + GETJSAMPLE(elemptr[8]); + tmp7 = GETJSAMPLE(elemptr[7]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[14]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[13]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[12]); + tmp13 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[11]); + tmp14 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[10]); + tmp15 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[9]); + tmp16 = GETJSAMPLE(elemptr[6]) - GETJSAMPLE(elemptr[8]); + + z1 = tmp0 + tmp4 + tmp5; + z2 = tmp1 + tmp3 + tmp6; + z3 = tmp2 + tmp7; + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) (z1 + z2 + z3 - 15 * CENTERJSAMPLE); + z3 += z3; + dataptr[6] = (DCTELEM) + DESCALE(MULTIPLY(z1 - z3, FIX(1.144122806)) - /* c6 */ + MULTIPLY(z2 - z3, FIX(0.437016024)), /* c12 */ + CONST_BITS); + tmp2 += ((tmp1 + tmp4) >> 1) - tmp7 - tmp7; + z1 = MULTIPLY(tmp3 - tmp2, FIX(1.531135173)) - /* c2+c14 */ + MULTIPLY(tmp6 - tmp2, FIX(2.238241955)); /* c4+c8 */ + z2 = MULTIPLY(tmp5 - tmp2, FIX(0.798468008)) - /* c8-c14 */ + MULTIPLY(tmp0 - tmp2, FIX(0.091361227)); /* c2-c4 */ + z3 = MULTIPLY(tmp0 - tmp3, FIX(1.383309603)) + /* c2 */ + MULTIPLY(tmp6 - tmp5, FIX(0.946293579)) + /* c8 */ + MULTIPLY(tmp1 - tmp4, FIX(0.790569415)); /* (c6+c12)/2 */ + + dataptr[2] = (DCTELEM) DESCALE(z1 + z3, CONST_BITS); + dataptr[4] = (DCTELEM) DESCALE(z2 + z3, CONST_BITS); + + /* Odd part */ + + tmp2 = MULTIPLY(tmp10 - tmp12 - tmp13 + tmp15 + tmp16, + FIX(1.224744871)); /* c5 */ + tmp1 = MULTIPLY(tmp10 - tmp14 - tmp15, FIX(1.344997024)) + /* c3 */ + MULTIPLY(tmp11 - tmp13 - tmp16, FIX(0.831253876)); /* c9 */ + tmp12 = MULTIPLY(tmp12, FIX(1.224744871)); /* c5 */ + tmp4 = MULTIPLY(tmp10 - tmp16, FIX(1.406466353)) + /* c1 */ + MULTIPLY(tmp11 + tmp14, FIX(1.344997024)) + /* c3 */ + MULTIPLY(tmp13 + tmp15, FIX(0.575212477)); /* c11 */ + tmp0 = MULTIPLY(tmp13, FIX(0.475753014)) - /* c7-c11 */ + MULTIPLY(tmp14, FIX(0.513743148)) + /* c3-c9 */ + MULTIPLY(tmp16, FIX(1.700497885)) + tmp4 + tmp12; /* c1+c13 */ + tmp3 = MULTIPLY(tmp10, - FIX(0.355500862)) - /* -(c1-c7) */ + MULTIPLY(tmp11, FIX(2.176250899)) - /* c3+c9 */ + MULTIPLY(tmp15, FIX(0.869244010)) + tmp4 - tmp12; /* c11+c13 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp0, CONST_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp1, CONST_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp2, CONST_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp3, CONST_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 15) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/15)**2 = 64/225, which we partially + * fold into the constant multipliers and final shifting: + * cK now represents sqrt(2) * cos(K*pi/30) * 256/225. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*6]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*5]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*4]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*3]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*2]; + tmp5 = dataptr[DCTSIZE*5] + wsptr[DCTSIZE*1]; + tmp6 = dataptr[DCTSIZE*6] + wsptr[DCTSIZE*0]; + tmp7 = dataptr[DCTSIZE*7]; + + tmp10 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*6]; + tmp11 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*5]; + tmp12 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*4]; + tmp13 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*3]; + tmp14 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*2]; + tmp15 = dataptr[DCTSIZE*5] - wsptr[DCTSIZE*1]; + tmp16 = dataptr[DCTSIZE*6] - wsptr[DCTSIZE*0]; + + z1 = tmp0 + tmp4 + tmp5; + z2 = tmp1 + tmp3 + tmp6; + z3 = tmp2 + tmp7; + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(z1 + z2 + z3, FIX(1.137777778)), /* 256/225 */ + CONST_BITS+2); + z3 += z3; + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(MULTIPLY(z1 - z3, FIX(1.301757503)) - /* c6 */ + MULTIPLY(z2 - z3, FIX(0.497227121)), /* c12 */ + CONST_BITS+2); + tmp2 += ((tmp1 + tmp4) >> 1) - tmp7 - tmp7; + z1 = MULTIPLY(tmp3 - tmp2, FIX(1.742091575)) - /* c2+c14 */ + MULTIPLY(tmp6 - tmp2, FIX(2.546621957)); /* c4+c8 */ + z2 = MULTIPLY(tmp5 - tmp2, FIX(0.908479156)) - /* c8-c14 */ + MULTIPLY(tmp0 - tmp2, FIX(0.103948774)); /* c2-c4 */ + z3 = MULTIPLY(tmp0 - tmp3, FIX(1.573898926)) + /* c2 */ + MULTIPLY(tmp6 - tmp5, FIX(1.076671805)) + /* c8 */ + MULTIPLY(tmp1 - tmp4, FIX(0.899492312)); /* (c6+c12)/2 */ + + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + z3, CONST_BITS+2); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(z2 + z3, CONST_BITS+2); + + /* Odd part */ + + tmp2 = MULTIPLY(tmp10 - tmp12 - tmp13 + tmp15 + tmp16, + FIX(1.393487498)); /* c5 */ + tmp1 = MULTIPLY(tmp10 - tmp14 - tmp15, FIX(1.530307725)) + /* c3 */ + MULTIPLY(tmp11 - tmp13 - tmp16, FIX(0.945782187)); /* c9 */ + tmp12 = MULTIPLY(tmp12, FIX(1.393487498)); /* c5 */ + tmp4 = MULTIPLY(tmp10 - tmp16, FIX(1.600246161)) + /* c1 */ + MULTIPLY(tmp11 + tmp14, FIX(1.530307725)) + /* c3 */ + MULTIPLY(tmp13 + tmp15, FIX(0.654463974)); /* c11 */ + tmp0 = MULTIPLY(tmp13, FIX(0.541301207)) - /* c7-c11 */ + MULTIPLY(tmp14, FIX(0.584525538)) + /* c3-c9 */ + MULTIPLY(tmp16, FIX(1.934788705)) + tmp4 + tmp12; /* c1+c13 */ + tmp3 = MULTIPLY(tmp10, - FIX(0.404480980)) - /* -(c1-c7) */ + MULTIPLY(tmp11, FIX(2.476089912)) - /* c3+c9 */ + MULTIPLY(tmp15, FIX(0.989006518)) + tmp4 - tmp12; /* c11+c13 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0, CONST_BITS+2); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1, CONST_BITS+2); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2, CONST_BITS+2); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp3, CONST_BITS+2); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 16x16 sample block. + */ + +GLOBAL(void) +jpeg_fdct_16x16 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17; + DCTELEM workspace[DCTSIZE2]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* cK represents sqrt(2) * cos(K*pi/32). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[15]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[14]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[13]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[12]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[11]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[10]); + tmp6 = GETJSAMPLE(elemptr[6]) + GETJSAMPLE(elemptr[9]); + tmp7 = GETJSAMPLE(elemptr[7]) + GETJSAMPLE(elemptr[8]); + + tmp10 = tmp0 + tmp7; + tmp14 = tmp0 - tmp7; + tmp11 = tmp1 + tmp6; + tmp15 = tmp1 - tmp6; + tmp12 = tmp2 + tmp5; + tmp16 = tmp2 - tmp5; + tmp13 = tmp3 + tmp4; + tmp17 = tmp3 - tmp4; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[15]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[14]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[13]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[12]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[11]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[10]); + tmp6 = GETJSAMPLE(elemptr[6]) - GETJSAMPLE(elemptr[9]); + tmp7 = GETJSAMPLE(elemptr[7]) - GETJSAMPLE(elemptr[8]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 + tmp13 - 16 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.306562965)) + /* c4[16] = c2[8] */ + MULTIPLY(tmp11 - tmp12, FIX_0_541196100), /* c12[16] = c6[8] */ + CONST_BITS-PASS1_BITS); + + tmp10 = MULTIPLY(tmp17 - tmp15, FIX(0.275899379)) + /* c14[16] = c7[8] */ + MULTIPLY(tmp14 - tmp16, FIX(1.387039845)); /* c2[16] = c1[8] */ + + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp15, FIX(1.451774982)) /* c6+c14 */ + + MULTIPLY(tmp16, FIX(2.172734804)), /* c2+c10 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(0.211164243)) /* c2-c6 */ + - MULTIPLY(tmp17, FIX(1.061594338)), /* c10+c14 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp11 = MULTIPLY(tmp0 + tmp1, FIX(1.353318001)) + /* c3 */ + MULTIPLY(tmp6 - tmp7, FIX(0.410524528)); /* c13 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.247225013)) + /* c5 */ + MULTIPLY(tmp5 + tmp7, FIX(0.666655658)); /* c11 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(1.093201867)) + /* c7 */ + MULTIPLY(tmp4 - tmp7, FIX(0.897167586)); /* c9 */ + tmp14 = MULTIPLY(tmp1 + tmp2, FIX(0.138617169)) + /* c15 */ + MULTIPLY(tmp6 - tmp5, FIX(1.407403738)); /* c1 */ + tmp15 = MULTIPLY(tmp1 + tmp3, - FIX(0.666655658)) + /* -c11 */ + MULTIPLY(tmp4 + tmp6, - FIX(1.247225013)); /* -c5 */ + tmp16 = MULTIPLY(tmp2 + tmp3, - FIX(1.353318001)) + /* -c3 */ + MULTIPLY(tmp5 - tmp4, FIX(0.410524528)); /* c13 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(tmp0, FIX(2.286341144)) + /* c7+c5+c3-c1 */ + MULTIPLY(tmp7, FIX(0.779653625)); /* c15+c13-c11+c9 */ + tmp11 += tmp14 + tmp15 + MULTIPLY(tmp1, FIX(0.071888074)) /* c9-c3-c15+c11 */ + - MULTIPLY(tmp6, FIX(1.663905119)); /* c7+c13+c1-c5 */ + tmp12 += tmp14 + tmp16 - MULTIPLY(tmp2, FIX(1.125726048)) /* c7+c5+c15-c3 */ + + MULTIPLY(tmp5, FIX(1.227391138)); /* c9-c11+c1-c13 */ + tmp13 += tmp15 + tmp16 + MULTIPLY(tmp3, FIX(1.065388962)) /* c15+c3+c11-c7 */ + + MULTIPLY(tmp4, FIX(2.167985692)); /* c1+c13+c5-c9 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp10, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp11, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp13, CONST_BITS-PASS1_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == DCTSIZE * 2) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/16)**2 = 1/2**2. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*3]; + tmp5 = dataptr[DCTSIZE*5] + wsptr[DCTSIZE*2]; + tmp6 = dataptr[DCTSIZE*6] + wsptr[DCTSIZE*1]; + tmp7 = dataptr[DCTSIZE*7] + wsptr[DCTSIZE*0]; + + tmp10 = tmp0 + tmp7; + tmp14 = tmp0 - tmp7; + tmp11 = tmp1 + tmp6; + tmp15 = tmp1 - tmp6; + tmp12 = tmp2 + tmp5; + tmp16 = tmp2 - tmp5; + tmp13 = tmp3 + tmp4; + tmp17 = tmp3 - tmp4; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*3]; + tmp5 = dataptr[DCTSIZE*5] - wsptr[DCTSIZE*2]; + tmp6 = dataptr[DCTSIZE*6] - wsptr[DCTSIZE*1]; + tmp7 = dataptr[DCTSIZE*7] - wsptr[DCTSIZE*0]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(tmp10 + tmp11 + tmp12 + tmp13, PASS1_BITS+2); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.306562965)) + /* c4[16] = c2[8] */ + MULTIPLY(tmp11 - tmp12, FIX_0_541196100), /* c12[16] = c6[8] */ + CONST_BITS+PASS1_BITS+2); + + tmp10 = MULTIPLY(tmp17 - tmp15, FIX(0.275899379)) + /* c14[16] = c7[8] */ + MULTIPLY(tmp14 - tmp16, FIX(1.387039845)); /* c2[16] = c1[8] */ + + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp15, FIX(1.451774982)) /* c6+c14 */ + + MULTIPLY(tmp16, FIX(2.172734804)), /* c2+10 */ + CONST_BITS+PASS1_BITS+2); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(0.211164243)) /* c2-c6 */ + - MULTIPLY(tmp17, FIX(1.061594338)), /* c10+c14 */ + CONST_BITS+PASS1_BITS+2); + + /* Odd part */ + + tmp11 = MULTIPLY(tmp0 + tmp1, FIX(1.353318001)) + /* c3 */ + MULTIPLY(tmp6 - tmp7, FIX(0.410524528)); /* c13 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.247225013)) + /* c5 */ + MULTIPLY(tmp5 + tmp7, FIX(0.666655658)); /* c11 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(1.093201867)) + /* c7 */ + MULTIPLY(tmp4 - tmp7, FIX(0.897167586)); /* c9 */ + tmp14 = MULTIPLY(tmp1 + tmp2, FIX(0.138617169)) + /* c15 */ + MULTIPLY(tmp6 - tmp5, FIX(1.407403738)); /* c1 */ + tmp15 = MULTIPLY(tmp1 + tmp3, - FIX(0.666655658)) + /* -c11 */ + MULTIPLY(tmp4 + tmp6, - FIX(1.247225013)); /* -c5 */ + tmp16 = MULTIPLY(tmp2 + tmp3, - FIX(1.353318001)) + /* -c3 */ + MULTIPLY(tmp5 - tmp4, FIX(0.410524528)); /* c13 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(tmp0, FIX(2.286341144)) + /* c7+c5+c3-c1 */ + MULTIPLY(tmp7, FIX(0.779653625)); /* c15+c13-c11+c9 */ + tmp11 += tmp14 + tmp15 + MULTIPLY(tmp1, FIX(0.071888074)) /* c9-c3-c15+c11 */ + - MULTIPLY(tmp6, FIX(1.663905119)); /* c7+c13+c1-c5 */ + tmp12 += tmp14 + tmp16 - MULTIPLY(tmp2, FIX(1.125726048)) /* c7+c5+c15-c3 */ + + MULTIPLY(tmp5, FIX(1.227391138)); /* c9-c11+c1-c13 */ + tmp13 += tmp15 + tmp16 + MULTIPLY(tmp3, FIX(1.065388962)) /* c15+c3+c11-c7 */ + + MULTIPLY(tmp4, FIX(2.167985692)); /* c1+c13+c5-c9 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp10, CONST_BITS+PASS1_BITS+2); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp11, CONST_BITS+PASS1_BITS+2); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12, CONST_BITS+PASS1_BITS+2); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp13, CONST_BITS+PASS1_BITS+2); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 16x8 sample block. + * + * 16-point FDCT in pass 1 (rows), 8-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_16x8 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17; + INT32 z1; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 16-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/32). */ + + dataptr = data; + ctr = 0; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[15]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[14]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[13]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[12]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[11]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[10]); + tmp6 = GETJSAMPLE(elemptr[6]) + GETJSAMPLE(elemptr[9]); + tmp7 = GETJSAMPLE(elemptr[7]) + GETJSAMPLE(elemptr[8]); + + tmp10 = tmp0 + tmp7; + tmp14 = tmp0 - tmp7; + tmp11 = tmp1 + tmp6; + tmp15 = tmp1 - tmp6; + tmp12 = tmp2 + tmp5; + tmp16 = tmp2 - tmp5; + tmp13 = tmp3 + tmp4; + tmp17 = tmp3 - tmp4; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[15]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[14]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[13]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[12]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[11]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[10]); + tmp6 = GETJSAMPLE(elemptr[6]) - GETJSAMPLE(elemptr[9]); + tmp7 = GETJSAMPLE(elemptr[7]) - GETJSAMPLE(elemptr[8]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 + tmp13 - 16 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.306562965)) + /* c4[16] = c2[8] */ + MULTIPLY(tmp11 - tmp12, FIX_0_541196100), /* c12[16] = c6[8] */ + CONST_BITS-PASS1_BITS); + + tmp10 = MULTIPLY(tmp17 - tmp15, FIX(0.275899379)) + /* c14[16] = c7[8] */ + MULTIPLY(tmp14 - tmp16, FIX(1.387039845)); /* c2[16] = c1[8] */ + + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp15, FIX(1.451774982)) /* c6+c14 */ + + MULTIPLY(tmp16, FIX(2.172734804)), /* c2+c10 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(0.211164243)) /* c2-c6 */ + - MULTIPLY(tmp17, FIX(1.061594338)), /* c10+c14 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp11 = MULTIPLY(tmp0 + tmp1, FIX(1.353318001)) + /* c3 */ + MULTIPLY(tmp6 - tmp7, FIX(0.410524528)); /* c13 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.247225013)) + /* c5 */ + MULTIPLY(tmp5 + tmp7, FIX(0.666655658)); /* c11 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(1.093201867)) + /* c7 */ + MULTIPLY(tmp4 - tmp7, FIX(0.897167586)); /* c9 */ + tmp14 = MULTIPLY(tmp1 + tmp2, FIX(0.138617169)) + /* c15 */ + MULTIPLY(tmp6 - tmp5, FIX(1.407403738)); /* c1 */ + tmp15 = MULTIPLY(tmp1 + tmp3, - FIX(0.666655658)) + /* -c11 */ + MULTIPLY(tmp4 + tmp6, - FIX(1.247225013)); /* -c5 */ + tmp16 = MULTIPLY(tmp2 + tmp3, - FIX(1.353318001)) + /* -c3 */ + MULTIPLY(tmp5 - tmp4, FIX(0.410524528)); /* c13 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(tmp0, FIX(2.286341144)) + /* c7+c5+c3-c1 */ + MULTIPLY(tmp7, FIX(0.779653625)); /* c15+c13-c11+c9 */ + tmp11 += tmp14 + tmp15 + MULTIPLY(tmp1, FIX(0.071888074)) /* c9-c3-c15+c11 */ + - MULTIPLY(tmp6, FIX(1.663905119)); /* c7+c13+c1-c5 */ + tmp12 += tmp14 + tmp16 - MULTIPLY(tmp2, FIX(1.125726048)) /* c7+c5+c15-c3 */ + + MULTIPLY(tmp5, FIX(1.227391138)); /* c9-c11+c1-c13 */ + tmp13 += tmp15 + tmp16 + MULTIPLY(tmp3, FIX(1.065388962)) /* c15+c3+c11-c7 */ + + MULTIPLY(tmp4, FIX(2.167985692)); /* c1+c13+c5-c9 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp10, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp11, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp13, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by 8/16 = 1/2. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + tmp10 = tmp0 + tmp3; + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS+1); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS+1); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, FIX_0_765366865), + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 - MULTIPLY(tmp13, FIX_1_847759065), + CONST_BITS+PASS1_BITS+1); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * 8-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0 + tmp10 + tmp12, + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1 + tmp11 + tmp13, + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2 + tmp11 + tmp12, + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp3 + tmp10 + tmp13, + CONST_BITS+PASS1_BITS+1); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 14x7 sample block. + * + * 14-point FDCT in pass 1 (rows), 7-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_14x7 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 z1, z2, z3; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Zero bottom row of output coefficient block. */ + MEMZERO(&data[DCTSIZE*7], SIZEOF(DCTELEM) * DCTSIZE); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 14-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/28). */ + + dataptr = data; + for (ctr = 0; ctr < 7; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[13]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[12]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[11]); + tmp13 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[10]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[9]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[8]); + tmp6 = GETJSAMPLE(elemptr[6]) + GETJSAMPLE(elemptr[7]); + + tmp10 = tmp0 + tmp6; + tmp14 = tmp0 - tmp6; + tmp11 = tmp1 + tmp5; + tmp15 = tmp1 - tmp5; + tmp12 = tmp2 + tmp4; + tmp16 = tmp2 - tmp4; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[13]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[12]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[11]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[10]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[9]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[8]); + tmp6 = GETJSAMPLE(elemptr[6]) - GETJSAMPLE(elemptr[7]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 + tmp13 - 14 * CENTERJSAMPLE) << PASS1_BITS); + tmp13 += tmp13; + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.274162392)) + /* c4 */ + MULTIPLY(tmp11 - tmp13, FIX(0.314692123)) - /* c12 */ + MULTIPLY(tmp12 - tmp13, FIX(0.881747734)), /* c8 */ + CONST_BITS-PASS1_BITS); + + tmp10 = MULTIPLY(tmp14 + tmp15, FIX(1.105676686)); /* c6 */ + + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp14, FIX(0.273079590)) /* c2-c6 */ + + MULTIPLY(tmp16, FIX(0.613604268)), /* c10 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp15, FIX(1.719280954)) /* c6+c10 */ + - MULTIPLY(tmp16, FIX(1.378756276)), /* c2 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = tmp1 + tmp2; + tmp11 = tmp5 - tmp4; + dataptr[7] = (DCTELEM) ((tmp0 - tmp10 + tmp3 - tmp11 - tmp6) << PASS1_BITS); + tmp3 <<= CONST_BITS; + tmp10 = MULTIPLY(tmp10, - FIX(0.158341681)); /* -c13 */ + tmp11 = MULTIPLY(tmp11, FIX(1.405321284)); /* c1 */ + tmp10 += tmp11 - tmp3; + tmp11 = MULTIPLY(tmp0 + tmp2, FIX(1.197448846)) + /* c5 */ + MULTIPLY(tmp4 + tmp6, FIX(0.752406978)); /* c9 */ + dataptr[5] = (DCTELEM) + DESCALE(tmp10 + tmp11 - MULTIPLY(tmp2, FIX(2.373959773)) /* c3+c5-c13 */ + + MULTIPLY(tmp4, FIX(1.119999435)), /* c1+c11-c9 */ + CONST_BITS-PASS1_BITS); + tmp12 = MULTIPLY(tmp0 + tmp1, FIX(1.334852607)) + /* c3 */ + MULTIPLY(tmp5 - tmp6, FIX(0.467085129)); /* c11 */ + dataptr[3] = (DCTELEM) + DESCALE(tmp10 + tmp12 - MULTIPLY(tmp1, FIX(0.424103948)) /* c3-c9-c13 */ + - MULTIPLY(tmp5, FIX(3.069855259)), /* c1+c5+c11 */ + CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) + DESCALE(tmp11 + tmp12 + tmp3 + tmp6 - + MULTIPLY(tmp0 + tmp6, FIX(1.126980169)), /* c3+c5-c1 */ + CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/14)*(8/7) = 32/49, which we + * partially fold into the constant multipliers and final shifting: + * 7-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/14) * 64/49. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*6]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*5]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*4]; + tmp3 = dataptr[DCTSIZE*3]; + + tmp10 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*6]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*5]; + tmp12 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*4]; + + z1 = tmp0 + tmp2; + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(z1 + tmp1 + tmp3, FIX(1.306122449)), /* 64/49 */ + CONST_BITS+PASS1_BITS+1); + tmp3 += tmp3; + z1 -= tmp3; + z1 -= tmp3; + z1 = MULTIPLY(z1, FIX(0.461784020)); /* (c2+c6-c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp2, FIX(1.202428084)); /* (c2+c4-c6)/2 */ + z3 = MULTIPLY(tmp1 - tmp2, FIX(0.411026446)); /* c6 */ + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + z2 + z3, CONST_BITS+PASS1_BITS+1); + z1 -= z2; + z2 = MULTIPLY(tmp0 - tmp1, FIX(1.151670509)); /* c4 */ + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(z2 + z3 - MULTIPLY(tmp1 - tmp3, FIX(0.923568041)), /* c2+c6-c4 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS+PASS1_BITS+1); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(1.221765677)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(tmp10 - tmp11, FIX(0.222383464)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(tmp11 + tmp12, - FIX(1.800824523)); /* -c1 */ + tmp1 += tmp2; + tmp3 = MULTIPLY(tmp10 + tmp12, FIX(0.801442310)); /* c5 */ + tmp0 += tmp3; + tmp2 += tmp3 + MULTIPLY(tmp12, FIX(2.443531355)); /* c3+c1-c5 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp0, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp1, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp2, CONST_BITS+PASS1_BITS+1); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 12x6 sample block. + * + * 12-point FDCT in pass 1 (rows), 6-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_12x6 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Zero 2 bottom rows of output coefficient block. */ + MEMZERO(&data[DCTSIZE*6], SIZEOF(DCTELEM) * DCTSIZE * 2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 12-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/24). */ + + dataptr = data; + for (ctr = 0; ctr < 6; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[11]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[10]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[9]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[8]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[7]); + tmp5 = GETJSAMPLE(elemptr[5]) + GETJSAMPLE(elemptr[6]); + + tmp10 = tmp0 + tmp5; + tmp13 = tmp0 - tmp5; + tmp11 = tmp1 + tmp4; + tmp14 = tmp1 - tmp4; + tmp12 = tmp2 + tmp3; + tmp15 = tmp2 - tmp3; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[11]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[10]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[9]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[8]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[7]); + tmp5 = GETJSAMPLE(elemptr[5]) - GETJSAMPLE(elemptr[6]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 - 12 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[6] = (DCTELEM) ((tmp13 - tmp14 - tmp15) << PASS1_BITS); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.224744871)), /* c4 */ + CONST_BITS-PASS1_BITS); + dataptr[2] = (DCTELEM) + DESCALE(tmp14 - tmp15 + MULTIPLY(tmp13 + tmp15, FIX(1.366025404)), /* c2 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp1 + tmp4, FIX_0_541196100); /* c9 */ + tmp14 = tmp10 + MULTIPLY(tmp1, FIX_0_765366865); /* c3-c9 */ + tmp15 = tmp10 - MULTIPLY(tmp4, FIX_1_847759065); /* c3+c9 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.121971054)); /* c5 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(0.860918669)); /* c7 */ + tmp10 = tmp12 + tmp13 + tmp14 - MULTIPLY(tmp0, FIX(0.580774953)) /* c5+c7-c1 */ + + MULTIPLY(tmp5, FIX(0.184591911)); /* c11 */ + tmp11 = MULTIPLY(tmp2 + tmp3, - FIX(0.184591911)); /* -c11 */ + tmp12 += tmp11 - tmp15 - MULTIPLY(tmp2, FIX(2.339493912)) /* c1+c5-c11 */ + + MULTIPLY(tmp5, FIX(0.860918669)); /* c7 */ + tmp13 += tmp11 - tmp14 + MULTIPLY(tmp3, FIX(0.725788011)) /* c1+c11-c7 */ + - MULTIPLY(tmp5, FIX(1.121971054)); /* c5 */ + tmp11 = tmp15 + MULTIPLY(tmp0 - tmp3, FIX(1.306562965)) /* c3 */ + - MULTIPLY(tmp2 + tmp5, FIX_0_541196100); /* c9 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp10, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp11, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp13, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/12)*(8/6) = 8/9, which we + * partially fold into the constant multipliers and final shifting: + * 6-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/12) * 16/9. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*5]; + tmp11 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*3]; + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*3]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(2.177324216)), /* c2 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(1.257078722)), /* c4 */ + CONST_BITS+PASS1_BITS+1); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp2, FIX(0.650711829)); /* c5 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0 + tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp2, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp2 - tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS+1); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 10x5 sample block. + * + * 10-point FDCT in pass 1 (rows), 5-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_10x5 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Zero 3 bottom rows of output coefficient block. */ + MEMZERO(&data[DCTSIZE*5], SIZEOF(DCTELEM) * DCTSIZE * 3); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 10-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/20). */ + + dataptr = data; + for (ctr = 0; ctr < 5; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[9]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[8]); + tmp12 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[7]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[6]); + tmp4 = GETJSAMPLE(elemptr[4]) + GETJSAMPLE(elemptr[5]); + + tmp10 = tmp0 + tmp4; + tmp13 = tmp0 - tmp4; + tmp11 = tmp1 + tmp3; + tmp14 = tmp1 - tmp3; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[9]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[8]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[7]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[6]); + tmp4 = GETJSAMPLE(elemptr[4]) - GETJSAMPLE(elemptr[5]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 + tmp12 - 10 * CENTERJSAMPLE) << PASS1_BITS); + tmp12 += tmp12; + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.144122806)) - /* c4 */ + MULTIPLY(tmp11 - tmp12, FIX(0.437016024)), /* c8 */ + CONST_BITS-PASS1_BITS); + tmp10 = MULTIPLY(tmp13 + tmp14, FIX(0.831253876)); /* c6 */ + dataptr[2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp13, FIX(0.513743148)), /* c2-c6 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(2.176250899)), /* c2+c6 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = tmp0 + tmp4; + tmp11 = tmp1 - tmp3; + dataptr[5] = (DCTELEM) ((tmp10 - tmp11 - tmp2) << PASS1_BITS); + tmp2 <<= CONST_BITS; + dataptr[1] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.396802247)) + /* c1 */ + MULTIPLY(tmp1, FIX(1.260073511)) + tmp2 + /* c3 */ + MULTIPLY(tmp3, FIX(0.642039522)) + /* c7 */ + MULTIPLY(tmp4, FIX(0.221231742)), /* c9 */ + CONST_BITS-PASS1_BITS); + tmp12 = MULTIPLY(tmp0 - tmp4, FIX(0.951056516)) - /* (c3+c7)/2 */ + MULTIPLY(tmp1 + tmp3, FIX(0.587785252)); /* (c1-c9)/2 */ + tmp13 = MULTIPLY(tmp10 + tmp11, FIX(0.309016994)) + /* (c3-c7)/2 */ + (tmp11 << (CONST_BITS - 1)) - tmp2; + dataptr[3] = (DCTELEM) DESCALE(tmp12 + tmp13, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp12 - tmp13, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/10)*(8/5) = 32/25, which we + * fold into the constant multipliers: + * 5-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/10) * 32/25. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*4]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*3]; + tmp2 = dataptr[DCTSIZE*2]; + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*4]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*3]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp2, FIX(1.28)), /* 32/25 */ + CONST_BITS+PASS1_BITS); + tmp11 = MULTIPLY(tmp11, FIX(1.011928851)); /* (c2+c4)/2 */ + tmp10 -= tmp2 << 2; + tmp10 = MULTIPLY(tmp10, FIX(0.452548340)); /* (c2-c4)/2 */ + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp11 + tmp10, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp11 - tmp10, CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp1, FIX(1.064004961)); /* c3 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0, FIX(0.657591230)), /* c1-c3 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp1, FIX(2.785601151)), /* c1+c3 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on an 8x4 sample block. + * + * 8-point FDCT in pass 1 (rows), 4-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_8x4 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Zero 4 bottom rows of output coefficient block. */ + MEMZERO(&data[DCTSIZE*4], SIZEOF(DCTELEM) * DCTSIZE * 4); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We must also scale the output by 8/4 = 2, which we add here. */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[4]); + + tmp10 = tmp0 + tmp3; + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[4]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 - 8 * CENTERJSAMPLE) << (PASS1_BITS+1)); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << (PASS1_BITS+1)); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-2); + dataptr[2] = (DCTELEM) RIGHT_SHIFT(z1 + MULTIPLY(tmp12, FIX_0_765366865), + CONST_BITS-PASS1_BITS-1); + dataptr[6] = (DCTELEM) RIGHT_SHIFT(z1 - MULTIPLY(tmp13, FIX_1_847759065), + CONST_BITS-PASS1_BITS-1); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * 8-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-2); + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + tmp10 + tmp12, CONST_BITS-PASS1_BITS-1); + dataptr[3] = (DCTELEM) + RIGHT_SHIFT(tmp1 + tmp11 + tmp13, CONST_BITS-PASS1_BITS-1); + dataptr[5] = (DCTELEM) + RIGHT_SHIFT(tmp2 + tmp11 + tmp12, CONST_BITS-PASS1_BITS-1); + dataptr[7] = (DCTELEM) + RIGHT_SHIFT(tmp3 + tmp10 + tmp13, CONST_BITS-PASS1_BITS-1); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * 4-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*3] + (ONE << (PASS1_BITS-1)); + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*2]; + + tmp10 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*3]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*2]; + + dataptr[DCTSIZE*0] = (DCTELEM) RIGHT_SHIFT(tmp0 + tmp1, PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) RIGHT_SHIFT(tmp0 - tmp1, PASS1_BITS); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS+PASS1_BITS-1); + + dataptr[DCTSIZE*1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 6x3 sample block. + * + * 6-point FDCT in pass 1 (rows), 3-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_6x3 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2; + INT32 tmp10, tmp11, tmp12; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* 6-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/12). */ + + dataptr = data; + for (ctr = 0; ctr < 3; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[5]); + tmp11 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[3]); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[5]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[3]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 - 6 * CENTERJSAMPLE) << (PASS1_BITS+1)); + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(1.224744871)), /* c2 */ + CONST_BITS-PASS1_BITS-1); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(0.707106781)), /* c4 */ + CONST_BITS-PASS1_BITS-1); + + /* Odd part */ + + tmp10 = DESCALE(MULTIPLY(tmp0 + tmp2, FIX(0.366025404)), /* c5 */ + CONST_BITS-PASS1_BITS-1); + + dataptr[1] = (DCTELEM) (tmp10 + ((tmp0 + tmp1) << (PASS1_BITS+1))); + dataptr[3] = (DCTELEM) ((tmp0 - tmp1 - tmp2) << (PASS1_BITS+1)); + dataptr[5] = (DCTELEM) (tmp10 + ((tmp2 - tmp1) << (PASS1_BITS+1))); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/6)*(8/3) = 32/9, which we partially + * fold into the constant multipliers (other part was done in pass 1): + * 3-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/6) * 16/9. + */ + + dataptr = data; + for (ctr = 0; ctr < 6; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*2]; + tmp1 = dataptr[DCTSIZE*1]; + + tmp2 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*2]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 + tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp1, FIX(1.257078722)), /* c2 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(MULTIPLY(tmp2, FIX(2.177324216)), /* c1 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 4x2 sample block. + * + * 4-point FDCT in pass 1 (rows), 2-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_4x2 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1; + INT32 tmp10, tmp11; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We must also scale the output by (8/4)*(8/2) = 2**3, which we add here. */ + /* 4-point FDCT kernel, */ + /* cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point FDCT]. */ + + dataptr = data; + for (ctr = 0; ctr < 2; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[3]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[2]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[3]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[2]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 - 4 * CENTERJSAMPLE) << (PASS1_BITS+3)); + dataptr[2] = (DCTELEM) ((tmp0 - tmp1) << (PASS1_BITS+3)); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-4); + + dataptr[1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS-PASS1_BITS-3); + dataptr[3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS-PASS1_BITS-3); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = dataptr[DCTSIZE*0] + (ONE << (PASS1_BITS-1)); + tmp1 = dataptr[DCTSIZE*1]; + + dataptr[DCTSIZE*0] = (DCTELEM) RIGHT_SHIFT(tmp0 + tmp1, PASS1_BITS); + + /* Odd part */ + + dataptr[DCTSIZE*1] = (DCTELEM) RIGHT_SHIFT(tmp0 - tmp1, PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 2x1 sample block. + * + * 2-point FDCT in pass 1 (rows), 1-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_2x1 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1; + JSAMPROW elemptr; + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + elemptr = sample_data[0] + start_col; + + tmp0 = GETJSAMPLE(elemptr[0]); + tmp1 = GETJSAMPLE(elemptr[1]); + + /* We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/2)*(8/1) = 2**5. + */ + + /* Even part */ + /* Apply unsigned->signed conversion */ + data[0] = (DCTELEM) ((tmp0 + tmp1 - 2 * CENTERJSAMPLE) << 5); + + /* Odd part */ + data[1] = (DCTELEM) ((tmp0 - tmp1) << 5); +} + + +/* + * Perform the forward DCT on an 8x16 sample block. + * + * 8-point FDCT in pass 1 (rows), 16-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_8x16 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17; + INT32 z1; + DCTELEM workspace[DCTSIZE2]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) + GETJSAMPLE(elemptr[4]); + + tmp10 = tmp0 + tmp3; + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[7]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[6]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[5]); + tmp3 = GETJSAMPLE(elemptr[3]) - GETJSAMPLE(elemptr[4]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) ((tmp10 + tmp11 - 8 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, FIX_0_765366865), + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 - MULTIPLY(tmp13, FIX_1_847759065), + CONST_BITS-PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * 8-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[1] = (DCTELEM) DESCALE(tmp0 + tmp10 + tmp12, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp1 + tmp11 + tmp13, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp2 + tmp11 + tmp12, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp3 + tmp10 + tmp13, CONST_BITS-PASS1_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == DCTSIZE * 2) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by 8/16 = 1/2. + * 16-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/32). + */ + + dataptr = data; + wsptr = workspace; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*3]; + tmp5 = dataptr[DCTSIZE*5] + wsptr[DCTSIZE*2]; + tmp6 = dataptr[DCTSIZE*6] + wsptr[DCTSIZE*1]; + tmp7 = dataptr[DCTSIZE*7] + wsptr[DCTSIZE*0]; + + tmp10 = tmp0 + tmp7; + tmp14 = tmp0 - tmp7; + tmp11 = tmp1 + tmp6; + tmp15 = tmp1 - tmp6; + tmp12 = tmp2 + tmp5; + tmp16 = tmp2 - tmp5; + tmp13 = tmp3 + tmp4; + tmp17 = tmp3 - tmp4; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*3]; + tmp5 = dataptr[DCTSIZE*5] - wsptr[DCTSIZE*2]; + tmp6 = dataptr[DCTSIZE*6] - wsptr[DCTSIZE*1]; + tmp7 = dataptr[DCTSIZE*7] - wsptr[DCTSIZE*0]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(tmp10 + tmp11 + tmp12 + tmp13, PASS1_BITS+1); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(1.306562965)) + /* c4[16] = c2[8] */ + MULTIPLY(tmp11 - tmp12, FIX_0_541196100), /* c12[16] = c6[8] */ + CONST_BITS+PASS1_BITS+1); + + tmp10 = MULTIPLY(tmp17 - tmp15, FIX(0.275899379)) + /* c14[16] = c7[8] */ + MULTIPLY(tmp14 - tmp16, FIX(1.387039845)); /* c2[16] = c1[8] */ + + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp15, FIX(1.451774982)) /* c6+c14 */ + + MULTIPLY(tmp16, FIX(2.172734804)), /* c2+c10 */ + CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(0.211164243)) /* c2-c6 */ + - MULTIPLY(tmp17, FIX(1.061594338)), /* c10+c14 */ + CONST_BITS+PASS1_BITS+1); + + /* Odd part */ + + tmp11 = MULTIPLY(tmp0 + tmp1, FIX(1.353318001)) + /* c3 */ + MULTIPLY(tmp6 - tmp7, FIX(0.410524528)); /* c13 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(1.247225013)) + /* c5 */ + MULTIPLY(tmp5 + tmp7, FIX(0.666655658)); /* c11 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(1.093201867)) + /* c7 */ + MULTIPLY(tmp4 - tmp7, FIX(0.897167586)); /* c9 */ + tmp14 = MULTIPLY(tmp1 + tmp2, FIX(0.138617169)) + /* c15 */ + MULTIPLY(tmp6 - tmp5, FIX(1.407403738)); /* c1 */ + tmp15 = MULTIPLY(tmp1 + tmp3, - FIX(0.666655658)) + /* -c11 */ + MULTIPLY(tmp4 + tmp6, - FIX(1.247225013)); /* -c5 */ + tmp16 = MULTIPLY(tmp2 + tmp3, - FIX(1.353318001)) + /* -c3 */ + MULTIPLY(tmp5 - tmp4, FIX(0.410524528)); /* c13 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(tmp0, FIX(2.286341144)) + /* c7+c5+c3-c1 */ + MULTIPLY(tmp7, FIX(0.779653625)); /* c15+c13-c11+c9 */ + tmp11 += tmp14 + tmp15 + MULTIPLY(tmp1, FIX(0.071888074)) /* c9-c3-c15+c11 */ + - MULTIPLY(tmp6, FIX(1.663905119)); /* c7+c13+c1-c5 */ + tmp12 += tmp14 + tmp16 - MULTIPLY(tmp2, FIX(1.125726048)) /* c7+c5+c15-c3 */ + + MULTIPLY(tmp5, FIX(1.227391138)); /* c9-c11+c1-c13 */ + tmp13 += tmp15 + tmp16 + MULTIPLY(tmp3, FIX(1.065388962)) /* c15+c3+c11-c7 */ + + MULTIPLY(tmp4, FIX(2.167985692)); /* c1+c13+c5-c9 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp10, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp11, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12, CONST_BITS+PASS1_BITS+1); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp13, CONST_BITS+PASS1_BITS+1); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 7x14 sample block. + * + * 7-point FDCT in pass 1 (rows), 14-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_7x14 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 z1, z2, z3; + DCTELEM workspace[8*6]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 7-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/14). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[6]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[5]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[4]); + tmp3 = GETJSAMPLE(elemptr[3]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[6]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[5]); + tmp12 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[4]); + + z1 = tmp0 + tmp2; + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((z1 + tmp1 + tmp3 - 7 * CENTERJSAMPLE) << PASS1_BITS); + tmp3 += tmp3; + z1 -= tmp3; + z1 -= tmp3; + z1 = MULTIPLY(z1, FIX(0.353553391)); /* (c2+c6-c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp2, FIX(0.920609002)); /* (c2+c4-c6)/2 */ + z3 = MULTIPLY(tmp1 - tmp2, FIX(0.314692123)); /* c6 */ + dataptr[2] = (DCTELEM) DESCALE(z1 + z2 + z3, CONST_BITS-PASS1_BITS); + z1 -= z2; + z2 = MULTIPLY(tmp0 - tmp1, FIX(0.881747734)); /* c4 */ + dataptr[4] = (DCTELEM) + DESCALE(z2 + z3 - MULTIPLY(tmp1 - tmp3, FIX(0.707106781)), /* c2+c6-c4 */ + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 + z2, CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp1 = MULTIPLY(tmp10 + tmp11, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(tmp10 - tmp11, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(tmp11 + tmp12, - FIX(1.378756276)); /* -c1 */ + tmp1 += tmp2; + tmp3 = MULTIPLY(tmp10 + tmp12, FIX(0.613604268)); /* c5 */ + tmp0 += tmp3; + tmp2 += tmp3 + MULTIPLY(tmp12, FIX(1.870828693)); /* c3+c1-c5 */ + + dataptr[1] = (DCTELEM) DESCALE(tmp0, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp1, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp2, CONST_BITS-PASS1_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 14) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/7)*(8/14) = 32/49, which we + * fold into the constant multipliers: + * 14-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/28) * 32/49. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = 0; ctr < 7; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*3]; + tmp13 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*2]; + tmp4 = dataptr[DCTSIZE*4] + wsptr[DCTSIZE*1]; + tmp5 = dataptr[DCTSIZE*5] + wsptr[DCTSIZE*0]; + tmp6 = dataptr[DCTSIZE*6] + dataptr[DCTSIZE*7]; + + tmp10 = tmp0 + tmp6; + tmp14 = tmp0 - tmp6; + tmp11 = tmp1 + tmp5; + tmp15 = tmp1 - tmp5; + tmp12 = tmp2 + tmp4; + tmp16 = tmp2 - tmp4; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*3]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*2]; + tmp4 = dataptr[DCTSIZE*4] - wsptr[DCTSIZE*1]; + tmp5 = dataptr[DCTSIZE*5] - wsptr[DCTSIZE*0]; + tmp6 = dataptr[DCTSIZE*6] - dataptr[DCTSIZE*7]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12 + tmp13, + FIX(0.653061224)), /* 32/49 */ + CONST_BITS+PASS1_BITS); + tmp13 += tmp13; + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp13, FIX(0.832106052)) + /* c4 */ + MULTIPLY(tmp11 - tmp13, FIX(0.205513223)) - /* c12 */ + MULTIPLY(tmp12 - tmp13, FIX(0.575835255)), /* c8 */ + CONST_BITS+PASS1_BITS); + + tmp10 = MULTIPLY(tmp14 + tmp15, FIX(0.722074570)); /* c6 */ + + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp14, FIX(0.178337691)) /* c2-c6 */ + + MULTIPLY(tmp16, FIX(0.400721155)), /* c10 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp15, FIX(1.122795725)) /* c6+c10 */ + - MULTIPLY(tmp16, FIX(0.900412262)), /* c2 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = tmp1 + tmp2; + tmp11 = tmp5 - tmp4; + dataptr[DCTSIZE*7] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp10 + tmp3 - tmp11 - tmp6, + FIX(0.653061224)), /* 32/49 */ + CONST_BITS+PASS1_BITS); + tmp3 = MULTIPLY(tmp3 , FIX(0.653061224)); /* 32/49 */ + tmp10 = MULTIPLY(tmp10, - FIX(0.103406812)); /* -c13 */ + tmp11 = MULTIPLY(tmp11, FIX(0.917760839)); /* c1 */ + tmp10 += tmp11 - tmp3; + tmp11 = MULTIPLY(tmp0 + tmp2, FIX(0.782007410)) + /* c5 */ + MULTIPLY(tmp4 + tmp6, FIX(0.491367823)); /* c9 */ + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp10 + tmp11 - MULTIPLY(tmp2, FIX(1.550341076)) /* c3+c5-c13 */ + + MULTIPLY(tmp4, FIX(0.731428202)), /* c1+c11-c9 */ + CONST_BITS+PASS1_BITS); + tmp12 = MULTIPLY(tmp0 + tmp1, FIX(0.871740478)) + /* c3 */ + MULTIPLY(tmp5 - tmp6, FIX(0.305035186)); /* c11 */ + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(tmp10 + tmp12 - MULTIPLY(tmp1, FIX(0.276965844)) /* c3-c9-c13 */ + - MULTIPLY(tmp5, FIX(2.004803435)), /* c1+c5+c11 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp11 + tmp12 + tmp3 + - MULTIPLY(tmp0, FIX(0.735987049)) /* c3+c5-c1 */ + - MULTIPLY(tmp6, FIX(0.082925825)), /* c9-c11-c13 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 6x12 sample block. + * + * 6-point FDCT in pass 1 (rows), 12-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_6x12 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + DCTELEM workspace[8*4]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 6-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/12). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[5]); + tmp11 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) + GETJSAMPLE(elemptr[3]); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[5]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[4]); + tmp2 = GETJSAMPLE(elemptr[2]) - GETJSAMPLE(elemptr[3]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp11 - 6 * CENTERJSAMPLE) << PASS1_BITS); + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(1.224744871)), /* c2 */ + CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(0.707106781)), /* c4 */ + CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = DESCALE(MULTIPLY(tmp0 + tmp2, FIX(0.366025404)), /* c5 */ + CONST_BITS-PASS1_BITS); + + dataptr[1] = (DCTELEM) (tmp10 + ((tmp0 + tmp1) << PASS1_BITS)); + dataptr[3] = (DCTELEM) ((tmp0 - tmp1 - tmp2) << PASS1_BITS); + dataptr[5] = (DCTELEM) (tmp10 + ((tmp2 - tmp1) << PASS1_BITS)); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 12) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/6)*(8/12) = 8/9, which we + * fold into the constant multipliers: + * 12-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/24) * 8/9. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*3]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*2]; + tmp2 = dataptr[DCTSIZE*2] + wsptr[DCTSIZE*1]; + tmp3 = dataptr[DCTSIZE*3] + wsptr[DCTSIZE*0]; + tmp4 = dataptr[DCTSIZE*4] + dataptr[DCTSIZE*7]; + tmp5 = dataptr[DCTSIZE*5] + dataptr[DCTSIZE*6]; + + tmp10 = tmp0 + tmp5; + tmp13 = tmp0 - tmp5; + tmp11 = tmp1 + tmp4; + tmp14 = tmp1 - tmp4; + tmp12 = tmp2 + tmp3; + tmp15 = tmp2 - tmp3; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*3]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*2]; + tmp2 = dataptr[DCTSIZE*2] - wsptr[DCTSIZE*1]; + tmp3 = dataptr[DCTSIZE*3] - wsptr[DCTSIZE*0]; + tmp4 = dataptr[DCTSIZE*4] - dataptr[DCTSIZE*7]; + tmp5 = dataptr[DCTSIZE*5] - dataptr[DCTSIZE*6]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12, FIX(0.888888889)), /* 8/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(MULTIPLY(tmp13 - tmp14 - tmp15, FIX(0.888888889)), /* 8/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.088662108)), /* c4 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp14 - tmp15, FIX(0.888888889)) + /* 8/9 */ + MULTIPLY(tmp13 + tmp15, FIX(1.214244803)), /* c2 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp1 + tmp4, FIX(0.481063200)); /* c9 */ + tmp14 = tmp10 + MULTIPLY(tmp1, FIX(0.680326102)); /* c3-c9 */ + tmp15 = tmp10 - MULTIPLY(tmp4, FIX(1.642452502)); /* c3+c9 */ + tmp12 = MULTIPLY(tmp0 + tmp2, FIX(0.997307603)); /* c5 */ + tmp13 = MULTIPLY(tmp0 + tmp3, FIX(0.765261039)); /* c7 */ + tmp10 = tmp12 + tmp13 + tmp14 - MULTIPLY(tmp0, FIX(0.516244403)) /* c5+c7-c1 */ + + MULTIPLY(tmp5, FIX(0.164081699)); /* c11 */ + tmp11 = MULTIPLY(tmp2 + tmp3, - FIX(0.164081699)); /* -c11 */ + tmp12 += tmp11 - tmp15 - MULTIPLY(tmp2, FIX(2.079550144)) /* c1+c5-c11 */ + + MULTIPLY(tmp5, FIX(0.765261039)); /* c7 */ + tmp13 += tmp11 - tmp14 + MULTIPLY(tmp3, FIX(0.645144899)) /* c1+c11-c7 */ + - MULTIPLY(tmp5, FIX(0.997307603)); /* c5 */ + tmp11 = tmp15 + MULTIPLY(tmp0 - tmp3, FIX(1.161389302)) /* c3 */ + - MULTIPLY(tmp2 + tmp5, FIX(0.481063200)); /* c9 */ + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp10, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp11, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp13, CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 5x10 sample block. + * + * 5-point FDCT in pass 1 (rows), 10-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_5x10 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4; + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + DCTELEM workspace[8*2]; + DCTELEM *dataptr; + DCTELEM *wsptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* 5-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/10). */ + + dataptr = data; + ctr = 0; + for (;;) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[4]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[3]); + tmp2 = GETJSAMPLE(elemptr[2]); + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + + tmp0 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[4]); + tmp1 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[3]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp10 + tmp2 - 5 * CENTERJSAMPLE) << PASS1_BITS); + tmp11 = MULTIPLY(tmp11, FIX(0.790569415)); /* (c2+c4)/2 */ + tmp10 -= tmp2 << 2; + tmp10 = MULTIPLY(tmp10, FIX(0.353553391)); /* (c2-c4)/2 */ + dataptr[2] = (DCTELEM) DESCALE(tmp11 + tmp10, CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) DESCALE(tmp11 - tmp10, CONST_BITS-PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp1, FIX(0.831253876)); /* c3 */ + + dataptr[1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0, FIX(0.513743148)), /* c1-c3 */ + CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp1, FIX(2.176250899)), /* c1+c3 */ + CONST_BITS-PASS1_BITS); + + ctr++; + + if (ctr != DCTSIZE) { + if (ctr == 10) + break; /* Done. */ + dataptr += DCTSIZE; /* advance pointer to next row */ + } else + dataptr = workspace; /* switch pointer to extended workspace */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/5)*(8/10) = 32/25, which we + * fold into the constant multipliers: + * 10-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/20) * 32/25. + */ + + dataptr = data; + wsptr = workspace; + for (ctr = 0; ctr < 5; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + wsptr[DCTSIZE*1]; + tmp1 = dataptr[DCTSIZE*1] + wsptr[DCTSIZE*0]; + tmp12 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*7]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*6]; + tmp4 = dataptr[DCTSIZE*4] + dataptr[DCTSIZE*5]; + + tmp10 = tmp0 + tmp4; + tmp13 = tmp0 - tmp4; + tmp11 = tmp1 + tmp3; + tmp14 = tmp1 - tmp3; + + tmp0 = dataptr[DCTSIZE*0] - wsptr[DCTSIZE*1]; + tmp1 = dataptr[DCTSIZE*1] - wsptr[DCTSIZE*0]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*7]; + tmp3 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*6]; + tmp4 = dataptr[DCTSIZE*4] - dataptr[DCTSIZE*5]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11 + tmp12, FIX(1.28)), /* 32/25 */ + CONST_BITS+PASS1_BITS); + tmp12 += tmp12; + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp12, FIX(1.464477191)) - /* c4 */ + MULTIPLY(tmp11 - tmp12, FIX(0.559380511)), /* c8 */ + CONST_BITS+PASS1_BITS); + tmp10 = MULTIPLY(tmp13 + tmp14, FIX(1.064004961)); /* c6 */ + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp13, FIX(0.657591230)), /* c2-c6 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) + DESCALE(tmp10 - MULTIPLY(tmp14, FIX(2.785601151)), /* c2+c6 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = tmp0 + tmp4; + tmp11 = tmp1 - tmp3; + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp2, FIX(1.28)), /* 32/25 */ + CONST_BITS+PASS1_BITS); + tmp2 = MULTIPLY(tmp2, FIX(1.28)); /* 32/25 */ + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(MULTIPLY(tmp0, FIX(1.787906876)) + /* c1 */ + MULTIPLY(tmp1, FIX(1.612894094)) + tmp2 + /* c3 */ + MULTIPLY(tmp3, FIX(0.821810588)) + /* c7 */ + MULTIPLY(tmp4, FIX(0.283176630)), /* c9 */ + CONST_BITS+PASS1_BITS); + tmp12 = MULTIPLY(tmp0 - tmp4, FIX(1.217352341)) - /* (c3+c7)/2 */ + MULTIPLY(tmp1 + tmp3, FIX(0.752365123)); /* (c1-c9)/2 */ + tmp13 = MULTIPLY(tmp10 + tmp11, FIX(0.395541753)) + /* (c3-c7)/2 */ + MULTIPLY(tmp11, FIX(0.64)) - tmp2; /* 16/25 */ + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp12 + tmp13, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp12 - tmp13, CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + wsptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 4x8 sample block. + * + * 4-point FDCT in pass 1 (rows), 8-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_4x8 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We must also scale the output by 8/4 = 2, which we add here. */ + /* 4-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). */ + + dataptr = data; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[3]); + tmp1 = GETJSAMPLE(elemptr[1]) + GETJSAMPLE(elemptr[2]); + + tmp10 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[3]); + tmp11 = GETJSAMPLE(elemptr[1]) - GETJSAMPLE(elemptr[2]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 - 4 * CENTERJSAMPLE) << (PASS1_BITS+1)); + dataptr[2] = (DCTELEM) ((tmp0 - tmp1) << (PASS1_BITS+1)); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-2); + + dataptr[1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS-PASS1_BITS-1); + dataptr[3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS-PASS1_BITS-1); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + /* Add fudge factor here for final descale. */ + tmp10 = tmp0 + tmp3 + (ONE << (PASS1_BITS-1)); + tmp12 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp13 = tmp1 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + dataptr[DCTSIZE*0] = (DCTELEM) RIGHT_SHIFT(tmp10 + tmp11, PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) RIGHT_SHIFT(tmp10 - tmp11, PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS+PASS1_BITS-1); + dataptr[DCTSIZE*2] = (DCTELEM) + RIGHT_SHIFT(z1 + MULTIPLY(tmp12, FIX_0_765366865), CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) + RIGHT_SHIFT(z1 - MULTIPLY(tmp13, FIX_1_847759065), CONST_BITS+PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * 8-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + * i0..i3 in the paper are tmp0..tmp3 here. + */ + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = MULTIPLY(tmp12 + tmp13, FIX_1_175875602); /* c3 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS+PASS1_BITS-1); + + tmp0 = MULTIPLY(tmp0, FIX_1_501321110); /* c1+c3-c5-c7 */ + tmp1 = MULTIPLY(tmp1, FIX_3_072711026); /* c1+c3+c5-c7 */ + tmp2 = MULTIPLY(tmp2, FIX_2_053119869); /* c1+c3-c5+c7 */ + tmp3 = MULTIPLY(tmp3, FIX_0_298631336); /* -c1+c3+c5-c7 */ + tmp10 = MULTIPLY(tmp10, - FIX_0_899976223); /* c7-c3 */ + tmp11 = MULTIPLY(tmp11, - FIX_2_562915447); /* -c1-c3 */ + tmp12 = MULTIPLY(tmp12, - FIX_0_390180644); /* c5-c3 */ + tmp13 = MULTIPLY(tmp13, - FIX_1_961570560); /* -c3-c5 */ + + tmp12 += z1; + tmp13 += z1; + + dataptr[DCTSIZE*1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + tmp10 + tmp12, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + RIGHT_SHIFT(tmp1 + tmp11 + tmp13, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) + RIGHT_SHIFT(tmp2 + tmp11 + tmp12, CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*7] = (DCTELEM) + RIGHT_SHIFT(tmp3 + tmp10 + tmp13, CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 3x6 sample block. + * + * 3-point FDCT in pass 1 (rows), 6-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_3x6 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1, tmp2; + INT32 tmp10, tmp11, tmp12; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + /* We scale the results further by 2 as part of output adaption */ + /* scaling for different DCT size. */ + /* 3-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/6). */ + + dataptr = data; + for (ctr = 0; ctr < 6; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]) + GETJSAMPLE(elemptr[2]); + tmp1 = GETJSAMPLE(elemptr[1]); + + tmp2 = GETJSAMPLE(elemptr[0]) - GETJSAMPLE(elemptr[2]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) + ((tmp0 + tmp1 - 3 * CENTERJSAMPLE) << (PASS1_BITS+1)); + dataptr[2] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp1, FIX(0.707106781)), /* c2 */ + CONST_BITS-PASS1_BITS-1); + + /* Odd part */ + + dataptr[1] = (DCTELEM) + DESCALE(MULTIPLY(tmp2, FIX(1.224744871)), /* c1 */ + CONST_BITS-PASS1_BITS-1); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + * We must also scale the output by (8/6)*(8/3) = 32/9, which we partially + * fold into the constant multipliers (other part was done in pass 1): + * 6-point FDCT kernel, cK represents sqrt(2) * cos(K*pi/12) * 16/9. + */ + + dataptr = data; + for (ctr = 0; ctr < 3; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*5]; + tmp11 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*3]; + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + tmp0 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*5]; + tmp1 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*4]; + tmp2 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*3]; + + dataptr[DCTSIZE*0] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 + tmp11, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*2] = (DCTELEM) + DESCALE(MULTIPLY(tmp12, FIX(2.177324216)), /* c2 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) + DESCALE(MULTIPLY(tmp10 - tmp11 - tmp11, FIX(1.257078722)), /* c4 */ + CONST_BITS+PASS1_BITS); + + /* Odd part */ + + tmp10 = MULTIPLY(tmp0 + tmp2, FIX(0.650711829)); /* c5 */ + + dataptr[DCTSIZE*1] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp0 + tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + DESCALE(MULTIPLY(tmp0 - tmp1 - tmp2, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) + DESCALE(tmp10 + MULTIPLY(tmp2 - tmp1, FIX(1.777777778)), /* 16/9 */ + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 2x4 sample block. + * + * 2-point FDCT in pass 1 (rows), 4-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_2x4 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1; + INT32 tmp10, tmp11; + DCTELEM *dataptr; + JSAMPROW elemptr; + int ctr; + SHIFT_TEMPS + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT. */ + /* We must also scale the output by (8/2)*(8/4) = 2**3, which we add here. */ + + dataptr = data; + for (ctr = 0; ctr < 4; ctr++) { + elemptr = sample_data[ctr] + start_col; + + /* Even part */ + + tmp0 = GETJSAMPLE(elemptr[0]); + tmp1 = GETJSAMPLE(elemptr[1]); + + /* Apply unsigned->signed conversion */ + dataptr[0] = (DCTELEM) ((tmp0 + tmp1 - 2 * CENTERJSAMPLE) << 3); + + /* Odd part */ + + dataptr[1] = (DCTELEM) ((tmp0 - tmp1) << 3); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We leave the results scaled up by an overall factor of 8. + * 4-point FDCT kernel, + * cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point FDCT]. + */ + + dataptr = data; + for (ctr = 0; ctr < 2; ctr++) { + /* Even part */ + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*3]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*2]; + + tmp10 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*3]; + tmp11 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*2]; + + dataptr[DCTSIZE*0] = (DCTELEM) (tmp0 + tmp1); + dataptr[DCTSIZE*2] = (DCTELEM) (tmp0 - tmp1); + + /* Odd part */ + + tmp0 = MULTIPLY(tmp10 + tmp11, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-1); + + dataptr[DCTSIZE*1] = (DCTELEM) + RIGHT_SHIFT(tmp0 + MULTIPLY(tmp10, FIX_0_765366865), /* c2-c6 */ + CONST_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) + RIGHT_SHIFT(tmp0 - MULTIPLY(tmp11, FIX_1_847759065), /* c2+c6 */ + CONST_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* + * Perform the forward DCT on a 1x2 sample block. + * + * 1-point FDCT in pass 1 (rows), 2-point in pass 2 (columns). + */ + +GLOBAL(void) +jpeg_fdct_1x2 (DCTELEM * data, JSAMPARRAY sample_data, JDIMENSION start_col) +{ + INT32 tmp0, tmp1; + + /* Pre-zero output coefficient block. */ + MEMZERO(data, SIZEOF(DCTELEM) * DCTSIZE2); + + tmp0 = GETJSAMPLE(sample_data[0][start_col]); + tmp1 = GETJSAMPLE(sample_data[1][start_col]); + + /* We leave the results scaled up by an overall factor of 8. + * We must also scale the output by (8/1)*(8/2) = 2**5. + */ + + /* Even part */ + /* Apply unsigned->signed conversion */ + data[DCTSIZE*0] = (DCTELEM) ((tmp0 + tmp1 - 2 * CENTERJSAMPLE) << 5); + + /* Odd part */ + data[DCTSIZE*1] = (DCTELEM) ((tmp0 - tmp1) << 5); +} + +#endif /* DCT_SCALING_SUPPORTED */ +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/lib/jpeg/src/jfdctint.d b/lib/jpeg/src/jfdctint.d new file mode 100644 index 0000000..c0e5649 --- /dev/null +++ b/lib/jpeg/src/jfdctint.d @@ -0,0 +1,16 @@ +jfdctint.o: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jidctflt.c b/lib/jpeg/src/jidctflt.c new file mode 100644 index 0000000..f399600 --- /dev/null +++ b/lib/jpeg/src/jidctflt.c @@ -0,0 +1,235 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * Modified 2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = cinfo->sample_range_limit; + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = z5 - z12 * ((FAST_FLOAT) 1.082392200); /* 2*(c2-c6) */ + tmp12 = z5 - z10 * ((FAST_FLOAT) 2.613125930); /* 2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 - tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*3] = tmp3 + tmp4; + wsptr[DCTSIZE*4] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + /* Apply signed->unsigned and prepare float->int conversion */ + z5 = wsptr[0] + ((FAST_FLOAT) CENTERJSAMPLE + (FAST_FLOAT) 0.5); + tmp10 = z5 + wsptr[4]; + tmp11 = z5 - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = z5 - z12 * ((FAST_FLOAT) 1.082392200); /* 2*(c2-c6) */ + tmp12 = z5 - z10 * ((FAST_FLOAT) 2.613125930); /* 2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 - tmp5; + + /* Final output stage: float->int conversion and range-limit */ + + outptr[0] = range_limit[((int) (tmp0 + tmp7)) & RANGE_MASK]; + outptr[7] = range_limit[((int) (tmp0 - tmp7)) & RANGE_MASK]; + outptr[1] = range_limit[((int) (tmp1 + tmp6)) & RANGE_MASK]; + outptr[6] = range_limit[((int) (tmp1 - tmp6)) & RANGE_MASK]; + outptr[2] = range_limit[((int) (tmp2 + tmp5)) & RANGE_MASK]; + outptr[5] = range_limit[((int) (tmp2 - tmp5)) & RANGE_MASK]; + outptr[3] = range_limit[((int) (tmp3 + tmp4)) & RANGE_MASK]; + outptr[4] = range_limit[((int) (tmp3 - tmp4)) & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/lib/jpeg/src/jidctflt.d b/lib/jpeg/src/jidctflt.d new file mode 100644 index 0000000..bb0bced --- /dev/null +++ b/lib/jpeg/src/jidctflt.d @@ -0,0 +1,16 @@ +jidctflt.o: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jidctfst.c b/lib/jpeg/src/jidctfst.c new file mode 100644 index 0000000..078b8c4 --- /dev/null +++ b/lib/jpeg/src/jidctfst.c @@ -0,0 +1,368 @@ +/* + * jidctfst.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jidctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * The dequantized coefficients are not integers because the AA&N scaling + * factors have been incorporated. We represent them scaled up by PASS1_BITS, + * so that the first and second IDCT rounds have the same input scaling. + * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + * avoid a descaling shift; this compromises accuracy rather drastically + * for small quantization table entries, but it saves a lot of shifts. + * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + * so we use a much larger scaling factor to preserve accuracy. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 8 +#define PASS1_BITS 2 +#else +#define CONST_BITS 8 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_1_082392200 ((INT32) 277) /* FIX(1.082392200) */ +#define FIX_1_414213562 ((INT32) 362) /* FIX(1.414213562) */ +#define FIX_1_847759065 ((INT32) 473) /* FIX(1.847759065) */ +#define FIX_2_613125930 ((INT32) 669) /* FIX(2.613125930) */ +#else +#define FIX_1_082392200 FIX(1.082392200) +#define FIX_1_414213562 FIX(1.414213562) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_613125930 FIX(2.613125930) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a DCTELEM result. For 8-bit data a 16x16->16 + * multiplication will do. For 12-bit data, the multiplier table is + * declared INT32, so a 32-bit multiply will be used. + */ + +#if BITS_IN_JSAMPLE == 8 +#define DEQUANTIZE(coef,quantval) (((IFAST_MULT_TYPE) (coef)) * (quantval)) +#else +#define DEQUANTIZE(coef,quantval) \ + DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS) +#endif + + +/* Like DESCALE, but applies to a DCTELEM and produces an int. + * We assume that int right shift is unsigned if INT32 right shift is. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS DCTELEM ishift_temp; +#if BITS_IN_JSAMPLE == 8 +#define DCTELEMBITS 16 /* DCTELEM may be 16 or 32 bits */ +#else +#define DCTELEMBITS 32 /* DCTELEM must be 32 bits */ +#endif +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +#ifdef USE_ACCURATE_ROUNDING +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n)) +#else +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT(x, n)) +#endif + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z5, z10, z11, z12, z13; + JCOEFPTR inptr; + IFAST_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS /* for DESCALE */ + ISHIFT_TEMPS /* for IDESCALE */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (IFAST_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5); + wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); + tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); + + tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); + tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562) + - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; + z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; + z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; + z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/lib/jpeg/src/jidctfst.d b/lib/jpeg/src/jidctfst.d new file mode 100644 index 0000000..52eb15a --- /dev/null +++ b/lib/jpeg/src/jidctfst.d @@ -0,0 +1,16 @@ +jidctfst.o: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jidctint.c b/lib/jpeg/src/jidctint.c new file mode 100644 index 0000000..49ef79f --- /dev/null +++ b/lib/jpeg/src/jidctint.c @@ -0,0 +1,5137 @@ +/* + * jidctint.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modification developed 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + * + * We also provide IDCT routines with various output sample block sizes for + * direct resolution reduction or enlargement and for direct resolving the + * common 2x1 and 1x2 subsampling cases without additional resampling: NxN + * (N=1...16), 2NxN, and Nx2N (N=1...8) pixels for one 8x8 input DCT block. + * + * For N<8 we simply take the corresponding low-frequency coefficients of + * the 8x8 input DCT block and apply an NxN point IDCT on the sub-block + * to yield the downscaled outputs. + * This can be seen as direct low-pass downsampling from the DCT domain + * point of view rather than the usual spatial domain point of view, + * yielding significant computational savings and results at least + * as good as common bilinear (averaging) spatial downsampling. + * + * For N>8 we apply a partial NxN IDCT on the 8 input coefficients as + * lower frequencies and higher frequencies assumed to be zero. + * It turns out that the computational effort is similar to the 8x8 IDCT + * regarding the output size. + * Furthermore, the scaling and descaling is the same for all IDCT sizes. + * + * CAUTION: We rely on the FIX() macro except for the N=1,2,4,8 cases + * since there would be too many additional constants to pre-calculate. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCT blocks. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate INT32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z2 <<= CONST_BITS; + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z2 += ONE << (CONST_BITS-PASS1_BITS-1); + + tmp0 = z2 + z3; + tmp1 = z2 - z3; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[DCTSIZE*0] = (int) RIGHT_SHIFT(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*7] = (int) RIGHT_SHIFT(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*1] = (int) RIGHT_SHIFT(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*6] = (int) RIGHT_SHIFT(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*2] = (int) RIGHT_SHIFT(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*5] = (int) RIGHT_SHIFT(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*3] = (int) RIGHT_SHIFT(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*4] = (int) RIGHT_SHIFT(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + /* Add fudge factor here for final descale. */ + z2 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 = (INT32) wsptr[4]; + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#ifdef IDCT_SCALING_SUPPORTED + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 7x7 output block. + * + * Optimized algorithm with 12 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/14). + */ + +GLOBAL(void) +jpeg_idct_7x7 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[7*7]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 7; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp13 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp13 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp13 += ONE << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ + tmp11 = tmp10 + tmp12 + tmp13 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ + tmp0 = z1 + z3; + z2 -= tmp0; + tmp0 = MULTIPLY(tmp0, FIX(1.274162392)) + tmp13; /* c2 */ + tmp10 += tmp0 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ + tmp12 += tmp0 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ + tmp13 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp1 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ + tmp1 += tmp2; + z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ + tmp0 += z2; + tmp2 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ + + /* Final output stage */ + + wsptr[7*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[7*6] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[7*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[7*5] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[7*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[7*4] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[7*3] = (int) RIGHT_SHIFT(tmp13, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 7 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 7; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp13 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp13 <<= CONST_BITS; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[4]; + z3 = (INT32) wsptr[6]; + + tmp10 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ + tmp11 = tmp10 + tmp12 + tmp13 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ + tmp0 = z1 + z3; + z2 -= tmp0; + tmp0 = MULTIPLY(tmp0, FIX(1.274162392)) + tmp13; /* c2 */ + tmp10 += tmp0 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ + tmp12 += tmp0 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ + tmp13 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + + tmp1 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp2 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp0 = tmp1 - tmp2; + tmp1 += tmp2; + tmp2 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ + tmp1 += tmp2; + z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ + tmp0 += z2; + tmp2 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 7; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 6x6 output block. + * + * Optimized algorithm with 3 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/12). + */ + +GLOBAL(void) +jpeg_idct_6x6 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[6*6]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ + tmp1 = tmp0 + tmp10; + tmp11 = RIGHT_SHIFT(tmp0 - tmp10 - tmp10, CONST_BITS-PASS1_BITS); + tmp10 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ + tmp10 = tmp1 + tmp0; + tmp12 = tmp1 - tmp0; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); + tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); + tmp1 = (z1 - z2 - z3) << PASS1_BITS; + + /* Final output stage */ + + wsptr[6*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[6*5] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[6*1] = (int) (tmp11 + tmp1); + wsptr[6*4] = (int) (tmp11 - tmp1); + wsptr[6*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[6*3] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 6 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + tmp2 = (INT32) wsptr[4]; + tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ + tmp1 = tmp0 + tmp10; + tmp11 = tmp0 - tmp10 - tmp10; + tmp10 = (INT32) wsptr[2]; + tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ + tmp10 = tmp1 + tmp0; + tmp12 = tmp1 - tmp0; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); + tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); + tmp1 = (z1 - z2 - z3) << CONST_BITS; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 6; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 5x5 output block. + * + * Optimized algorithm with 5 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/10). + */ + +GLOBAL(void) +jpeg_idct_5x5 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp10, tmp11, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[5*5]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 5; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp12 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp12 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp12 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp0 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z1 = MULTIPLY(tmp0 + tmp1, FIX(0.790569415)); /* (c2+c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp1, FIX(0.353553391)); /* (c2-c4)/2 */ + z3 = tmp12 + z2; + tmp10 = z3 + z1; + tmp11 = z3 - z1; + tmp12 -= z2 << 2; + + /* Odd part */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ + tmp0 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ + tmp1 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ + + /* Final output stage */ + + wsptr[5*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[5*4] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[5*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[5*3] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[5*2] = (int) RIGHT_SHIFT(tmp12, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 5 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 5; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp12 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp12 <<= CONST_BITS; + tmp0 = (INT32) wsptr[2]; + tmp1 = (INT32) wsptr[4]; + z1 = MULTIPLY(tmp0 + tmp1, FIX(0.790569415)); /* (c2+c4)/2 */ + z2 = MULTIPLY(tmp0 - tmp1, FIX(0.353553391)); /* (c2-c4)/2 */ + z3 = tmp12 + z2; + tmp10 = z3 + z1; + tmp11 = z3 - z1; + tmp12 -= z2 << 2; + + /* Odd part */ + + z2 = (INT32) wsptr[1]; + z3 = (INT32) wsptr[3]; + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ + tmp0 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ + tmp1 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 5; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 4x4 output block. + * + * Optimized algorithm with 3 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point IDCT]. + */ + +GLOBAL(void) +jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[4*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp10 = (tmp0 + tmp2) << PASS1_BITS; + tmp12 = (tmp0 - tmp2) << PASS1_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp0 = RIGHT_SHIFT(z1 + MULTIPLY(z2, FIX_0_765366865), /* c2-c6 */ + CONST_BITS-PASS1_BITS); + tmp2 = RIGHT_SHIFT(z1 - MULTIPLY(z3, FIX_1_847759065), /* c2+c6 */ + CONST_BITS-PASS1_BITS); + + /* Final output stage */ + + wsptr[4*0] = (int) (tmp10 + tmp0); + wsptr[4*3] = (int) (tmp10 - tmp0); + wsptr[4*1] = (int) (tmp12 + tmp2); + wsptr[4*2] = (int) (tmp12 - tmp2); + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp2 = (INT32) wsptr[2]; + + tmp10 = (tmp0 + tmp2) << CONST_BITS; + tmp12 = (tmp0 - tmp2) << CONST_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = (INT32) wsptr[1]; + z3 = (INT32) wsptr[3]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + tmp0 = z1 + MULTIPLY(z2, FIX_0_765366865); /* c2-c6 */ + tmp2 = z1 - MULTIPLY(z3, FIX_1_847759065); /* c2+c6 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 4; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 3x3 output block. + * + * Optimized algorithm with 2 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/6). + */ + +GLOBAL(void) +jpeg_idct_3x3 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[3*3]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 3; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ + tmp10 = tmp0 + tmp12; + tmp2 = tmp0 - tmp12 - tmp12; + + /* Odd part */ + + tmp12 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ + + /* Final output stage */ + + wsptr[3*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[3*2] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[3*1] = (int) RIGHT_SHIFT(tmp2, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 3 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 3; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + tmp2 = (INT32) wsptr[2]; + tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ + tmp10 = tmp0 + tmp12; + tmp2 = tmp0 - tmp12 - tmp12; + + /* Odd part */ + + tmp12 = (INT32) wsptr[1]; + tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 3; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 2x2 output block. + * + * Multiplication-less algorithm. + */ + +GLOBAL(void) +jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + ISLOW_MULT_TYPE * quantptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* Pass 1: process columns from input. */ + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + /* Column 0 */ + tmp4 = DEQUANTIZE(coef_block[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp5 = DEQUANTIZE(coef_block[DCTSIZE*1], quantptr[DCTSIZE*1]); + /* Add fudge factor here for final descale. */ + tmp4 += ONE << 2; + + tmp0 = tmp4 + tmp5; + tmp2 = tmp4 - tmp5; + + /* Column 1 */ + tmp4 = DEQUANTIZE(coef_block[DCTSIZE*0+1], quantptr[DCTSIZE*0+1]); + tmp5 = DEQUANTIZE(coef_block[DCTSIZE*1+1], quantptr[DCTSIZE*1+1]); + + tmp1 = tmp4 + tmp5; + tmp3 = tmp4 - tmp5; + + /* Pass 2: process 2 rows, store into output array. */ + + /* Row 0 */ + outptr = output_buf[0] + output_col; + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp0 + tmp1, 3) & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp0 - tmp1, 3) & RANGE_MASK]; + + /* Row 1 */ + outptr = output_buf[1] + output_col; + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp2 + tmp3, 3) & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp2 - tmp3, 3) & RANGE_MASK]; +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 1x1 output block. + * + * We hardly need an inverse DCT routine for this: just take the + * average pixel value, which is one-eighth of the DC coefficient. + */ + +GLOBAL(void) +jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + int dcval; + ISLOW_MULT_TYPE * quantptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* 1x1 is trivial: just take the DC coefficient divided by 8. */ + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + dcval = DEQUANTIZE(coef_block[0], quantptr[0]); + dcval = (int) DESCALE((INT32) dcval, 3); + + output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 9x9 output block. + * + * Optimized algorithm with 10 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/18). + */ + +GLOBAL(void) +jpeg_idct_9x9 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*9]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp3 = MULTIPLY(z3, FIX(0.707106781)); /* c6 */ + tmp1 = tmp0 + tmp3; + tmp2 = tmp0 - tmp3 - tmp3; + + tmp0 = MULTIPLY(z1 - z2, FIX(0.707106781)); /* c6 */ + tmp11 = tmp2 + tmp0; + tmp14 = tmp2 - tmp0 - tmp0; + + tmp0 = MULTIPLY(z1 + z2, FIX(1.328926049)); /* c2 */ + tmp2 = MULTIPLY(z1, FIX(1.083350441)); /* c4 */ + tmp3 = MULTIPLY(z2, FIX(0.245575608)); /* c8 */ + + tmp10 = tmp1 + tmp0 - tmp3; + tmp12 = tmp1 - tmp0 + tmp2; + tmp13 = tmp1 - tmp2 + tmp3; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z2 = MULTIPLY(z2, - FIX(1.224744871)); /* -c3 */ + + tmp2 = MULTIPLY(z1 + z3, FIX(0.909038955)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(0.483689525)); /* c7 */ + tmp0 = tmp2 + tmp3 - z2; + tmp1 = MULTIPLY(z3 - z4, FIX(1.392728481)); /* c1 */ + tmp2 += z2 - tmp1; + tmp3 += z2 + tmp1; + tmp1 = MULTIPLY(z1 - z3 - z4, FIX(1.224744871)); /* c3 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp13 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp13 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp14, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 9 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 9; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[4]; + z3 = (INT32) wsptr[6]; + + tmp3 = MULTIPLY(z3, FIX(0.707106781)); /* c6 */ + tmp1 = tmp0 + tmp3; + tmp2 = tmp0 - tmp3 - tmp3; + + tmp0 = MULTIPLY(z1 - z2, FIX(0.707106781)); /* c6 */ + tmp11 = tmp2 + tmp0; + tmp14 = tmp2 - tmp0 - tmp0; + + tmp0 = MULTIPLY(z1 + z2, FIX(1.328926049)); /* c2 */ + tmp2 = MULTIPLY(z1, FIX(1.083350441)); /* c4 */ + tmp3 = MULTIPLY(z2, FIX(0.245575608)); /* c8 */ + + tmp10 = tmp1 + tmp0 - tmp3; + tmp12 = tmp1 - tmp0 + tmp2; + tmp13 = tmp1 - tmp2 + tmp3; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + z2 = MULTIPLY(z2, - FIX(1.224744871)); /* -c3 */ + + tmp2 = MULTIPLY(z1 + z3, FIX(0.909038955)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(0.483689525)); /* c7 */ + tmp0 = tmp2 + tmp3 - z2; + tmp1 = MULTIPLY(z3 - z4, FIX(1.392728481)); /* c1 */ + tmp2 += z2 - tmp1; + tmp3 += z2 + tmp1; + tmp1 = MULTIPLY(z1 - z3 - z4, FIX(1.224744871)); /* c3 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp13 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 10x10 output block. + * + * Optimized algorithm with 12 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/20). + */ + +GLOBAL(void) +jpeg_idct_10x10 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24; + INT32 z1, z2, z3, z4, z5; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*10]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z3 += ONE << (CONST_BITS-PASS1_BITS-1); + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ + z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ + tmp10 = z3 + z1; + tmp11 = z3 - z2; + + tmp22 = RIGHT_SHIFT(z3 - ((z1 - z2) << 1), /* c0 = (c4-c8)*2 */ + CONST_BITS-PASS1_BITS); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ + tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ + tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ + + tmp20 = tmp10 + tmp12; + tmp24 = tmp10 - tmp12; + tmp21 = tmp11 + tmp13; + tmp23 = tmp11 - tmp13; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = z2 + z4; + tmp13 = z2 - z4; + + tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ + z5 = z3 << CONST_BITS; + + z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ + z4 = z5 + tmp12; + + tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ + tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ + + z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ + z4 = z5 - tmp12 - (tmp13 << (CONST_BITS - 1)); + + tmp12 = (z1 - tmp13 - z3) << PASS1_BITS; + + tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ + tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) (tmp22 + tmp12); + wsptr[8*7] = (int) (tmp22 - tmp12); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 10 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 10; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 <<= CONST_BITS; + z4 = (INT32) wsptr[4]; + z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ + z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ + tmp10 = z3 + z1; + tmp11 = z3 - z2; + + tmp22 = z3 - ((z1 - z2) << 1); /* c0 = (c4-c8)*2 */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ + tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ + tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ + + tmp20 = tmp10 + tmp12; + tmp24 = tmp10 - tmp12; + tmp21 = tmp11 + tmp13; + tmp23 = tmp11 - tmp13; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z3 <<= CONST_BITS; + z4 = (INT32) wsptr[7]; + + tmp11 = z2 + z4; + tmp13 = z2 - z4; + + tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ + + z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ + z4 = z3 + tmp12; + + tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ + tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ + + z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ + z4 = z3 - tmp12 - (tmp13 << (CONST_BITS - 1)); + + tmp12 = ((z1 - tmp13) << CONST_BITS) - z3; + + tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ + tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 11x11 output block. + * + * Optimized algorithm with 24 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/22). + */ + +GLOBAL(void) +jpeg_idct_11x11 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*11]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp10 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp10 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp10 += ONE << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp20 = MULTIPLY(z2 - z3, FIX(2.546640132)); /* c2+c4 */ + tmp23 = MULTIPLY(z2 - z1, FIX(0.430815045)); /* c2-c6 */ + z4 = z1 + z3; + tmp24 = MULTIPLY(z4, - FIX(1.155664402)); /* -(c2-c10) */ + z4 -= z2; + tmp25 = tmp10 + MULTIPLY(z4, FIX(1.356927976)); /* c2 */ + tmp21 = tmp20 + tmp23 + tmp25 - + MULTIPLY(z2, FIX(1.821790775)); /* c2+c4+c10-c6 */ + tmp20 += tmp25 + MULTIPLY(z3, FIX(2.115825087)); /* c4+c6 */ + tmp23 += tmp25 - MULTIPLY(z1, FIX(1.513598477)); /* c6+c8 */ + tmp24 += tmp25; + tmp22 = tmp24 - MULTIPLY(z3, FIX(0.788749120)); /* c8+c10 */ + tmp24 += MULTIPLY(z2, FIX(1.944413522)) - /* c2+c8 */ + MULTIPLY(z1, FIX(1.390975730)); /* c4+c10 */ + tmp25 = tmp10 - MULTIPLY(z4, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = z1 + z2; + tmp14 = MULTIPLY(tmp11 + z3 + z4, FIX(0.398430003)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.887983902)); /* c3-c9 */ + tmp12 = MULTIPLY(z1 + z3, FIX(0.670361295)); /* c5-c9 */ + tmp13 = tmp14 + MULTIPLY(z1 + z4, FIX(0.366151574)); /* c7-c9 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(z1, FIX(0.923107866)); /* c7+c5+c3-c1-2*c9 */ + z1 = tmp14 - MULTIPLY(z2 + z3, FIX(1.163011579)); /* c7+c9 */ + tmp11 += z1 + MULTIPLY(z2, FIX(2.073276588)); /* c1+c7+3*c9-c3 */ + tmp12 += z1 - MULTIPLY(z3, FIX(1.192193623)); /* c3+c5-c7-c9 */ + z1 = MULTIPLY(z2 + z4, - FIX(1.798248910)); /* -(c1+c9) */ + tmp11 += z1; + tmp13 += z1 + MULTIPLY(z4, FIX(2.102458632)); /* c1+c5+c9-c7 */ + tmp14 += MULTIPLY(z2, - FIX(1.467221301)) + /* -(c5+c9) */ + MULTIPLY(z3, FIX(1.001388905)) - /* c1-c9 */ + MULTIPLY(z4, FIX(1.684843907)); /* c3+c9 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 11 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 11; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp10 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp10 <<= CONST_BITS; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[4]; + z3 = (INT32) wsptr[6]; + + tmp20 = MULTIPLY(z2 - z3, FIX(2.546640132)); /* c2+c4 */ + tmp23 = MULTIPLY(z2 - z1, FIX(0.430815045)); /* c2-c6 */ + z4 = z1 + z3; + tmp24 = MULTIPLY(z4, - FIX(1.155664402)); /* -(c2-c10) */ + z4 -= z2; + tmp25 = tmp10 + MULTIPLY(z4, FIX(1.356927976)); /* c2 */ + tmp21 = tmp20 + tmp23 + tmp25 - + MULTIPLY(z2, FIX(1.821790775)); /* c2+c4+c10-c6 */ + tmp20 += tmp25 + MULTIPLY(z3, FIX(2.115825087)); /* c4+c6 */ + tmp23 += tmp25 - MULTIPLY(z1, FIX(1.513598477)); /* c6+c8 */ + tmp24 += tmp25; + tmp22 = tmp24 - MULTIPLY(z3, FIX(0.788749120)); /* c8+c10 */ + tmp24 += MULTIPLY(z2, FIX(1.944413522)) - /* c2+c8 */ + MULTIPLY(z1, FIX(1.390975730)); /* c4+c10 */ + tmp25 = tmp10 - MULTIPLY(z4, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = z1 + z2; + tmp14 = MULTIPLY(tmp11 + z3 + z4, FIX(0.398430003)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.887983902)); /* c3-c9 */ + tmp12 = MULTIPLY(z1 + z3, FIX(0.670361295)); /* c5-c9 */ + tmp13 = tmp14 + MULTIPLY(z1 + z4, FIX(0.366151574)); /* c7-c9 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(z1, FIX(0.923107866)); /* c7+c5+c3-c1-2*c9 */ + z1 = tmp14 - MULTIPLY(z2 + z3, FIX(1.163011579)); /* c7+c9 */ + tmp11 += z1 + MULTIPLY(z2, FIX(2.073276588)); /* c1+c7+3*c9-c3 */ + tmp12 += z1 - MULTIPLY(z3, FIX(1.192193623)); /* c3+c5-c7-c9 */ + z1 = MULTIPLY(z2 + z4, - FIX(1.798248910)); /* -(c1+c9) */ + tmp11 += z1; + tmp13 += z1 + MULTIPLY(z4, FIX(2.102458632)); /* c1+c5+c9-c7 */ + tmp14 += MULTIPLY(z2, - FIX(1.467221301)) + /* -(c5+c9) */ + MULTIPLY(z3, FIX(1.001388905)) - /* c1-c9 */ + MULTIPLY(z4, FIX(1.684843907)); /* c3+c9 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 12x12 output block. + * + * Optimized algorithm with 15 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/24). + */ + +GLOBAL(void) +jpeg_idct_12x12 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*12]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z3 += ONE << (CONST_BITS-PASS1_BITS-1); + + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ + z1 <<= CONST_BITS; + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + z2 <<= CONST_BITS; + + tmp12 = z1 - z2; + + tmp21 = z3 + tmp12; + tmp24 = z3 - tmp12; + + tmp12 = z4 + z2; + + tmp20 = tmp10 + tmp12; + tmp25 = tmp10 - tmp12; + + tmp12 = z4 - z1 - z2; + + tmp22 = tmp11 + tmp12; + tmp23 = tmp11 - tmp12; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ + tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ + + tmp10 = z1 + z3; + tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ + tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ + tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ + tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ + tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ + tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ + tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ + MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ + + z1 -= z4; + z2 -= z3; + z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ + tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ + tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 12 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 12; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 <<= CONST_BITS; + + z4 = (INT32) wsptr[4]; + z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + z1 = (INT32) wsptr[2]; + z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ + z1 <<= CONST_BITS; + z2 = (INT32) wsptr[6]; + z2 <<= CONST_BITS; + + tmp12 = z1 - z2; + + tmp21 = z3 + tmp12; + tmp24 = z3 - tmp12; + + tmp12 = z4 + z2; + + tmp20 = tmp10 + tmp12; + tmp25 = tmp10 - tmp12; + + tmp12 = z4 - z1 - z2; + + tmp22 = tmp11 + tmp12; + tmp23 = tmp11 - tmp12; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ + tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ + + tmp10 = z1 + z3; + tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ + tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ + tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ + tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ + tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ + tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ + tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ + MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ + + z1 -= z4; + z2 -= z3; + z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ + tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ + tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 13x13 output block. + * + * Optimized algorithm with 29 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/26). + */ + +GLOBAL(void) +jpeg_idct_13x13 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*13]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z1 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z4 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + tmp12 = MULTIPLY(tmp10, FIX(1.155388986)); /* (c4+c6)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.096834934)) + z1; /* (c4-c6)/2 */ + + tmp20 = MULTIPLY(z2, FIX(1.373119086)) + tmp12 + tmp13; /* c2 */ + tmp22 = MULTIPLY(z2, FIX(0.501487041)) - tmp12 + tmp13; /* c10 */ + + tmp12 = MULTIPLY(tmp10, FIX(0.316450131)); /* (c8-c12)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.486914739)) + z1; /* (c8+c12)/2 */ + + tmp21 = MULTIPLY(z2, FIX(1.058554052)) - tmp12 + tmp13; /* c6 */ + tmp25 = MULTIPLY(z2, - FIX(1.252223920)) + tmp12 + tmp13; /* c4 */ + + tmp12 = MULTIPLY(tmp10, FIX(0.435816023)); /* (c2-c10)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.937303064)) - z1; /* (c2+c10)/2 */ + + tmp23 = MULTIPLY(z2, - FIX(0.170464608)) - tmp12 - tmp13; /* c12 */ + tmp24 = MULTIPLY(z2, - FIX(0.803364869)) + tmp12 - tmp13; /* c8 */ + + tmp26 = MULTIPLY(tmp11 - z2, FIX(1.414213562)) + z1; /* c0 */ + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = MULTIPLY(z1 + z2, FIX(1.322312651)); /* c3 */ + tmp12 = MULTIPLY(z1 + z3, FIX(1.163874945)); /* c5 */ + tmp15 = z1 + z4; + tmp13 = MULTIPLY(tmp15, FIX(0.937797057)); /* c7 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(z1, FIX(2.020082300)); /* c7+c5+c3-c1 */ + tmp14 = MULTIPLY(z2 + z3, - FIX(0.338443458)); /* -c11 */ + tmp11 += tmp14 + MULTIPLY(z2, FIX(0.837223564)); /* c5+c9+c11-c3 */ + tmp12 += tmp14 - MULTIPLY(z3, FIX(1.572116027)); /* c1+c5-c9-c11 */ + tmp14 = MULTIPLY(z2 + z4, - FIX(1.163874945)); /* -c5 */ + tmp11 += tmp14; + tmp13 += tmp14 + MULTIPLY(z4, FIX(2.205608352)); /* c3+c5+c9-c7 */ + tmp14 = MULTIPLY(z3 + z4, - FIX(0.657217813)); /* -c9 */ + tmp12 += tmp14; + tmp13 += tmp14; + tmp15 = MULTIPLY(tmp15, FIX(0.338443458)); /* c11 */ + tmp14 = tmp15 + MULTIPLY(z1, FIX(0.318774355)) - /* c9-c11 */ + MULTIPLY(z2, FIX(0.466105296)); /* c1-c7 */ + z1 = MULTIPLY(z3 - z2, FIX(0.937797057)); /* c7 */ + tmp14 += z1; + tmp15 += z1 + MULTIPLY(z3, FIX(0.384515595)) - /* c3-c7 */ + MULTIPLY(z4, FIX(1.742345811)); /* c1+c11 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*12] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp26, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 13 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 13; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z1 <<= CONST_BITS; + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[4]; + z4 = (INT32) wsptr[6]; + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + tmp12 = MULTIPLY(tmp10, FIX(1.155388986)); /* (c4+c6)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.096834934)) + z1; /* (c4-c6)/2 */ + + tmp20 = MULTIPLY(z2, FIX(1.373119086)) + tmp12 + tmp13; /* c2 */ + tmp22 = MULTIPLY(z2, FIX(0.501487041)) - tmp12 + tmp13; /* c10 */ + + tmp12 = MULTIPLY(tmp10, FIX(0.316450131)); /* (c8-c12)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.486914739)) + z1; /* (c8+c12)/2 */ + + tmp21 = MULTIPLY(z2, FIX(1.058554052)) - tmp12 + tmp13; /* c6 */ + tmp25 = MULTIPLY(z2, - FIX(1.252223920)) + tmp12 + tmp13; /* c4 */ + + tmp12 = MULTIPLY(tmp10, FIX(0.435816023)); /* (c2-c10)/2 */ + tmp13 = MULTIPLY(tmp11, FIX(0.937303064)) - z1; /* (c2+c10)/2 */ + + tmp23 = MULTIPLY(z2, - FIX(0.170464608)) - tmp12 - tmp13; /* c12 */ + tmp24 = MULTIPLY(z2, - FIX(0.803364869)) + tmp12 - tmp13; /* c8 */ + + tmp26 = MULTIPLY(tmp11 - z2, FIX(1.414213562)) + z1; /* c0 */ + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = MULTIPLY(z1 + z2, FIX(1.322312651)); /* c3 */ + tmp12 = MULTIPLY(z1 + z3, FIX(1.163874945)); /* c5 */ + tmp15 = z1 + z4; + tmp13 = MULTIPLY(tmp15, FIX(0.937797057)); /* c7 */ + tmp10 = tmp11 + tmp12 + tmp13 - + MULTIPLY(z1, FIX(2.020082300)); /* c7+c5+c3-c1 */ + tmp14 = MULTIPLY(z2 + z3, - FIX(0.338443458)); /* -c11 */ + tmp11 += tmp14 + MULTIPLY(z2, FIX(0.837223564)); /* c5+c9+c11-c3 */ + tmp12 += tmp14 - MULTIPLY(z3, FIX(1.572116027)); /* c1+c5-c9-c11 */ + tmp14 = MULTIPLY(z2 + z4, - FIX(1.163874945)); /* -c5 */ + tmp11 += tmp14; + tmp13 += tmp14 + MULTIPLY(z4, FIX(2.205608352)); /* c3+c5+c9-c7 */ + tmp14 = MULTIPLY(z3 + z4, - FIX(0.657217813)); /* -c9 */ + tmp12 += tmp14; + tmp13 += tmp14; + tmp15 = MULTIPLY(tmp15, FIX(0.338443458)); /* c11 */ + tmp14 = tmp15 + MULTIPLY(z1, FIX(0.318774355)) - /* c9-c11 */ + MULTIPLY(z2, FIX(0.466105296)); /* c1-c7 */ + z1 = MULTIPLY(z3 - z2, FIX(0.937797057)); /* c7 */ + tmp14 += z1; + tmp15 += z1 + MULTIPLY(z3, FIX(0.384515595)) - /* c3-c7 */ + MULTIPLY(z4, FIX(1.742345811)); /* c1+c11 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 14x14 output block. + * + * Optimized algorithm with 20 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/28). + */ + +GLOBAL(void) +jpeg_idct_14x14 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*14]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z1 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ + z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ + z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ + + tmp10 = z1 + z2; + tmp11 = z1 + z3; + tmp12 = z1 - z4; + + tmp23 = RIGHT_SHIFT(z1 - ((z2 + z3 - z4) << 1), /* c0 = (c4+c12-c8)*2 */ + CONST_BITS-PASS1_BITS); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ + + tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ + tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ + tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ + MULTIPLY(z2, FIX(1.378756276)); /* c2 */ + + tmp20 = tmp10 + tmp13; + tmp26 = tmp10 - tmp13; + tmp21 = tmp11 + tmp14; + tmp25 = tmp11 - tmp14; + tmp22 = tmp12 + tmp15; + tmp24 = tmp12 - tmp15; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp13 = z4 << CONST_BITS; + + tmp14 = z1 + z3; + tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ + tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ + tmp10 = tmp11 + tmp12 + tmp13 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ + tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ + tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ + z1 -= z2; + tmp15 = MULTIPLY(z1, FIX(0.467085129)) - tmp13; /* c11 */ + tmp16 += tmp15; + z1 += z4; + z4 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - tmp13; /* -c13 */ + tmp11 += z4 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ + tmp12 += z4 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ + z4 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ + tmp14 += z4 + tmp13 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ + tmp15 += z4 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ + + tmp13 = (z1 - z3) << PASS1_BITS; + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*13] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*12] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) (tmp23 + tmp13); + wsptr[8*10] = (int) (tmp23 - tmp13); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp16, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp26 - tmp16, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 14 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 14; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z1 <<= CONST_BITS; + z4 = (INT32) wsptr[4]; + z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ + z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ + z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ + + tmp10 = z1 + z2; + tmp11 = z1 + z3; + tmp12 = z1 - z4; + + tmp23 = z1 - ((z2 + z3 - z4) << 1); /* c0 = (c4+c12-c8)*2 */ + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[6]; + + z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ + + tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ + tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ + tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ + MULTIPLY(z2, FIX(1.378756276)); /* c2 */ + + tmp20 = tmp10 + tmp13; + tmp26 = tmp10 - tmp13; + tmp21 = tmp11 + tmp14; + tmp25 = tmp11 - tmp14; + tmp22 = tmp12 + tmp15; + tmp24 = tmp12 - tmp15; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + z4 <<= CONST_BITS; + + tmp14 = z1 + z3; + tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ + tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ + tmp10 = tmp11 + tmp12 + z4 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ + tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ + tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ + z1 -= z2; + tmp15 = MULTIPLY(z1, FIX(0.467085129)) - z4; /* c11 */ + tmp16 += tmp15; + tmp13 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - z4; /* -c13 */ + tmp11 += tmp13 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ + tmp12 += tmp13 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ + tmp13 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ + tmp14 += tmp13 + z4 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ + tmp15 += tmp13 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ + + tmp13 = ((z1 - z3) << CONST_BITS) + z4; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 15x15 output block. + * + * Optimized algorithm with 22 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/30). + */ + +GLOBAL(void) +jpeg_idct_15x15 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*15]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z1 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z4 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = MULTIPLY(z4, FIX(0.437016024)); /* c12 */ + tmp11 = MULTIPLY(z4, FIX(1.144122806)); /* c6 */ + + tmp12 = z1 - tmp10; + tmp13 = z1 + tmp11; + z1 -= (tmp11 - tmp10) << 1; /* c0 = (c6-c12)*2 */ + + z4 = z2 - z3; + z3 += z2; + tmp10 = MULTIPLY(z3, FIX(1.337628990)); /* (c2+c4)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.045680613)); /* (c2-c4)/2 */ + z2 = MULTIPLY(z2, FIX(1.439773946)); /* c4+c14 */ + + tmp20 = tmp13 + tmp10 + tmp11; + tmp23 = tmp12 - tmp10 + tmp11 + z2; + + tmp10 = MULTIPLY(z3, FIX(0.547059574)); /* (c8+c14)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.399234004)); /* (c8-c14)/2 */ + + tmp25 = tmp13 - tmp10 - tmp11; + tmp26 = tmp12 + tmp10 - tmp11 - z2; + + tmp10 = MULTIPLY(z3, FIX(0.790569415)); /* (c6+c12)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.353553391)); /* (c6-c12)/2 */ + + tmp21 = tmp12 + tmp10 + tmp11; + tmp24 = tmp13 - tmp10 + tmp11; + tmp11 += tmp11; + tmp22 = z1 + tmp11; /* c10 = c6-c12 */ + tmp27 = z1 - tmp11 - tmp11; /* c0 = (c6-c12)*2 */ + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z4 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z3 = MULTIPLY(z4, FIX(1.224744871)); /* c5 */ + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp13 = z2 - z4; + tmp15 = MULTIPLY(z1 + tmp13, FIX(0.831253876)); /* c9 */ + tmp11 = tmp15 + MULTIPLY(z1, FIX(0.513743148)); /* c3-c9 */ + tmp14 = tmp15 - MULTIPLY(tmp13, FIX(2.176250899)); /* c3+c9 */ + + tmp13 = MULTIPLY(z2, - FIX(0.831253876)); /* -c9 */ + tmp15 = MULTIPLY(z2, - FIX(1.344997024)); /* -c3 */ + z2 = z1 - z4; + tmp12 = z3 + MULTIPLY(z2, FIX(1.406466353)); /* c1 */ + + tmp10 = tmp12 + MULTIPLY(z4, FIX(2.457431844)) - tmp15; /* c1+c7 */ + tmp16 = tmp12 - MULTIPLY(z1, FIX(1.112434820)) + tmp13; /* c1-c13 */ + tmp12 = MULTIPLY(z2, FIX(1.224744871)) - z3; /* c5 */ + z2 = MULTIPLY(z1 + z4, FIX(0.575212477)); /* c11 */ + tmp13 += z2 + MULTIPLY(z1, FIX(0.475753014)) - z3; /* c7-c11 */ + tmp15 += z2 - MULTIPLY(z4, FIX(0.869244010)) + z3; /* c11+c13 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*14] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*13] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*12] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp16, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp26 - tmp16, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp27, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 15 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 15; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z1 <<= CONST_BITS; + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[4]; + z4 = (INT32) wsptr[6]; + + tmp10 = MULTIPLY(z4, FIX(0.437016024)); /* c12 */ + tmp11 = MULTIPLY(z4, FIX(1.144122806)); /* c6 */ + + tmp12 = z1 - tmp10; + tmp13 = z1 + tmp11; + z1 -= (tmp11 - tmp10) << 1; /* c0 = (c6-c12)*2 */ + + z4 = z2 - z3; + z3 += z2; + tmp10 = MULTIPLY(z3, FIX(1.337628990)); /* (c2+c4)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.045680613)); /* (c2-c4)/2 */ + z2 = MULTIPLY(z2, FIX(1.439773946)); /* c4+c14 */ + + tmp20 = tmp13 + tmp10 + tmp11; + tmp23 = tmp12 - tmp10 + tmp11 + z2; + + tmp10 = MULTIPLY(z3, FIX(0.547059574)); /* (c8+c14)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.399234004)); /* (c8-c14)/2 */ + + tmp25 = tmp13 - tmp10 - tmp11; + tmp26 = tmp12 + tmp10 - tmp11 - z2; + + tmp10 = MULTIPLY(z3, FIX(0.790569415)); /* (c6+c12)/2 */ + tmp11 = MULTIPLY(z4, FIX(0.353553391)); /* (c6-c12)/2 */ + + tmp21 = tmp12 + tmp10 + tmp11; + tmp24 = tmp13 - tmp10 + tmp11; + tmp11 += tmp11; + tmp22 = z1 + tmp11; /* c10 = c6-c12 */ + tmp27 = z1 - tmp11 - tmp11; /* c0 = (c6-c12)*2 */ + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z4 = (INT32) wsptr[5]; + z3 = MULTIPLY(z4, FIX(1.224744871)); /* c5 */ + z4 = (INT32) wsptr[7]; + + tmp13 = z2 - z4; + tmp15 = MULTIPLY(z1 + tmp13, FIX(0.831253876)); /* c9 */ + tmp11 = tmp15 + MULTIPLY(z1, FIX(0.513743148)); /* c3-c9 */ + tmp14 = tmp15 - MULTIPLY(tmp13, FIX(2.176250899)); /* c3+c9 */ + + tmp13 = MULTIPLY(z2, - FIX(0.831253876)); /* -c9 */ + tmp15 = MULTIPLY(z2, - FIX(1.344997024)); /* -c3 */ + z2 = z1 - z4; + tmp12 = z3 + MULTIPLY(z2, FIX(1.406466353)); /* c1 */ + + tmp10 = tmp12 + MULTIPLY(z4, FIX(2.457431844)) - tmp15; /* c1+c7 */ + tmp16 = tmp12 - MULTIPLY(z1, FIX(1.112434820)) + tmp13; /* c1-c13 */ + tmp12 = MULTIPLY(z2, FIX(1.224744871)) - z3; /* c5 */ + z2 = MULTIPLY(z1 + z4, FIX(0.575212477)); /* c11 */ + tmp13 += z2 + MULTIPLY(z1, FIX(0.475753014)) - z3; /* c7-c11 */ + tmp15 += z2 - MULTIPLY(z4, FIX(0.869244010)) + z3; /* c11+c13 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[14] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp27, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 16x16 output block. + * + * Optimized algorithm with 28 multiplications in the 1-D kernel. + * cK represents sqrt(2) * cos(K*pi/32). + */ + +GLOBAL(void) +jpeg_idct_16x16 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*16]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += 1 << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ + tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + tmp12 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + z3 = z1 - z2; + z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ + z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ + + tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ + tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ + tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ + tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ + + tmp20 = tmp10 + tmp0; + tmp27 = tmp10 - tmp0; + tmp21 = tmp12 + tmp1; + tmp26 = tmp12 - tmp1; + tmp22 = tmp13 + tmp2; + tmp25 = tmp13 - tmp2; + tmp23 = tmp11 + tmp3; + tmp24 = tmp11 - tmp3; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = z1 + z3; + + tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ + tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ + tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ + tmp13 = tmp10 + tmp11 + tmp12 - + MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ + z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ + tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ + tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ + z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ + tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ + tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ + z2 += z4; + z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ + tmp1 += z1; + tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ + z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ + tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ + tmp12 += z2; + z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ + tmp2 += z2; + tmp3 += z2; + z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ + tmp10 += z2; + tmp11 += z2; + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*15] = (int) RIGHT_SHIFT(tmp20 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*14] = (int) RIGHT_SHIFT(tmp21 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*13] = (int) RIGHT_SHIFT(tmp22 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*12] = (int) RIGHT_SHIFT(tmp23 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp24 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp25 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp26 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp27 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp27 - tmp13, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 16 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 16; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + + z1 = (INT32) wsptr[4]; + tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ + tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + tmp12 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[6]; + z3 = z1 - z2; + z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ + z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ + + tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ + tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ + tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ + tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ + + tmp20 = tmp10 + tmp0; + tmp27 = tmp10 - tmp0; + tmp21 = tmp12 + tmp1; + tmp26 = tmp12 - tmp1; + tmp22 = tmp13 + tmp2; + tmp25 = tmp13 - tmp2; + tmp23 = tmp11 + tmp3; + tmp24 = tmp11 - tmp3; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = z1 + z3; + + tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ + tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ + tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ + tmp13 = tmp10 + tmp11 + tmp12 - + MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ + z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ + tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ + tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ + z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ + tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ + tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ + z2 += z4; + z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ + tmp1 += z1; + tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ + z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ + tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ + tmp12 += z2; + z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ + tmp2 += z2; + tmp3 += z2; + z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ + tmp10 += z2; + tmp11 += z2; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[15] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[14] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp27 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp27 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 16x8 output block. + * + * 8-point IDCT in pass 1 (columns), 16-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_16x8 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*8]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z2 <<= CONST_BITS; + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z2 += ONE << (CONST_BITS-PASS1_BITS-1); + + tmp0 = z2 + z3; + tmp1 = z2 - z3; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[DCTSIZE*0] = (int) RIGHT_SHIFT(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*7] = (int) RIGHT_SHIFT(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*1] = (int) RIGHT_SHIFT(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*6] = (int) RIGHT_SHIFT(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*2] = (int) RIGHT_SHIFT(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*5] = (int) RIGHT_SHIFT(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*3] = (int) RIGHT_SHIFT(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*4] = (int) RIGHT_SHIFT(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process 8 rows from work array, store into output array. + * 16-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/32). + */ + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + + z1 = (INT32) wsptr[4]; + tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ + tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + tmp12 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[6]; + z3 = z1 - z2; + z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ + z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ + + tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ + tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ + tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ + tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ + + tmp20 = tmp10 + tmp0; + tmp27 = tmp10 - tmp0; + tmp21 = tmp12 + tmp1; + tmp26 = tmp12 - tmp1; + tmp22 = tmp13 + tmp2; + tmp25 = tmp13 - tmp2; + tmp23 = tmp11 + tmp3; + tmp24 = tmp11 - tmp3; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = z1 + z3; + + tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ + tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ + tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ + tmp13 = tmp10 + tmp11 + tmp12 - + MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ + z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ + tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ + tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ + z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ + tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ + tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ + z2 += z4; + z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ + tmp1 += z1; + tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ + z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ + tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ + tmp12 += z2; + z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ + tmp2 += z2; + tmp3 += z2; + z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ + tmp10 += z2; + tmp11 += z2; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[15] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[14] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp27 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp27 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 14x7 output block. + * + * 7-point IDCT in pass 1 (columns), 14-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_14x7 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*7]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 7-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/14). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp23 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp23 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp23 += ONE << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp20 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ + tmp22 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ + tmp21 = tmp20 + tmp22 + tmp23 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ + tmp10 = z1 + z3; + z2 -= tmp10; + tmp10 = MULTIPLY(tmp10, FIX(1.274162392)) + tmp23; /* c2 */ + tmp20 += tmp10 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ + tmp22 += tmp10 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ + tmp23 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp11 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp10 = tmp11 - tmp12; + tmp11 += tmp12; + tmp12 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ + tmp11 += tmp12; + z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ + tmp10 += z2; + tmp12 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 7 rows from work array, store into output array. + * 14-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/28). + */ + wsptr = workspace; + for (ctr = 0; ctr < 7; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z1 <<= CONST_BITS; + z4 = (INT32) wsptr[4]; + z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ + z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ + z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ + + tmp10 = z1 + z2; + tmp11 = z1 + z3; + tmp12 = z1 - z4; + + tmp23 = z1 - ((z2 + z3 - z4) << 1); /* c0 = (c4+c12-c8)*2 */ + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[6]; + + z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ + + tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ + tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ + tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ + MULTIPLY(z2, FIX(1.378756276)); /* c2 */ + + tmp20 = tmp10 + tmp13; + tmp26 = tmp10 - tmp13; + tmp21 = tmp11 + tmp14; + tmp25 = tmp11 - tmp14; + tmp22 = tmp12 + tmp15; + tmp24 = tmp12 - tmp15; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + z4 <<= CONST_BITS; + + tmp14 = z1 + z3; + tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ + tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ + tmp10 = tmp11 + tmp12 + z4 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ + tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ + tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ + z1 -= z2; + tmp15 = MULTIPLY(z1, FIX(0.467085129)) - z4; /* c11 */ + tmp16 += tmp15; + tmp13 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - z4; /* -c13 */ + tmp11 += tmp13 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ + tmp12 += tmp13 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ + tmp13 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ + tmp14 += tmp13 + z4 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ + tmp15 += tmp13 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ + + tmp13 = ((z1 - z3) << CONST_BITS) + z4; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp16, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 12x6 output block. + * + * 6-point IDCT in pass 1 (columns), 12-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_12x6 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*6]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 6-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/12). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp10 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp10 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp10 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp12 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp20 = MULTIPLY(tmp12, FIX(0.707106781)); /* c4 */ + tmp11 = tmp10 + tmp20; + tmp21 = RIGHT_SHIFT(tmp10 - tmp20 - tmp20, CONST_BITS-PASS1_BITS); + tmp20 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp10 = MULTIPLY(tmp20, FIX(1.224744871)); /* c2 */ + tmp20 = tmp11 + tmp10; + tmp22 = tmp11 - tmp10; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp11 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp10 = tmp11 + ((z1 + z2) << CONST_BITS); + tmp12 = tmp11 + ((z3 - z2) << CONST_BITS); + tmp11 = (z1 - z2 - z3) << PASS1_BITS; + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) (tmp21 + tmp11); + wsptr[8*4] = (int) (tmp21 - tmp11); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 6 rows from work array, store into output array. + * 12-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/24). + */ + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 <<= CONST_BITS; + + z4 = (INT32) wsptr[4]; + z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + z1 = (INT32) wsptr[2]; + z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ + z1 <<= CONST_BITS; + z2 = (INT32) wsptr[6]; + z2 <<= CONST_BITS; + + tmp12 = z1 - z2; + + tmp21 = z3 + tmp12; + tmp24 = z3 - tmp12; + + tmp12 = z4 + z2; + + tmp20 = tmp10 + tmp12; + tmp25 = tmp10 - tmp12; + + tmp12 = z4 - z1 - z2; + + tmp22 = tmp11 + tmp12; + tmp23 = tmp11 - tmp12; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z4 = (INT32) wsptr[7]; + + tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ + tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ + + tmp10 = z1 + z3; + tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ + tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ + tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ + tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ + tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ + tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ + tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ + MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ + + z1 -= z4; + z2 -= z3; + z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ + tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ + tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 10x5 output block. + * + * 5-point IDCT in pass 1 (columns), 10-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_10x5 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*5]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 5-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/10). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp12 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp12 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp12 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp13 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp14 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z1 = MULTIPLY(tmp13 + tmp14, FIX(0.790569415)); /* (c2+c4)/2 */ + z2 = MULTIPLY(tmp13 - tmp14, FIX(0.353553391)); /* (c2-c4)/2 */ + z3 = tmp12 + z2; + tmp10 = z3 + z1; + tmp11 = z3 - z1; + tmp12 -= z2 << 2; + + /* Odd part */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ + tmp13 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ + tmp14 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp10 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp10 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp11 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp11 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp12, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 5 rows from work array, store into output array. + * 10-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/20). + */ + wsptr = workspace; + for (ctr = 0; ctr < 5; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 <<= CONST_BITS; + z4 = (INT32) wsptr[4]; + z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ + z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ + tmp10 = z3 + z1; + tmp11 = z3 - z2; + + tmp22 = z3 - ((z1 - z2) << 1); /* c0 = (c4-c8)*2 */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ + tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ + tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ + + tmp20 = tmp10 + tmp12; + tmp24 = tmp10 - tmp12; + tmp21 = tmp11 + tmp13; + tmp23 = tmp11 - tmp13; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + z3 <<= CONST_BITS; + z4 = (INT32) wsptr[7]; + + tmp11 = z2 + z4; + tmp13 = z2 - z4; + + tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ + + z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ + z4 = z3 + tmp12; + + tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ + tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ + + z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ + z4 = z3 - tmp12 - (tmp13 << (CONST_BITS - 1)); + + tmp12 = ((z1 - tmp13) << CONST_BITS) - z3; + + tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ + tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 8; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 8x4 output block. + * + * 4-point IDCT in pass 1 (columns), 8-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_8x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 4-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp10 = (tmp0 + tmp2) << PASS1_BITS; + tmp12 = (tmp0 - tmp2) << PASS1_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp0 = RIGHT_SHIFT(z1 + MULTIPLY(z2, FIX_0_765366865), /* c2-c6 */ + CONST_BITS-PASS1_BITS); + tmp2 = RIGHT_SHIFT(z1 - MULTIPLY(z3, FIX_1_847759065), /* c2+c6 */ + CONST_BITS-PASS1_BITS); + + /* Final output stage */ + + wsptr[8*0] = (int) (tmp10 + tmp0); + wsptr[8*3] = (int) (tmp10 - tmp0); + wsptr[8*1] = (int) (tmp12 + tmp2); + wsptr[8*2] = (int) (tmp12 - tmp2); + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + /* Add fudge factor here for final descale. */ + z2 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 = (INT32) wsptr[4]; + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 6x3 output block. + * + * 3-point IDCT in pass 1 (columns), 6-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_6x3 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[6*3]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 3-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/6). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ + tmp10 = tmp0 + tmp12; + tmp2 = tmp0 - tmp12 - tmp12; + + /* Odd part */ + + tmp12 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ + + /* Final output stage */ + + wsptr[6*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[6*2] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[6*1] = (int) RIGHT_SHIFT(tmp2, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 3 rows from work array, store into output array. + * 6-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/12). + */ + wsptr = workspace; + for (ctr = 0; ctr < 3; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + tmp2 = (INT32) wsptr[4]; + tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ + tmp1 = tmp0 + tmp10; + tmp11 = tmp0 - tmp10 - tmp10; + tmp10 = (INT32) wsptr[2]; + tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ + tmp10 = tmp1 + tmp0; + tmp12 = tmp1 - tmp0; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); + tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); + tmp1 = (z1 - z2 - z3) << CONST_BITS; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 6; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 4x2 output block. + * + * 2-point IDCT in pass 1 (columns), 4-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_4x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + INT32 * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + INT32 workspace[4*2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp10 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + /* Odd part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + /* Final output stage */ + + wsptr[4*0] = tmp10 + tmp0; + wsptr[4*1] = tmp10 - tmp0; + } + + /* Pass 2: process 2 rows from work array, store into output array. + * 4-point IDCT kernel, + * cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point IDCT]. + */ + wsptr = workspace; + for (ctr = 0; ctr < 2; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = wsptr[0] + (ONE << 2); + tmp2 = wsptr[2]; + + tmp10 = (tmp0 + tmp2) << CONST_BITS; + tmp12 = (tmp0 - tmp2) << CONST_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = wsptr[1]; + z3 = wsptr[3]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + tmp0 = z1 + MULTIPLY(z2, FIX_0_765366865); /* c2-c6 */ + tmp2 = z1 - MULTIPLY(z3, FIX_1_847759065); /* c2+c6 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+3) + & RANGE_MASK]; + + wsptr += 4; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 2x1 output block. + * + * 1-point IDCT in pass 1 (columns), 2-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_2x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp10; + ISLOW_MULT_TYPE * quantptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* Pass 1: empty. */ + + /* Pass 2: process 1 row from input, store into output array. */ + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + outptr = output_buf[0] + output_col; + + /* Even part */ + + tmp10 = DEQUANTIZE(coef_block[0], quantptr[0]); + /* Add fudge factor here for final descale. */ + tmp10 += ONE << 2; + + /* Odd part */ + + tmp0 = DEQUANTIZE(coef_block[1], quantptr[1]); + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, 3) & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, 3) & RANGE_MASK]; +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 8x16 output block. + * + * 16-point IDCT in pass 1 (columns), 8-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_8x16 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[8*16]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 16-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/32). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + + z1 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ + tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ + + tmp10 = tmp0 + tmp1; + tmp11 = tmp0 - tmp1; + tmp12 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + z3 = z1 - z2; + z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ + z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ + + tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ + tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ + tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ + tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ + + tmp20 = tmp10 + tmp0; + tmp27 = tmp10 - tmp0; + tmp21 = tmp12 + tmp1; + tmp26 = tmp12 - tmp1; + tmp22 = tmp13 + tmp2; + tmp25 = tmp13 - tmp2; + tmp23 = tmp11 + tmp3; + tmp24 = tmp11 - tmp3; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = z1 + z3; + + tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ + tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ + tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ + tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ + tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ + tmp0 = tmp1 + tmp2 + tmp3 - + MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ + tmp13 = tmp10 + tmp11 + tmp12 - + MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ + z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ + tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ + tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ + z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ + tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ + tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ + z2 += z4; + z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ + tmp1 += z1; + tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ + z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ + tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ + tmp12 += z2; + z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ + tmp2 += z2; + tmp3 += z2; + z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ + tmp10 += z2; + tmp11 += z2; + + /* Final output stage */ + + wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*15] = (int) RIGHT_SHIFT(tmp20 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*14] = (int) RIGHT_SHIFT(tmp21 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*13] = (int) RIGHT_SHIFT(tmp22 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*12] = (int) RIGHT_SHIFT(tmp23 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*11] = (int) RIGHT_SHIFT(tmp24 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*10] = (int) RIGHT_SHIFT(tmp25 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*9] = (int) RIGHT_SHIFT(tmp26 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) RIGHT_SHIFT(tmp27 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[8*8] = (int) RIGHT_SHIFT(tmp27 - tmp13, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < 16; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + /* Add fudge factor here for final descale. */ + z2 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + z3 = (INT32) wsptr[4]; + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 7x14 output block. + * + * 14-point IDCT in pass 1 (columns), 7-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_7x14 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[7*14]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 14-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/28). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 7; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z1 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z1 += ONE << (CONST_BITS-PASS1_BITS-1); + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ + z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ + z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ + + tmp10 = z1 + z2; + tmp11 = z1 + z3; + tmp12 = z1 - z4; + + tmp23 = RIGHT_SHIFT(z1 - ((z2 + z3 - z4) << 1), /* c0 = (c4+c12-c8)*2 */ + CONST_BITS-PASS1_BITS); + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ + + tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ + tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ + tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ + MULTIPLY(z2, FIX(1.378756276)); /* c2 */ + + tmp20 = tmp10 + tmp13; + tmp26 = tmp10 - tmp13; + tmp21 = tmp11 + tmp14; + tmp25 = tmp11 - tmp14; + tmp22 = tmp12 + tmp15; + tmp24 = tmp12 - tmp15; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp13 = z4 << CONST_BITS; + + tmp14 = z1 + z3; + tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ + tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ + tmp10 = tmp11 + tmp12 + tmp13 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ + tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ + tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ + z1 -= z2; + tmp15 = MULTIPLY(z1, FIX(0.467085129)) - tmp13; /* c11 */ + tmp16 += tmp15; + z1 += z4; + z4 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - tmp13; /* -c13 */ + tmp11 += z4 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ + tmp12 += z4 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ + z4 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ + tmp14 += z4 + tmp13 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ + tmp15 += z4 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ + + tmp13 = (z1 - z3) << PASS1_BITS; + + /* Final output stage */ + + wsptr[7*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[7*13] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[7*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[7*12] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[7*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[7*11] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[7*3] = (int) (tmp23 + tmp13); + wsptr[7*10] = (int) (tmp23 - tmp13); + wsptr[7*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[7*9] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[7*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[7*8] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + wsptr[7*6] = (int) RIGHT_SHIFT(tmp26 + tmp16, CONST_BITS-PASS1_BITS); + wsptr[7*7] = (int) RIGHT_SHIFT(tmp26 - tmp16, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 14 rows from work array, store into output array. + * 7-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/14). + */ + wsptr = workspace; + for (ctr = 0; ctr < 14; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp23 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp23 <<= CONST_BITS; + + z1 = (INT32) wsptr[2]; + z2 = (INT32) wsptr[4]; + z3 = (INT32) wsptr[6]; + + tmp20 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ + tmp22 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ + tmp21 = tmp20 + tmp22 + tmp23 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ + tmp10 = z1 + z3; + z2 -= tmp10; + tmp10 = MULTIPLY(tmp10, FIX(1.274162392)) + tmp23; /* c2 */ + tmp20 += tmp10 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ + tmp22 += tmp10 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ + tmp23 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + + tmp11 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ + tmp12 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ + tmp10 = tmp11 - tmp12; + tmp11 += tmp12; + tmp12 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ + tmp11 += tmp12; + z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ + tmp10 += z2; + tmp12 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 7; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 6x12 output block. + * + * 12-point IDCT in pass 1 (columns), 6-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_6x12 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[6*12]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 12-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/24). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z3 += ONE << (CONST_BITS-PASS1_BITS-1); + + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ + + tmp10 = z3 + z4; + tmp11 = z3 - z4; + + z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ + z1 <<= CONST_BITS; + z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + z2 <<= CONST_BITS; + + tmp12 = z1 - z2; + + tmp21 = z3 + tmp12; + tmp24 = z3 - tmp12; + + tmp12 = z4 + z2; + + tmp20 = tmp10 + tmp12; + tmp25 = tmp10 - tmp12; + + tmp12 = z4 - z1 - z2; + + tmp22 = tmp11 + tmp12; + tmp23 = tmp11 - tmp12; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ + tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ + + tmp10 = z1 + z3; + tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ + tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ + tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ + tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ + tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ + tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ + tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ + MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ + + z1 -= z4; + z2 -= z3; + z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ + tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ + tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ + + /* Final output stage */ + + wsptr[6*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[6*11] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[6*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[6*10] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[6*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); + wsptr[6*9] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); + wsptr[6*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[6*8] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[6*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[6*7] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + wsptr[6*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); + wsptr[6*6] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 12 rows from work array, store into output array. + * 6-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/12). + */ + wsptr = workspace; + for (ctr = 0; ctr < 12; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp10 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp10 <<= CONST_BITS; + tmp12 = (INT32) wsptr[4]; + tmp20 = MULTIPLY(tmp12, FIX(0.707106781)); /* c4 */ + tmp11 = tmp10 + tmp20; + tmp21 = tmp10 - tmp20 - tmp20; + tmp20 = (INT32) wsptr[2]; + tmp10 = MULTIPLY(tmp20, FIX(1.224744871)); /* c2 */ + tmp20 = tmp11 + tmp10; + tmp22 = tmp11 - tmp10; + + /* Odd part */ + + z1 = (INT32) wsptr[1]; + z2 = (INT32) wsptr[3]; + z3 = (INT32) wsptr[5]; + tmp11 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp10 = tmp11 + ((z1 + z2) << CONST_BITS); + tmp12 = tmp11 + ((z3 - z2) << CONST_BITS); + tmp11 = (z1 - z2 - z3) << CONST_BITS; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 6; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 5x10 output block. + * + * 10-point IDCT in pass 1 (columns), 5-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_5x10 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp10, tmp11, tmp12, tmp13, tmp14; + INT32 tmp20, tmp21, tmp22, tmp23, tmp24; + INT32 z1, z2, z3, z4, z5; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[5*10]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 10-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/20). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 5; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z3 += ONE << (CONST_BITS-PASS1_BITS-1); + z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ + z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ + tmp10 = z3 + z1; + tmp11 = z3 - z2; + + tmp22 = RIGHT_SHIFT(z3 - ((z1 - z2) << 1), /* c0 = (c4-c8)*2 */ + CONST_BITS-PASS1_BITS); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ + tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ + tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ + + tmp20 = tmp10 + tmp12; + tmp24 = tmp10 - tmp12; + tmp21 = tmp11 + tmp13; + tmp23 = tmp11 - tmp13; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp11 = z2 + z4; + tmp13 = z2 - z4; + + tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ + z5 = z3 << CONST_BITS; + + z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ + z4 = z5 + tmp12; + + tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ + tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ + + z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ + z4 = z5 - tmp12 - (tmp13 << (CONST_BITS - 1)); + + tmp12 = (z1 - tmp13 - z3) << PASS1_BITS; + + tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ + tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ + + /* Final output stage */ + + wsptr[5*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); + wsptr[5*9] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); + wsptr[5*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); + wsptr[5*8] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); + wsptr[5*2] = (int) (tmp22 + tmp12); + wsptr[5*7] = (int) (tmp22 - tmp12); + wsptr[5*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); + wsptr[5*6] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); + wsptr[5*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); + wsptr[5*5] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 10 rows from work array, store into output array. + * 5-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/10). + */ + wsptr = workspace; + for (ctr = 0; ctr < 10; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp12 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp12 <<= CONST_BITS; + tmp13 = (INT32) wsptr[2]; + tmp14 = (INT32) wsptr[4]; + z1 = MULTIPLY(tmp13 + tmp14, FIX(0.790569415)); /* (c2+c4)/2 */ + z2 = MULTIPLY(tmp13 - tmp14, FIX(0.353553391)); /* (c2-c4)/2 */ + z3 = tmp12 + z2; + tmp10 = z3 + z1; + tmp11 = z3 - z1; + tmp12 -= z2 << 2; + + /* Odd part */ + + z2 = (INT32) wsptr[1]; + z3 = (INT32) wsptr[3]; + + z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ + tmp13 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ + tmp14 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp13, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp14, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 5; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 4x8 output block. + * + * 8-point IDCT in pass 1 (columns), 4-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_4x8 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[4*8]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 4; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[4*0] = dcval; + wsptr[4*1] = dcval; + wsptr[4*2] = dcval; + wsptr[4*3] = dcval; + wsptr[4*4] = dcval; + wsptr[4*5] = dcval; + wsptr[4*6] = dcval; + wsptr[4*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z2, FIX_0_765366865); + tmp3 = z1 - MULTIPLY(z3, FIX_1_847759065); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + z2 <<= CONST_BITS; + z3 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + z2 += ONE << (CONST_BITS-PASS1_BITS-1); + + tmp0 = z2 + z3; + tmp1 = z2 - z3; + + tmp10 = tmp0 + tmp2; + tmp13 = tmp0 - tmp2; + tmp11 = tmp1 + tmp3; + tmp12 = tmp1 - tmp3; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z2 = tmp0 + tmp2; + z3 = tmp1 + tmp3; + + z1 = MULTIPLY(z2 + z3, FIX_1_175875602); /* sqrt(2) * c3 */ + z2 = MULTIPLY(z2, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z3 = MULTIPLY(z3, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + z2 += z1; + z3 += z1; + + z1 = MULTIPLY(tmp0 + tmp3, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + tmp0 += z1 + z2; + tmp3 += z1 + z3; + + z1 = MULTIPLY(tmp1 + tmp2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp1 += z1 + z3; + tmp2 += z1 + z2; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[4*0] = (int) RIGHT_SHIFT(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[4*7] = (int) RIGHT_SHIFT(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[4*1] = (int) RIGHT_SHIFT(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[4*6] = (int) RIGHT_SHIFT(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[4*2] = (int) RIGHT_SHIFT(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[4*5] = (int) RIGHT_SHIFT(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[4*3] = (int) RIGHT_SHIFT(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[4*4] = (int) RIGHT_SHIFT(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process 8 rows from work array, store into output array. + * 4-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/16). + */ + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp2 = (INT32) wsptr[2]; + + tmp10 = (tmp0 + tmp2) << CONST_BITS; + tmp12 = (tmp0 - tmp2) << CONST_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = (INT32) wsptr[1]; + z3 = (INT32) wsptr[3]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + tmp0 = z1 + MULTIPLY(z2, FIX_0_765366865); /* c2-c6 */ + tmp2 = z1 - MULTIPLY(z3, FIX_1_847759065); /* c2+c6 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 4; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 3x6 output block. + * + * 6-point IDCT in pass 1 (columns), 3-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_3x6 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[3*6]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 6-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/12). + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 3; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= CONST_BITS; + /* Add fudge factor here for final descale. */ + tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ + tmp1 = tmp0 + tmp10; + tmp11 = RIGHT_SHIFT(tmp0 - tmp10 - tmp10, CONST_BITS-PASS1_BITS); + tmp10 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ + tmp10 = tmp1 + tmp0; + tmp12 = tmp1 - tmp0; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ + tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); + tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); + tmp1 = (z1 - z2 - z3) << PASS1_BITS; + + /* Final output stage */ + + wsptr[3*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[3*5] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); + wsptr[3*1] = (int) (tmp11 + tmp1); + wsptr[3*4] = (int) (tmp11 - tmp1); + wsptr[3*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[3*3] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); + } + + /* Pass 2: process 6 rows from work array, store into output array. + * 3-point IDCT kernel, cK represents sqrt(2) * cos(K*pi/6). + */ + wsptr = workspace; + for (ctr = 0; ctr < 6; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); + tmp0 <<= CONST_BITS; + tmp2 = (INT32) wsptr[2]; + tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ + tmp10 = tmp0 + tmp12; + tmp2 = tmp0 - tmp12 - tmp12; + + /* Odd part */ + + tmp12 = (INT32) wsptr[1]; + tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += 3; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 2x4 output block. + * + * 4-point IDCT in pass 1 (columns), 2-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_2x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + INT32 * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + INT32 workspace[2*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. + * 4-point IDCT kernel, + * cK represents sqrt(2) * cos(K*pi/16) [refers to 8-point IDCT]. + */ + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = 0; ctr < 2; ctr++, inptr++, quantptr++, wsptr++) { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp10 = (tmp0 + tmp2) << CONST_BITS; + tmp12 = (tmp0 - tmp2) << CONST_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); /* c6 */ + tmp0 = z1 + MULTIPLY(z2, FIX_0_765366865); /* c2-c6 */ + tmp2 = z1 - MULTIPLY(z3, FIX_1_847759065); /* c2+c6 */ + + /* Final output stage */ + + wsptr[2*0] = tmp10 + tmp0; + wsptr[2*3] = tmp10 - tmp0; + wsptr[2*1] = tmp12 + tmp2; + wsptr[2*2] = tmp12 - tmp2; + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + + /* Even part */ + + /* Add fudge factor here for final descale. */ + tmp10 = wsptr[0] + (ONE << (CONST_BITS+2)); + + /* Odd part */ + + tmp0 = wsptr[1]; + + /* Final output stage */ + + outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS+3) + & RANGE_MASK]; + + wsptr += 2; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a 1x2 output block. + * + * 2-point IDCT in pass 1 (columns), 1-point in pass 2 (rows). + */ + +GLOBAL(void) +jpeg_idct_1x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp10; + ISLOW_MULT_TYPE * quantptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* Process 1 column from input, store into output array. */ + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + /* Even part */ + + tmp10 = DEQUANTIZE(coef_block[DCTSIZE*0], quantptr[DCTSIZE*0]); + /* Add fudge factor here for final descale. */ + tmp10 += ONE << 2; + + /* Odd part */ + + tmp0 = DEQUANTIZE(coef_block[DCTSIZE*1], quantptr[DCTSIZE*1]); + + /* Final output stage */ + + output_buf[0][output_col] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, 3) + & RANGE_MASK]; + output_buf[1][output_col] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, 3) + & RANGE_MASK]; +} + +#endif /* IDCT_SCALING_SUPPORTED */ +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/lib/jpeg/src/jidctint.d b/lib/jpeg/src/jidctint.d new file mode 100644 index 0000000..71507da --- /dev/null +++ b/lib/jpeg/src/jidctint.d @@ -0,0 +1,16 @@ +jidctint.o: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jdct.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jdct.h: diff --git a/lib/jpeg/src/jinclude.h b/lib/jpeg/src/jinclude.h new file mode 100644 index 0000000..5ff60fe --- /dev/null +++ b/lib/jpeg/src/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/lib/jpeg/src/jmemmgr.c b/lib/jpeg/src/jmemmgr.c new file mode 100644 index 0000000..b636f1b --- /dev/null +++ b/lib/jpeg/src/jmemmgr.c @@ -0,0 +1,1118 @@ +/* + * jmemmgr.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the JPEG system-independent memory management + * routines. This code is usable across a wide variety of machines; most + * of the system dependencies have been isolated in a separate file. + * The major functions provided here are: + * * pool-based allocation and freeing of memory; + * * policy decisions about how to divide available memory among the + * virtual arrays; + * * control logic for swapping virtual arrays between main memory and + * backing storage. + * The separate system-dependent file provides the actual backing-storage + * access code, and it contains the policy decision about how much total + * main memory to use. + * This file is system-dependent in the sense that some of its functions + * are unnecessary in some systems. For example, if there is enough virtual + * memory so that backing storage will never be used, much of the virtual + * array control logic could be removed. (Of course, if you have that much + * memory then you shouldn't care about a little bit of unused code...) + */ + +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL(void) +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + + +LOCAL(void) +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + +METHODDEF(void *) +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF(void FAR *) +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF(JSAMPARRAY) +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF(JBLOCKARRAY) +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + + +METHODDEF(jvirt_sarray_ptr) +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + + +METHODDEF(jvirt_barray_ptr) +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + + +METHODDEF(void) +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + + +LOCAL(void) +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +LOCAL(void) +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +METHODDEF(JSAMPARRAY) +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +METHODDEF(JBLOCKARRAY) +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF(void) +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF(void) +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL(void) +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Make MAX_ALLOC_CHUNK accessible to other modules */ + mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} diff --git a/lib/jpeg/src/jmemmgr.d b/lib/jpeg/src/jmemmgr.d new file mode 100644 index 0000000..ee4a016 --- /dev/null +++ b/lib/jpeg/src/jmemmgr.d @@ -0,0 +1,16 @@ +jmemmgr.o: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h jmemsys.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jmemsys.h: diff --git a/lib/jpeg/src/jmemnobs.c b/lib/jpeg/src/jmemnobs.c new file mode 100644 index 0000000..6aa1e92 --- /dev/null +++ b/lib/jpeg/src/jmemnobs.c @@ -0,0 +1,109 @@ +/* + * jmemnobs.c + * + * Copyright (C) 1992-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a really simple implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that no backing-store files are needed: all required space + * can be obtained from malloc(). + * This is very portable in the sense that it'll compile on almost anything, + * but you'd better have lots of main memory (or virtual memory) if you want + * to process big images. + * Note that the max_memory_to_use option is ignored by this implementation. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * Here we always say, "we got all you want bud!" + */ + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return max_bytes_needed; +} + + +/* + * Backing store (temporary file) management. + * Since jpeg_mem_available always promised the moon, + * this should never be called and we can just error out. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. Here, there isn't any. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + return 0; /* just set max_memory_to_use to 0 */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} diff --git a/lib/jpeg/src/jmemnobs.d b/lib/jpeg/src/jmemnobs.d new file mode 100644 index 0000000..9f5bff9 --- /dev/null +++ b/lib/jpeg/src/jmemnobs.d @@ -0,0 +1,16 @@ +jmemnobs.o: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h jmemsys.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +jmemsys.h: diff --git a/lib/jpeg/src/jmemsys.h b/lib/jpeg/src/jmemsys.h new file mode 100644 index 0000000..2a87961 --- /dev/null +++ b/lib/jpeg/src/jmemsys.h @@ -0,0 +1,198 @@ +/* + * jmemsys.h + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file defines the interface between the system-independent + * and system-dependent portions of the JPEG memory manager. No other + * modules need include it. (The system-independent portion is jmemmgr.c; + * there are several different versions of the system-dependent portion.) + * + * This file works as-is for the system-dependent memory managers supplied + * in the IJG distribution. You may need to modify it if you write a + * custom memory manager. If system-dependent changes are needed in + * this file, the best method is to #ifdef them based on a configuration + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR + * and USE_MAC_MEMMGR. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN(void *) jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); +EXTERN(void) jpeg_free_small JPP((j_common_ptr cinfo, void * object, + size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN(void FAR *) jpeg_get_large JPP((j_common_ptr cinfo, + size_t sizeofobject)); +EXTERN(void) jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN(long) jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +#ifdef USE_MAC_MEMMGR /* Mac-specific junk */ +#include +#endif /* USE_MAC_MEMMGR */ + + +typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + backing_store_ptr info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else +#ifdef USE_MAC_MEMMGR + /* For the Mac manager (jmemmac.c), we need: */ + short temp_file; /* file reference number to temp file */ + FSSpec tempSpec; /* the FSSpec for the temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +#endif +} backing_store_info; + + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN(void) jpeg_open_backing_store JPP((j_common_ptr cinfo, + backing_store_ptr info, + long total_bytes_needed)); + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN(long) jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_mem_term JPP((j_common_ptr cinfo)); diff --git a/lib/jpeg/src/jmorecfg.h b/lib/jpeg/src/jmorecfg.h new file mode 100644 index 0000000..fe6d87d --- /dev/null +++ b/lib/jpeg/src/jmorecfg.h @@ -0,0 +1,371 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/lib/jpeg/src/jpegint.h b/lib/jpeg/src/jpegint.h new file mode 100644 index 0000000..d891b90 --- /dev/null +++ b/lib/jpeg/src/jpegint.h @@ -0,0 +1,407 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +typedef JMETHOD(void, forward_DCT_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); + +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* It is useful to allow each component to have a separate FDCT method. */ + forward_DCT_ptr forward_DCT[MAX_COMPONENTS]; +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_arith_encoder jIAEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_arith_decoder jIADecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#define jpeg_natural_order7 jZAGTable7 +#define jpeg_natural_order6 jZAGTable6 +#define jpeg_natural_order5 jZAGTable5 +#define jpeg_natural_order4 jZAGTable4 +#define jpeg_natural_order3 jZAGTable3 +#define jpeg_natural_order2 jZAGTable2 +#define jpeg_aritab jAriTab +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_arith_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_arith_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ +extern const int jpeg_natural_order7[]; /* zz to natural order for 7x7 block */ +extern const int jpeg_natural_order6[]; /* zz to natural order for 6x6 block */ +extern const int jpeg_natural_order5[]; /* zz to natural order for 5x5 block */ +extern const int jpeg_natural_order4[]; /* zz to natural order for 4x4 block */ +extern const int jpeg_natural_order3[]; /* zz to natural order for 3x3 block */ +extern const int jpeg_natural_order2[]; /* zz to natural order for 2x2 block */ + +/* Arithmetic coding probability estimation tables in jaricom.c */ +extern const INT32 jpeg_aritab[]; + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/lib/jpeg/src/jpeglib.h b/lib/jpeg/src/jpeglib.h new file mode 100644 index 0000000..2b30fbd --- /dev/null +++ b/lib/jpeg/src/jpeglib.h @@ -0,0 +1,1158 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 80". + */ + +#define JPEG_LIB_VERSION 80 /* Version 8.0 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_h_scaled_size/DCTSIZE) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/lib/jpeg/src/jpegtran.c b/lib/jpeg/src/jpegtran.c new file mode 100644 index 0000000..3798e0f --- /dev/null +++ b/lib/jpeg/src/jpegtran.c @@ -0,0 +1,560 @@ +/* + * jpegtran.c + * + * Copyright (C) 1995-2010, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for JPEG transcoding. + * It is very similar to cjpeg.c, and partly to djpeg.c, but provides + * lossless transcoding between different JPEG file formats. It also + * provides some lossless and sort-of-lossless transformations of JPEG data. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "transupp.h" /* Support routines for jpegtran */ +#include "jversion.h" /* for version message */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ +static char * scaleoption; /* -scale switch */ +static JCOPY_OPTION copyoption; /* -copy switch */ +static jpeg_transform_info transformoption; /* image transformation options */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -copy none Copy no extra markers from source file\n"); + fprintf(stderr, " -copy comments Copy only comment markers (default)\n"); + fprintf(stderr, " -copy all Copy all extra markers\n"); +#ifdef ENTROPY_OPT_SUPPORTED + fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); +#endif +#ifdef C_PROGRESSIVE_SUPPORTED + fprintf(stderr, " -progressive Create progressive JPEG file\n"); +#endif + fprintf(stderr, "Switches for modifying the image:\n"); +#if TRANSFORMS_SUPPORTED + fprintf(stderr, " -crop WxH+X+Y Crop to a rectangular subarea\n"); + fprintf(stderr, " -grayscale Reduce to grayscale (omit color data)\n"); + fprintf(stderr, " -flip [horizontal|vertical] Mirror image (left-right or top-bottom)\n"); + fprintf(stderr, " -perfect Fail if there is non-transformable edge blocks\n"); + fprintf(stderr, " -rotate [90|180|270] Rotate image (degrees clockwise)\n"); +#endif + fprintf(stderr, " -scale M/N Scale output image by fraction M/N, eg, 1/8\n"); +#if TRANSFORMS_SUPPORTED + fprintf(stderr, " -transpose Transpose image\n"); + fprintf(stderr, " -transverse Transverse transpose image\n"); + fprintf(stderr, " -trim Drop non-transformable edge blocks\n"); +#endif + fprintf(stderr, "Switches for advanced users:\n"); + fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n"); + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + fprintf(stderr, "Switches for wizards:\n"); +#ifdef C_ARITH_CODING_SUPPORTED + fprintf(stderr, " -arithmetic Use arithmetic coding\n"); +#endif +#ifdef C_MULTISCAN_FILES_SUPPORTED + fprintf(stderr, " -scans file Create multi-scan JPEG per script file\n"); +#endif + exit(EXIT_FAILURE); +} + + +LOCAL(void) +select_transform (JXFORM_CODE transform) +/* Silly little routine to detect multiple transform options, + * which we can't handle. + */ +{ +#if TRANSFORMS_SUPPORTED + if (transformoption.transform == JXFORM_NONE || + transformoption.transform == transform) { + transformoption.transform = transform; + } else { + fprintf(stderr, "%s: can only do one image transformation at a time\n", + progname); + usage(); + } +#else + fprintf(stderr, "%s: sorry, image transformation was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif +} + + +LOCAL(int) +parse_switches (j_compress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + boolean simple_progressive; + char * scansarg = NULL; /* saves -scans parm if any */ + + /* Set up default JPEG parameters. */ + simple_progressive = FALSE; + outfilename = NULL; + scaleoption = NULL; + copyoption = JCOPYOPT_DEFAULT; + transformoption.transform = JXFORM_NONE; + transformoption.perfect = FALSE; + transformoption.trim = FALSE; + transformoption.force_grayscale = FALSE; + transformoption.crop = FALSE; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "arithmetic", 1)) { + /* Use arithmetic coding. */ +#ifdef C_ARITH_CODING_SUPPORTED + cinfo->arith_code = TRUE; +#else + fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "copy", 2)) { + /* Select which extra markers to copy. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "none", 1)) { + copyoption = JCOPYOPT_NONE; + } else if (keymatch(argv[argn], "comments", 1)) { + copyoption = JCOPYOPT_COMMENTS; + } else if (keymatch(argv[argn], "all", 1)) { + copyoption = JCOPYOPT_ALL; + } else + usage(); + + } else if (keymatch(arg, "crop", 2)) { + /* Perform lossless cropping. */ +#if TRANSFORMS_SUPPORTED + if (++argn >= argc) /* advance to next argument */ + usage(); + if (! jtransform_parse_crop_spec(&transformoption, argv[argn])) { + fprintf(stderr, "%s: bogus -crop argument '%s'\n", + progname, argv[argn]); + exit(EXIT_FAILURE); + } +#else + select_transform(JXFORM_NONE); /* force an error */ +#endif + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's JPEGTRAN, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "flip", 1)) { + /* Mirror left-right or top-bottom. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "horizontal", 1)) + select_transform(JXFORM_FLIP_H); + else if (keymatch(argv[argn], "vertical", 1)) + select_transform(JXFORM_FLIP_V); + else + usage(); + + } else if (keymatch(arg, "grayscale", 1) || keymatch(arg, "greyscale",1)) { + /* Force to grayscale. */ +#if TRANSFORMS_SUPPORTED + transformoption.force_grayscale = TRUE; +#else + select_transform(JXFORM_NONE); /* force an error */ +#endif + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) { + /* Enable entropy parm optimization. */ +#ifdef ENTROPY_OPT_SUPPORTED + cinfo->optimize_coding = TRUE; +#else + fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "perfect", 2)) { + /* Fail if there is any partial edge MCUs that the transform can't + * handle. */ + transformoption.perfect = TRUE; + + } else if (keymatch(arg, "progressive", 2)) { + /* Select simple progressive mode. */ +#ifdef C_PROGRESSIVE_SUPPORTED + simple_progressive = TRUE; + /* We must postpone execution until num_components is known. */ +#else + fprintf(stderr, "%s: sorry, progressive output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "restart", 1)) { + /* Restart interval in MCU rows (or in MCUs with 'b'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (lval < 0 || lval > 65535L) + usage(); + if (ch == 'b' || ch == 'B') { + cinfo->restart_interval = (unsigned int) lval; + cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */ + } else { + cinfo->restart_in_rows = (int) lval; + /* restart_interval will be computed during startup */ + } + + } else if (keymatch(arg, "rotate", 2)) { + /* Rotate 90, 180, or 270 degrees (measured clockwise). */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "90", 2)) + select_transform(JXFORM_ROT_90); + else if (keymatch(argv[argn], "180", 3)) + select_transform(JXFORM_ROT_180); + else if (keymatch(argv[argn], "270", 3)) + select_transform(JXFORM_ROT_270); + else + usage(); + + } else if (keymatch(arg, "scale", 4)) { + /* Scale the output image by a fraction M/N. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + scaleoption = argv[argn]; + /* We must postpone processing until decompression startup. */ + + } else if (keymatch(arg, "scans", 1)) { + /* Set scan script. */ +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (++argn >= argc) /* advance to next argument */ + usage(); + scansarg = argv[argn]; + /* We must postpone reading the file in case -progressive appears. */ +#else + fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "transpose", 1)) { + /* Transpose (across UL-to-LR axis). */ + select_transform(JXFORM_TRANSPOSE); + + } else if (keymatch(arg, "transverse", 6)) { + /* Transverse transpose (across UR-to-LL axis). */ + select_transform(JXFORM_TRANSVERSE); + + } else if (keymatch(arg, "trim", 3)) { + /* Trim off any partial edge MCUs that the transform can't handle. */ + transformoption.trim = TRUE; + + } else { + usage(); /* bogus switch */ + } + } + + /* Post-switch-scanning cleanup */ + + if (for_real) { + +#ifdef C_PROGRESSIVE_SUPPORTED + if (simple_progressive) /* process -progressive; -scans can override */ + jpeg_simple_progression(cinfo); +#endif + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (scansarg != NULL) /* process -scans if it was present */ + if (! read_scan_script(cinfo, scansarg)) + usage(); +#endif + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_decompress_struct srcinfo; + struct jpeg_compress_struct dstinfo; + struct jpeg_error_mgr jsrcerr, jdsterr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + jvirt_barray_ptr * src_coef_arrays; + jvirt_barray_ptr * dst_coef_arrays; + int file_index; + /* We assume all-in-memory processing and can therefore use only a + * single file pointer for sequential input and output operation. + */ + FILE * fp; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "jpegtran"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG decompression object with default error handling. */ + srcinfo.err = jpeg_std_error(&jsrcerr); + jpeg_create_decompress(&srcinfo); + /* Initialize the JPEG compression object with default error handling. */ + dstinfo.err = jpeg_std_error(&jdsterr); + jpeg_create_compress(&dstinfo); + + /* Now safe to enable signal catcher. + * Note: we assume only the decompression object will have virtual arrays. + */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &srcinfo); +#endif + + /* Scan command line to find file names. + * It is convenient to use just one switch-parsing routine, but the switch + * values read here are mostly ignored; we will rescan the switches after + * opening the input file. Also note that most of the switches affect the + * destination JPEG object, so we parse into that and then copy over what + * needs to affects the source too. + */ + + file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE); + jsrcerr.trace_level = jdsterr.trace_level; + srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use; + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s for reading\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + fp = read_stdin(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &dstinfo, &progress); +#endif + + /* Specify data source for decompression */ + jpeg_stdio_src(&srcinfo, fp); + + /* Enable saving of extra markers that we want to copy */ + jcopy_markers_setup(&srcinfo, copyoption); + + /* Read file header */ + (void) jpeg_read_header(&srcinfo, TRUE); + + /* Adjust default decompression parameters */ + if (scaleoption != NULL) + if (sscanf(scaleoption, "%d/%d", + &srcinfo.scale_num, &srcinfo.scale_denom) < 1) + usage(); + + /* Any space needed by a transform option must be requested before + * jpeg_read_coefficients so that memory allocation will be done right. + */ +#if TRANSFORMS_SUPPORTED + /* Fail right away if -perfect is given and transformation is not perfect. + */ + if (!jtransform_request_workspace(&srcinfo, &transformoption)) { + fprintf(stderr, "%s: transformation is not perfect\n", progname); + exit(EXIT_FAILURE); + } +#endif + + /* Read source file as DCT coefficients */ + src_coef_arrays = jpeg_read_coefficients(&srcinfo); + + /* Initialize destination compression parameters from source values */ + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + /* Adjust destination parameters if required by transform options; + * also find out which set of coefficient arrays will hold the output. + */ +#if TRANSFORMS_SUPPORTED + dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); +#else + dst_coef_arrays = src_coef_arrays; +#endif + + /* Close input file, if we opened it. + * Note: we assume that jpeg_read_coefficients consumed all input + * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will + * only consume more while (! cinfo->inputctl->eoi_reached). + * We cannot call jpeg_finish_decompress here since we still need the + * virtual arrays allocated from the source object for processing. + */ + if (fp != stdin) + fclose(fp); + + /* Open the output file. */ + if (outfilename != NULL) { + if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s for writing\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + fp = write_stdout(); + } + + /* Adjust default compression parameters by re-parsing the options */ + file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); + + /* Specify data destination for compression */ + jpeg_stdio_dest(&dstinfo, fp); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + /* Copy to the output file any extra markers that we want to preserve */ + jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); + + /* Execute image transformation, if any */ +#if TRANSFORMS_SUPPORTED + jtransform_execute_transformation(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); +#endif + + /* Finish compression and release memory */ + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); + (void) jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + /* Close output file, if we opened it */ + if (fp != stdout) + fclose(fp); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &dstinfo); +#endif + + /* All done. */ + exit(jsrcerr.num_warnings + jdsterr.num_warnings ?EXIT_WARNING:EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/lib/jpeg/src/jpegtran.d b/lib/jpeg/src/jpegtran.d new file mode 100644 index 0000000..bc05518 --- /dev/null +++ b/lib/jpeg/src/jpegtran.d @@ -0,0 +1,20 @@ +jpegtran.o: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h transupp.h jversion.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: + +transupp.h: + +jversion.h: diff --git a/lib/jpeg/src/jquant1.c b/lib/jpeg/src/jquant1.c new file mode 100644 index 0000000..aaa34a1 --- /dev/null +++ b/lib/jpeg/src/jquant1.c @@ -0,0 +1,856 @@ +/* + * jquant1.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 1-pass color quantization (color mapping) routines. + * These routines provide mapping to a fixed color map using equally spaced + * color values. Optional Floyd-Steinberg or ordered dithering is available. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef QUANT_1PASS_SUPPORTED + + +/* + * The main purpose of 1-pass quantization is to provide a fast, if not very + * high quality, colormapped output capability. A 2-pass quantizer usually + * gives better visual quality; however, for quantized grayscale output this + * quantizer is perfectly adequate. Dithering is highly recommended with this + * quantizer, though you can turn it off if you really want to. + * + * In 1-pass quantization the colormap must be chosen in advance of seeing the + * image. We use a map consisting of all combinations of Ncolors[i] color + * values for the i'th component. The Ncolors[] values are chosen so that + * their product, the total number of colors, is no more than that requested. + * (In most cases, the product will be somewhat less.) + * + * Since the colormap is orthogonal, the representative value for each color + * component can be determined without considering the other components; + * then these indexes can be combined into a colormap index by a standard + * N-dimensional-array-subscript calculation. Most of the arithmetic involved + * can be precalculated and stored in the lookup table colorindex[]. + * colorindex[i][j] maps pixel value j in component i to the nearest + * representative value (grid plane) for that component; this index is + * multiplied by the array stride for component i, so that the + * index of the colormap entry closest to a given pixel value is just + * sum( colorindex[component-number][pixel-component-value] ) + * Aside from being fast, this scheme allows for variable spacing between + * representative values with no additional lookup cost. + * + * If gamma correction has been applied in color conversion, it might be wise + * to adjust the color grid spacing so that the representative colors are + * equidistant in linear space. At this writing, gamma correction is not + * implemented by jdcolor, so nothing is done here. + */ + + +/* Declarations for ordered dithering. + * + * We use a standard 16x16 ordered dither array. The basic concept of ordered + * dithering is described in many references, for instance Dale Schumacher's + * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). + * In place of Schumacher's comparisons against a "threshold" value, we add a + * "dither" value to the input pixel and then round the result to the nearest + * output value. The dither value is equivalent to (0.5 - threshold) times + * the distance between output values. For ordered dithering, we assume that + * the output colors are equally spaced; if not, results will probably be + * worse, since the dither may be too much or too little at a given point. + * + * The normal calculation would be to form pixel value + dither, range-limit + * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + * We can skip the separate range-limiting step by extending the colorindex + * table in both directions. + */ + +#define ODITHER_SIZE 16 /* dimension of dither matrix */ +/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ +#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE) /* # cells in matrix */ +#define ODITHER_MASK (ODITHER_SIZE-1) /* mask for wrapping around counters */ + +typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE]; +typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE]; + +static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { + /* Bayer's order-4 dither array. Generated by the code given in + * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. + * The values in this array must range from 0 to ODITHER_CELLS-1. + */ + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + + +/* Declarations for Floyd-Steinberg dithering. + * + * Errors are accumulated into the array fserrors[], at a resolution of + * 1/16th of a pixel count. The error at a given pixel is propagated + * to its not-yet-processed neighbors using the standard F-S fractions, + * ... (here) 7/16 + * 3/16 5/16 1/16 + * We work left-to-right on even rows, right-to-left on odd rows. + * + * We can get away with a single array (holding one row's worth of errors) + * by using it to store the current row's errors at pixel columns not yet + * processed, but the next row's errors at columns already processed. We + * need only a few extra variables to hold the errors immediately around the + * current column. (If we are lucky, those variables are in registers, but + * even if not, they're probably cheaper to access than array elements are.) + * + * The fserrors[] array is indexed [component#][position]. + * We provide (#columns + 2) entries per component; the extra entry at each + * end saves us from special-casing the first and last pixels. + * + * Note: on a wide image, we might not have enough room in a PC's near data + * segment to hold the error array; so it is allocated with alloc_large. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef INT16 FSERROR; /* 16 bits should be enough */ +typedef int LOCFSERROR; /* use 'int' for calculation temps */ +#else +typedef INT32 FSERROR; /* may need more than 16 bits */ +typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ +#endif + +typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */ + + +/* Private subobject */ + +#define MAX_Q_COMPS 4 /* max components I can handle */ + +typedef struct { + struct jpeg_color_quantizer pub; /* public fields */ + + /* Initially allocated colormap is saved here */ + JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + int sv_actual; /* number of entries in use */ + + JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + /* colorindex[i][j] = index of color closest to pixel value j in component i, + * premultiplied as described above. Since colormap indexes must fit into + * JSAMPLEs, the entries of this array will too. + */ + boolean is_padded; /* is the colorindex padded for odither? */ + + int Ncolors[MAX_Q_COMPS]; /* # of values alloced to each component */ + + /* Variables for ordered dithering */ + int row_index; /* cur row's vertical index in dither matrix */ + ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */ + + /* Variables for Floyd-Steinberg dithering */ + FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */ + boolean on_odd_row; /* flag to remember which row we are on */ +} my_cquantizer; + +typedef my_cquantizer * my_cquantize_ptr; + + +/* + * Policy-making subroutines for create_colormap and create_colorindex. + * These routines determine the colormap to be used. The rest of the module + * only assumes that the colormap is orthogonal. + * + * * select_ncolors decides how to divvy up the available colors + * among the components. + * * output_value defines the set of representative values for a component. + * * largest_input_value defines the mapping from input values to + * representative values for a component. + * Note that the latter two routines may impose different policies for + * different components, though this is not currently done. + */ + + +LOCAL(int) +select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) +/* Determine allocation of desired colors to components, */ +/* and fill in Ncolors[] array to indicate choice. */ +/* Return value is total number of colors (product of Ncolors[] values). */ +{ + int nc = cinfo->out_color_components; /* number of color components */ + int max_colors = cinfo->desired_number_of_colors; + int total_colors, iroot, i, j; + boolean changed; + long temp; + static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE }; + + /* We can allocate at least the nc'th root of max_colors per component. */ + /* Compute floor(nc'th root of max_colors). */ + iroot = 1; + do { + iroot++; + temp = iroot; /* set temp = iroot ** nc */ + for (i = 1; i < nc; i++) + temp *= iroot; + } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ + iroot--; /* now iroot = floor(root) */ + + /* Must have at least 2 color values per component */ + if (iroot < 2) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp); + + /* Initialize to iroot color values for each component */ + total_colors = 1; + for (i = 0; i < nc; i++) { + Ncolors[i] = iroot; + total_colors *= iroot; + } + /* We may be able to increment the count for one or more components without + * exceeding max_colors, though we know not all can be incremented. + * Sometimes, the first component can be incremented more than once! + * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) + * In RGB colorspace, try to increment G first, then R, then B. + */ + do { + changed = FALSE; + for (i = 0; i < nc; i++) { + j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i); + /* calculate new total_colors if Ncolors[j] is incremented */ + temp = total_colors / Ncolors[j]; + temp *= Ncolors[j]+1; /* done in long arith to avoid oflo */ + if (temp > (long) max_colors) + break; /* won't fit, done with this pass */ + Ncolors[j]++; /* OK, apply the increment */ + total_colors = (int) temp; + changed = TRUE; + } + } while (changed); + + return total_colors; +} + + +LOCAL(int) +output_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return j'th output value, where j will range from 0 to maxj */ +/* The output values must fall in 0..MAXJSAMPLE in increasing order */ +{ + /* We always provide values 0 and MAXJSAMPLE for each component; + * any additional values are equally spaced between these limits. + * (Forcing the upper and lower values to the limits ensures that + * dithering can't produce a color outside the selected gamut.) + */ + return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); +} + + +LOCAL(int) +largest_input_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return largest input value that should map to j'th output value */ +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ +{ + /* Breakpoints are halfway between values returned by output_value */ + return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); +} + + +/* + * Create the colormap. + */ + +LOCAL(void) +create_colormap (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colormap; /* Created colormap */ + int total_colors; /* Number of distinct output colors */ + int i,j,k, nci, blksize, blkdist, ptr, val; + + /* Select number of colors for each component */ + total_colors = select_ncolors(cinfo, cquantize->Ncolors); + + /* Report selected color counts */ + if (cinfo->out_color_components == 3) + TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS, + total_colors, cquantize->Ncolors[0], + cquantize->Ncolors[1], cquantize->Ncolors[2]); + else + TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors); + + /* Allocate and fill in the colormap. */ + /* The colors are ordered in the map in standard row-major order, */ + /* i.e. rightmost (highest-indexed) color changes most rapidly. */ + + colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + /* blkdist is distance between groups of identical entries for a component */ + blkdist = total_colors; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colormap entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blkdist / nci; + for (j = 0; j < nci; j++) { + /* Compute j'th output value (out of nci) for component */ + val = output_value(cinfo, i, j, nci-1); + /* Fill in all colormap entries that have this value of this component */ + for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { + /* fill in blksize entries beginning at ptr */ + for (k = 0; k < blksize; k++) + colormap[i][ptr+k] = (JSAMPLE) val; + } + } + blkdist = blksize; /* blksize of this color is blkdist of next */ + } + + /* Save the colormap in private storage, + * where it will survive color quantization mode changes. + */ + cquantize->sv_colormap = colormap; + cquantize->sv_actual = total_colors; +} + + +/* + * Create the color index table. + */ + +LOCAL(void) +create_colorindex (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPROW indexptr; + int i,j,k, nci, blksize, val, pad; + + /* For ordered dither, we pad the color index tables by MAXJSAMPLE in + * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + * This is not necessary in the other dithering modes. However, we + * flag whether it was done in case user changes dithering mode. + */ + if (cinfo->dither_mode == JDITHER_ORDERED) { + pad = MAXJSAMPLE*2; + cquantize->is_padded = TRUE; + } else { + pad = 0; + cquantize->is_padded = FALSE; + } + + cquantize->colorindex = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1 + pad), + (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + blksize = cquantize->sv_actual; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colorindex entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blksize / nci; + + /* adjust colorindex pointers to provide padding at negative indexes. */ + if (pad) + cquantize->colorindex[i] += MAXJSAMPLE; + + /* in loop, val = index of current output value, */ + /* and k = largest j that maps to current val */ + indexptr = cquantize->colorindex[i]; + val = 0; + k = largest_input_value(cinfo, i, 0, nci-1); + for (j = 0; j <= MAXJSAMPLE; j++) { + while (j > k) /* advance val if past boundary */ + k = largest_input_value(cinfo, i, ++val, nci-1); + /* premultiply so that no multiplication needed in main processing */ + indexptr[j] = (JSAMPLE) (val * blksize); + } + /* Pad at both ends if necessary */ + if (pad) + for (j = 1; j <= MAXJSAMPLE; j++) { + indexptr[-j] = indexptr[0]; + indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE]; + } + } +} + + +/* + * Create an ordered-dither array for a component having ncolors + * distinct output values. + */ + +LOCAL(ODITHER_MATRIX_PTR) +make_odither_array (j_decompress_ptr cinfo, int ncolors) +{ + ODITHER_MATRIX_PTR odither; + int j,k; + INT32 num,den; + + odither = (ODITHER_MATRIX_PTR) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ODITHER_MATRIX)); + /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + * Hence the dither value for the matrix cell with fill order f + * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + * On 16-bit-int machine, be careful to avoid overflow. + */ + den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1)); + for (j = 0; j < ODITHER_SIZE; j++) { + for (k = 0; k < ODITHER_SIZE; k++) { + num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k]))) + * MAXJSAMPLE; + /* Ensure round towards zero despite C's lack of consistency + * about rounding negative values in integer division... + */ + odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den); + } + } + return odither; +} + + +/* + * Create the ordered-dither tables. + * Components having the same number of representative colors may + * share a dither table. + */ + +LOCAL(void) +create_odither_tables (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + ODITHER_MATRIX_PTR odither; + int i, j, nci; + + for (i = 0; i < cinfo->out_color_components; i++) { + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + odither = NULL; /* search for matching prior component */ + for (j = 0; j < i; j++) { + if (nci == cquantize->Ncolors[j]) { + odither = cquantize->odither[j]; + break; + } + } + if (odither == NULL) /* need a new table? */ + odither = make_odither_array(cinfo, nci); + cquantize->odither[i] = odither; + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colorindex = cquantize->colorindex; + register int pixcode, ci; + register JSAMPROW ptrin, ptrout; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + register int nc = cinfo->out_color_components; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = 0; + for (ci = 0; ci < nc; ci++) { + pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]); + } + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW ptrin, ptrout; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]); + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + int * dither; /* points to active row of dither matrix */ + int row_index, col_index; /* current indexes into dither matrix */ + int nc = cinfo->out_color_components; + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + row_index = cquantize->row_index; + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + colorindex_ci = cquantize->colorindex[ci]; + dither = cquantize->odither[ci][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + * select output value, accumulate into output code for this pixel. + * Range-limiting need not be done explicitly, as we have extended + * the colorindex table to produce the right answers for out-of-range + * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + * required amount of padding. + */ + *output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]]; + input_ptr += nc; + output_ptr++; + col_index = (col_index + 1) & ODITHER_MASK; + } + } + /* Advance row index for next row */ + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int * dither0; /* points to active row of dither matrix */ + int * dither1; + int * dither2; + int row_index, col_index; /* current indexes into dither matrix */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + row_index = cquantize->row_index; + input_ptr = input_buf[row]; + output_ptr = output_buf[row]; + dither0 = cquantize->odither[0][row_index]; + dither1 = cquantize->odither[1][row_index]; + dither2 = cquantize->odither[2][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) + + dither0[col_index]]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) + + dither1[col_index]]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) + + dither2[col_index]]); + *output_ptr++ = (JSAMPLE) pixcode; + col_index = (col_index + 1) & ODITHER_MASK; + } + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register LOCFSERROR cur; /* current error or pixel value */ + LOCFSERROR belowerr; /* error for pixel below cur */ + LOCFSERROR bpreverr; /* error for below/prev col */ + LOCFSERROR bnexterr; /* error for below/next col */ + LOCFSERROR delta; + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + JSAMPROW colormap_ci; + int pixcode; + int nc = cinfo->out_color_components; + int dir; /* 1 for left-to-right, -1 for right-to-left */ + int dirnc; /* dir * nc */ + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + input_ptr += (width-1) * nc; /* so point to rightmost pixel */ + output_ptr += width-1; + dir = -1; + dirnc = -nc; + errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */ + } else { + /* work left to right in this row */ + dir = 1; + dirnc = nc; + errorptr = cquantize->fserrors[ci]; /* => entry before first column */ + } + colorindex_ci = cquantize->colorindex[ci]; + colormap_ci = cquantize->sv_colormap[ci]; + /* Preset error values: no error propagated to first pixel from left */ + cur = 0; + /* and no error propagated to row below yet */ + belowerr = bpreverr = 0; + + for (col = width; col > 0; col--) { + /* cur holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE; this sets the required size + * of the range_limit array. + */ + cur += GETJSAMPLE(*input_ptr); + cur = GETJSAMPLE(range_limit[cur]); + /* Select output value, accumulate into output code for this pixel */ + pixcode = GETJSAMPLE(colorindex_ci[cur]); + *output_ptr += (JSAMPLE) pixcode; + /* Compute actual representation error at this pixel */ + /* Note: we can do this even though we don't have the final */ + /* pixel code, because the colormap is orthogonal. */ + cur -= GETJSAMPLE(colormap_ci[pixcode]); + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + bnexterr = cur; + delta = cur * 2; + cur += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr + cur); + cur += delta; /* form error * 5 */ + bpreverr = belowerr + cur; + belowerr = bnexterr; + cur += delta; /* form error * 7 */ + /* At this point cur contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + input_ptr += dirnc; /* advance input ptr to next column */ + output_ptr += dir; /* advance output ptr to next column */ + errorptr += dir; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error value into the + * final fserrors[] entry. Note we need not unload belowerr because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */ + } + cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE); + } +} + + +/* + * Allocate workspace for Floyd-Steinberg errors. + */ + +LOCAL(void) +alloc_fs_workspace (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) { + cquantize->fserrors[i] = (FSERRPTR) + (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + } +} + + +/* + * Initialize for one-pass color quantization. + */ + +METHODDEF(void) +start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + /* Install my colormap. */ + cinfo->colormap = cquantize->sv_colormap; + cinfo->actual_number_of_colors = cquantize->sv_actual; + + /* Initialize for desired dithering mode. */ + switch (cinfo->dither_mode) { + case JDITHER_NONE: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = color_quantize3; + else + cquantize->pub.color_quantize = color_quantize; + break; + case JDITHER_ORDERED: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = quantize3_ord_dither; + else + cquantize->pub.color_quantize = quantize_ord_dither; + cquantize->row_index = 0; /* initialize state for ordered dither */ + /* If user changed to ordered dither from another mode, + * we must recreate the color index table with padding. + * This will cost extra space, but probably isn't very likely. + */ + if (! cquantize->is_padded) + create_colorindex(cinfo); + /* Create ordered-dither tables if we didn't already. */ + if (cquantize->odither[0] == NULL) + create_odither_tables(cinfo); + break; + case JDITHER_FS: + cquantize->pub.color_quantize = quantize_fs_dither; + cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ + /* Allocate Floyd-Steinberg workspace if didn't already. */ + if (cquantize->fserrors[0] == NULL) + alloc_fs_workspace(cinfo); + /* Initialize the propagated errors to zero. */ + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) + jzero_far((void FAR *) cquantize->fserrors[i], arraysize); + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } +} + + +/* + * Finish up at the end of the pass. + */ + +METHODDEF(void) +finish_pass_1_quant (j_decompress_ptr cinfo) +{ + /* no work in 1-pass case */ +} + + +/* + * Switch to a new external colormap between output passes. + * Shouldn't get to this module! + */ + +METHODDEF(void) +new_color_map_1_quant (j_decompress_ptr cinfo) +{ + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + + +/* + * Module initialization routine for 1-pass color quantization. + */ + +GLOBAL(void) +jinit_1pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_1_quant; + cquantize->pub.finish_pass = finish_pass_1_quant; + cquantize->pub.new_color_map = new_color_map_1_quant; + cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */ + cquantize->odither[0] = NULL; /* Also flag odither arrays not allocated */ + + /* Make sure my internal arrays won't overflow */ + if (cinfo->out_color_components > MAX_Q_COMPS) + ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1); + + /* Create the colormap and color index table. */ + create_colormap(cinfo); + create_colorindex(cinfo); + + /* Allocate Floyd-Steinberg workspace now if requested. + * We do this now since it is FAR storage and may affect the memory + * manager's space calculations. If the user changes to FS dither + * mode in a later pass, we will allocate the space then, and will + * possibly overrun the max_memory_to_use setting. + */ + if (cinfo->dither_mode == JDITHER_FS) + alloc_fs_workspace(cinfo); +} + +#endif /* QUANT_1PASS_SUPPORTED */ diff --git a/lib/jpeg/src/jquant1.d b/lib/jpeg/src/jquant1.d new file mode 100644 index 0000000..e81af17 --- /dev/null +++ b/lib/jpeg/src/jquant1.d @@ -0,0 +1,14 @@ +jquant1.o: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jquant2.c b/lib/jpeg/src/jquant2.c new file mode 100644 index 0000000..87a3920 --- /dev/null +++ b/lib/jpeg/src/jquant2.c @@ -0,0 +1,1310 @@ +/* + * jquant2.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 2-pass color quantization (color mapping) routines. + * These routines provide selection of a custom color map for an image, + * followed by mapping of the image to that color map, with optional + * Floyd-Steinberg dithering. + * It is also possible to use just the second pass to map to an arbitrary + * externally-given color map. + * + * Note: ordered dithering is not supported, since there isn't any fast + * way to compute intercolor distances; it's unclear that ordered dither's + * fundamental assumptions even hold with an irregularly spaced color map. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef QUANT_2PASS_SUPPORTED + + +/* + * This module implements the well-known Heckbert paradigm for color + * quantization. Most of the ideas used here can be traced back to + * Heckbert's seminal paper + * Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", + * Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. + * + * In the first pass over the image, we accumulate a histogram showing the + * usage count of each possible color. To keep the histogram to a reasonable + * size, we reduce the precision of the input; typical practice is to retain + * 5 or 6 bits per color, so that 8 or 4 different input values are counted + * in the same histogram cell. + * + * Next, the color-selection step begins with a box representing the whole + * color space, and repeatedly splits the "largest" remaining box until we + * have as many boxes as desired colors. Then the mean color in each + * remaining box becomes one of the possible output colors. + * + * The second pass over the image maps each input pixel to the closest output + * color (optionally after applying a Floyd-Steinberg dithering correction). + * This mapping is logically trivial, but making it go fast enough requires + * considerable care. + * + * Heckbert-style quantizers vary a good deal in their policies for choosing + * the "largest" box and deciding where to cut it. The particular policies + * used here have proved out well in experimental comparisons, but better ones + * may yet be found. + * + * In earlier versions of the IJG code, this module quantized in YCbCr color + * space, processing the raw upsampled data without a color conversion step. + * This allowed the color conversion math to be done only once per colormap + * entry, not once per pixel. However, that optimization precluded other + * useful optimizations (such as merging color conversion with upsampling) + * and it also interfered with desired capabilities such as quantizing to an + * externally-supplied colormap. We have therefore abandoned that approach. + * The present code works in the post-conversion color space, typically RGB. + * + * To improve the visual quality of the results, we actually work in scaled + * RGB space, giving G distances more weight than R, and R in turn more than + * B. To do everything in integer math, we must use integer scale factors. + * The 2/3/1 scale factors used here correspond loosely to the relative + * weights of the colors in the NTSC grayscale equation. + * If you want to use this code to quantize a non-RGB color space, you'll + * probably need to change these scale factors. + */ + +#define R_SCALE 2 /* scale R distances by this much */ +#define G_SCALE 3 /* scale G distances by this much */ +#define B_SCALE 1 /* and B by this much */ + +/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined + * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B + * and B,G,R orders. If you define some other weird order in jmorecfg.h, + * you'll get compile errors until you extend this logic. In that case + * you'll probably want to tweak the histogram sizes too. + */ + +#if RGB_RED == 0 +#define C0_SCALE R_SCALE +#endif +#if RGB_BLUE == 0 +#define C0_SCALE B_SCALE +#endif +#if RGB_GREEN == 1 +#define C1_SCALE G_SCALE +#endif +#if RGB_RED == 2 +#define C2_SCALE R_SCALE +#endif +#if RGB_BLUE == 2 +#define C2_SCALE B_SCALE +#endif + + +/* + * First we have the histogram data structure and routines for creating it. + * + * The number of bits of precision can be adjusted by changing these symbols. + * We recommend keeping 6 bits for G and 5 each for R and B. + * If you have plenty of memory and cycles, 6 bits all around gives marginally + * better results; if you are short of memory, 5 bits all around will save + * some space but degrade the results. + * To maintain a fully accurate histogram, we'd need to allocate a "long" + * (preferably unsigned long) for each cell. In practice this is overkill; + * we can get by with 16 bits per cell. Few of the cell counts will overflow, + * and clamping those that do overflow to the maximum value will give close- + * enough results. This reduces the recommended histogram size from 256Kb + * to 128Kb, which is a useful savings on PC-class machines. + * (In the second pass the histogram space is re-used for pixel mapping data; + * in that capacity, each cell must be able to store zero to the number of + * desired colors. 16 bits/cell is plenty for that too.) + * Since the JPEG code is intended to run in small memory model on 80x86 + * machines, we can't just allocate the histogram in one chunk. Instead + * of a true 3-D array, we use a row of pointers to 2-D arrays. Each + * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and + * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that + * on 80x86 machines, the pointer row is in near memory but the actual + * arrays are in far memory (same arrangement as we use for image arrays). + */ + +#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ + +/* These will do the right thing for either R,G,B or B,G,R color order, + * but you may not like the results for other color orders. + */ +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ + +/* Number of elements along histogram axes. */ +#define HIST_C0_ELEMS (1<cquantize; + register JSAMPROW ptr; + register histptr histp; + register hist3d histogram = cquantize->histogram; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptr = input_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the histogram */ + histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT] + [GETJSAMPLE(ptr[1]) >> C1_SHIFT] + [GETJSAMPLE(ptr[2]) >> C2_SHIFT]; + /* increment, check for overflow and undo increment if so. */ + if (++(*histp) <= 0) + (*histp)--; + ptr += 3; + } + } +} + + +/* + * Next we have the really interesting routines: selection of a colormap + * given the completed histogram. + * These routines work with a list of "boxes", each representing a rectangular + * subset of the input color space (to histogram precision). + */ + +typedef struct { + /* The bounds of the box (inclusive); expressed as histogram indexes */ + int c0min, c0max; + int c1min, c1max; + int c2min, c2max; + /* The volume (actually 2-norm) of the box */ + INT32 volume; + /* The number of nonzero histogram cells within this box */ + long colorcount; +} box; + +typedef box * boxptr; + + +LOCAL(boxptr) +find_biggest_color_pop (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest color population */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register long maxc = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->colorcount > maxc && boxp->volume > 0) { + which = boxp; + maxc = boxp->colorcount; + } + } + return which; +} + + +LOCAL(boxptr) +find_biggest_volume (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest (scaled) volume */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register INT32 maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->volume > maxv) { + which = boxp; + maxv = boxp->volume; + } + } + return which; +} + + +LOCAL(void) +update_box (j_decompress_ptr cinfo, boxptr boxp) +/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ +/* and recompute its volume and population */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + INT32 dist0,dist1,dist2; + long ccount; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + if (c0max > c0min) + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0min = c0min = c0; + goto have_c0min; + } + } + have_c0min: + if (c0max > c0min) + for (c0 = c0max; c0 >= c0min; c0--) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0max = c0max = c0; + goto have_c0max; + } + } + have_c0max: + if (c1max > c1min) + for (c1 = c1min; c1 <= c1max; c1++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1min = c1min = c1; + goto have_c1min; + } + } + have_c1min: + if (c1max > c1min) + for (c1 = c1max; c1 >= c1min; c1--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1max = c1max = c1; + goto have_c1max; + } + } + have_c1max: + if (c2max > c2min) + for (c2 = c2min; c2 <= c2max; c2++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2min = c2min = c2; + goto have_c2min; + } + } + have_c2min: + if (c2max > c2min) + for (c2 = c2max; c2 >= c2min; c2--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2max = c2max = c2; + goto have_c2max; + } + } + have_c2max: + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++, histp++) + if (*histp != 0) { + ccount++; + } + } + boxp->colorcount = ccount; +} + + +LOCAL(int) +median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, + int desired_colors) +/* Repeatedly select and split the largest box until we have enough boxes */ +{ + int n,lb; + int c0,c1,c2,cmax; + register boxptr b1,b2; + + while (numboxes < desired_colors) { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + if (numboxes*2 <= desired_colors) { + b1 = find_biggest_color_pop(boxlist, numboxes); + } else { + b1 = find_biggest_volume(boxlist, numboxes); + } + if (b1 == NULL) /* no splittable boxes left! */ + break; + b2 = &boxlist[numboxes]; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max; + b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min; + /* Choose which axis to split the box on. + * Current algorithm: longest scaled axis. + * See notes in update_box about scaling distances. + */ + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + /* We want to break any ties in favor of green, then red, blue last. + * This code does the right thing for R,G,B or B,G,R color orders only. + */ +#if RGB_RED == 0 + cmax = c1; n = 1; + if (c0 > cmax) { cmax = c0; n = 0; } + if (c2 > cmax) { n = 2; } +#else + cmax = c1; n = 1; + if (c2 > cmax) { cmax = c2; n = 2; } + if (c0 > cmax) { n = 0; } +#endif + /* Choose split point along selected axis, and update box bounds. + * Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + switch (n) { + case 0: + lb = (b1->c0max + b1->c0min) / 2; + b1->c0max = lb; + b2->c0min = lb+1; + break; + case 1: + lb = (b1->c1max + b1->c1min) / 2; + b1->c1max = lb; + b2->c1min = lb+1; + break; + case 2: + lb = (b1->c2max + b1->c2min) / 2; + b1->c2max = lb; + b2->c2min = lb+1; + break; + } + /* Update stats for boxes */ + update_box(cinfo, b1); + update_box(cinfo, b2); + numboxes++; + } + return numboxes; +} + + +LOCAL(void) +compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) +/* Compute representative color for a box, put it in colormap[icolor] */ +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + long count; + long total = 0; + long c0total = 0; + long c1total = 0; + long c2total = 0; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) { + if ((count = *histp++) != 0) { + total += count; + c0total += ((c0 << C0_SHIFT) + ((1<>1)) * count; + c1total += ((c1 << C1_SHIFT) + ((1<>1)) * count; + c2total += ((c2 << C2_SHIFT) + ((1<>1)) * count; + } + } + } + + cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total); + cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total); + cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total); +} + + +LOCAL(void) +select_colors (j_decompress_ptr cinfo, int desired_colors) +/* Master routine for color selection */ +{ + boxptr boxlist; + int numboxes; + int i; + + /* Allocate workspace for box list */ + boxlist = (boxptr) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box)); + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].c0min = 0; + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c1min = 0; + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c2min = 0; + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + /* Shrink it to actually-used volume and set its statistics */ + update_box(cinfo, & boxlist[0]); + /* Perform median-cut to produce final box list */ + numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors); + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color(cinfo, & boxlist[i], i); + cinfo->actual_number_of_colors = numboxes; + TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes); +} + + +/* + * These routines are concerned with the time-critical task of mapping input + * colors to the nearest color in the selected colormap. + * + * We re-use the histogram space as an "inverse color map", essentially a + * cache for the results of nearest-color searches. All colors within a + * histogram cell will be mapped to the same colormap entry, namely the one + * closest to the cell's center. This may not be quite the closest entry to + * the actual input color, but it's almost as good. A zero in the cache + * indicates we haven't found the nearest color for that cell yet; the array + * is cleared to zeroes before starting the mapping pass. When we find the + * nearest color for a cell, its colormap index plus one is recorded in the + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + * when they need to use an unfilled entry in the cache. + * + * Our method of efficiently finding nearest colors is based on the "locally + * sorted search" idea described by Heckbert and on the incremental distance + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + * the distances from a given colormap entry to each cell of the histogram can + * be computed quickly using an incremental method: the differences between + * distances to adjacent cells themselves differ by a constant. This allows a + * fairly fast implementation of the "brute force" approach of computing the + * distance from every colormap entry to every histogram cell. Unfortunately, + * it needs a work array to hold the best-distance-so-far for each histogram + * cell (because the inner loop has to be over cells, not colormap entries). + * The work array elements have to be INT32s, so the work array would need + * 256Kb at our recommended precision. This is not feasible in DOS machines. + * + * To get around these problems, we apply Thomas' method to compute the + * nearest colors for only the cells within a small subbox of the histogram. + * The work array need be only as big as the subbox, so the memory usage + * problem is solved. Furthermore, we need not fill subboxes that are never + * referenced in pass2; many images use only part of the color gamut, so a + * fair amount of work is saved. An additional advantage of this + * approach is that we can apply Heckbert's locality criterion to quickly + * eliminate colormap entries that are far away from the subbox; typically + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + * and we need not compute their distances to individual cells in the subbox. + * The speed of this approach is heavily influenced by the subbox size: too + * small means too much overhead, too big loses because Heckbert's criterion + * can't eliminate as many colormap entries. Empirically the best subbox + * size seems to be about 1/512th of the histogram (1/8th in each direction). + * + * Thomas' article also describes a refined method which is asymptotically + * faster than the brute-force method, but it is also far more complex and + * cannot efficiently be applied to small subboxes. It is therefore not + * useful for programs intended to be portable to DOS machines. On machines + * with plenty of memory, filling the whole histogram in one shot with Thomas' + * refined method might be faster than the present code --- but then again, + * it might not be any faster, and it's certainly more complicated. + */ + + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ +#define BOX_C0_LOG (HIST_C0_BITS-3) +#define BOX_C1_LOG (HIST_C1_BITS-3) +#define BOX_C2_LOG (HIST_C2_BITS-3) + +#define BOX_C0_ELEMS (1<actual_number_of_colors; + int maxc0, maxc1, maxc2; + int centerc0, centerc1, centerc2; + int i, x, ncolors; + INT32 minmaxdist, min_dist, max_dist, tdist; + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + centerc0 = (minc0 + maxc0) >> 1; + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + centerc1 = (minc1 + maxc1) >> 1; + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + centerc2 = (minc2 + maxc2) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) { + /* We compute the squared-c0-distance term, then add in the other two. */ + x = GETJSAMPLE(cinfo->colormap[0][i]); + if (x < minc0) { + tdist = (x - minc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else if (x > maxc0) { + tdist = (x - maxc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerc0) { + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[1][i]); + if (x < minc1) { + tdist = (x - minc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc1) { + tdist = (x - maxc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc1) { + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[2][i]); + if (x < minc2) { + tdist = (x - minc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc2) { + tdist = (x - maxc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc2) { + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; + } + + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = (JSAMPLE) i; + } + return ncolors; +} + + +LOCAL(void) +find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, + int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) +/* Find the closest colormap entry for each cell in the update box, + * given the list of candidate colors prepared by find_nearby_colors. + * Return the indexes of the closest entries in the bestcolor[] array. + * This routine uses Thomas' incremental distance calculation method to + * find the distance from a colormap entry to successive cells in the box. + */ +{ + int ic0, ic1, ic2; + int i, icolor; + register INT32 * bptr; /* pointer into bestdist[] array */ + JSAMPLE * cptr; /* pointer into bestcolor[] array */ + INT32 dist0, dist1; /* initial distance values */ + register INT32 dist2; /* current distance in inner loop */ + INT32 xx0, xx1; /* distance increments */ + register INT32 xx2; + INT32 inc0, inc1, inc2; /* initial values for increments */ + /* This array holds the distance to the nearest-so-far color for each cell */ + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + for (i = 0; i < numcolors; i++) { + icolor = GETJSAMPLE(colorlist[i]); + /* Compute (square of) distance from minc0/c1/c2 to this color */ + inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE; + dist0 = inc0*inc0; + inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE; + dist0 += inc1*inc1; + inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE; + dist0 += inc2*inc2; + /* Form the initial difference increments */ + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + /* Now loop over all cells in box, updating distance per Thomas method */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inc0; + for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) { + dist1 = dist0; + xx1 = inc1; + for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) { + dist2 = dist1; + xx2 = inc2; + for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) { + if (dist2 < *bptr) { + *bptr = dist2; + *cptr = (JSAMPLE) icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_C2 * STEP_C2; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_C1 * STEP_C1; + } + dist0 += xx0; + xx0 += 2 * STEP_C0 * STEP_C0; + } + } +} + + +LOCAL(void) +fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) +/* Fill the inverse-colormap entries in the update box that contains */ +/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ +/* we can fill as many others as we wish.) */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int minc0, minc1, minc2; /* lower left corner of update box */ + int ic0, ic1, ic2; + register JSAMPLE * cptr; /* pointer into bestcolor[] array */ + register histptr cachep; /* pointer into main cache array */ + /* This array lists the candidate colormap indexes. */ + JSAMPLE colorlist[MAXNUMCOLORS]; + int numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Convert cell coordinates to update box ID */ + c0 >>= BOX_C0_LOG; + c1 >>= BOX_C1_LOG; + c2 >>= BOX_C2_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist); + + /* Determine the actually nearest colors. */ + find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, + bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + c1 <<= BOX_C1_LOG; + c2 <<= BOX_C2_LOG; + cptr = bestcolor; + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + cachep = & histogram[c0+ic0][c1+ic1][c2]; + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + *cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1); + } + } + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +pass2_no_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register JSAMPROW inptr, outptr; + register histptr cachep; + register int c0, c1, c2; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the cache */ + c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT; + c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT; + c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT; + cachep = & histogram[c0][c1][c2]; + /* If we have not seen this color before, find nearest colormap entry */ + /* and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, c0,c1,c2); + /* Now emit the colormap index for this cell */ + *outptr++ = (JSAMPLE) (*cachep - 1); + } + } +} + + +METHODDEF(void) +pass2_fs_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + JSAMPROW inptr; /* => current input pixel */ + JSAMPROW outptr; /* => current output pixel */ + histptr cachep; + int dir; /* +1 or -1 depending on direction */ + int dir3; /* 3*dir, for advancing inptr & errorptr */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + int *error_limit = cquantize->error_limiter; + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + inptr += (width-1) * 3; /* so point to rightmost pixel */ + outptr += width-1; + dir = -1; + dir3 = -3; + errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */ + cquantize->on_odd_row = FALSE; /* flip for next time */ + } else { + /* work left to right in this row */ + dir = 1; + dir3 = 3; + errorptr = cquantize->fserrors; /* => entry before first real column */ + cquantize->on_odd_row = TRUE; /* flip for next time */ + } + /* Preset error values: no error propagated to first pixel from left */ + cur0 = cur1 = cur2 = 0; + /* and no error propagated to row below yet */ + belowerr0 = belowerr1 = belowerr2 = 0; + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + for (col = width; col > 0; col--) { + /* curN holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4); + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4); + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4); + /* Limit the error using transfer function set by init_error_limit. + * See comments with init_error_limit for rationale. + */ + cur0 = error_limit[cur0]; + cur1 = error_limit[cur1]; + cur2 = error_limit[cur2]; + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + * this sets the required size of the range_limit array. + */ + cur0 += GETJSAMPLE(inptr[0]); + cur1 += GETJSAMPLE(inptr[1]); + cur2 += GETJSAMPLE(inptr[2]); + cur0 = GETJSAMPLE(range_limit[cur0]); + cur1 = GETJSAMPLE(range_limit[cur1]); + cur2 = GETJSAMPLE(range_limit[cur2]); + /* Index into the cache with adjusted pixel value */ + cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT]; + /* If we have not seen this color before, find nearest colormap */ + /* entry and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT); + /* Now emit the colormap index for this cell */ + { register int pixcode = *cachep - 1; + *outptr = (JSAMPLE) pixcode; + /* Compute representation error for this pixel */ + cur0 -= GETJSAMPLE(colormap0[pixcode]); + cur1 -= GETJSAMPLE(colormap1[pixcode]); + cur2 -= GETJSAMPLE(colormap2[pixcode]); + } + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + { register LOCFSERROR bnexterr, delta; + + bnexterr = cur0; /* Process component 0 */ + delta = cur0 * 2; + cur0 += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr0 + cur0); + cur0 += delta; /* form error * 5 */ + bpreverr0 = belowerr0 + cur0; + belowerr0 = bnexterr; + cur0 += delta; /* form error * 7 */ + bnexterr = cur1; /* Process component 1 */ + delta = cur1 * 2; + cur1 += delta; /* form error * 3 */ + errorptr[1] = (FSERROR) (bpreverr1 + cur1); + cur1 += delta; /* form error * 5 */ + bpreverr1 = belowerr1 + cur1; + belowerr1 = bnexterr; + cur1 += delta; /* form error * 7 */ + bnexterr = cur2; /* Process component 2 */ + delta = cur2 * 2; + cur2 += delta; /* form error * 3 */ + errorptr[2] = (FSERROR) (bpreverr2 + cur2); + cur2 += delta; /* form error * 5 */ + bpreverr2 = belowerr2 + cur2; + belowerr2 = bnexterr; + cur2 += delta; /* form error * 7 */ + } + /* At this point curN contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + inptr += dir3; /* Advance pixel pointers to next column */ + outptr += dir; + errorptr += dir3; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error values into the + * final fserrors[] entry. Note we need not unload belowerrN because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ + errorptr[1] = (FSERROR) bpreverr1; + errorptr[2] = (FSERROR) bpreverr2; + } +} + + +/* + * Initialize the error-limiting transfer function (lookup table). + * The raw F-S error computation can potentially compute error values of up to + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * much less, otherwise obviously wrong pixels will be created. (Typical + * effects include weird fringes at color-area boundaries, isolated bright + * pixels in a dark area, etc.) The standard advice for avoiding this problem + * is to ensure that the "corners" of the color cube are allocated as output + * colors; then repeated errors in the same direction cannot cause cascading + * error buildup. However, that only prevents the error from getting + * completely out of hand; Aaron Giles reports that error limiting improves + * the results even with corner colors allocated. + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * well, but the smoother transfer function used below is even better. Thanks + * to Aaron Giles for this idea. + */ + +LOCAL(void) +init_error_limit (j_decompress_ptr cinfo) +/* Allocate and fill in the error_limiter table */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + int * table; + int in, out; + + table = (int *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int)); + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + cquantize->error_limiter = table; + +#define STEPSIZE ((MAXJSAMPLE+1)/16) + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + out = 0; + for (in = 0; in < STEPSIZE; in++, out++) { + table[in] = out; table[-in] = -out; + } + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) { + table[in] = out; table[-in] = -out; + } + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + for (; in <= MAXJSAMPLE; in++) { + table[in] = out; table[-in] = -out; + } +#undef STEPSIZE +} + + +/* + * Finish up at the end of each pass. + */ + +METHODDEF(void) +finish_pass1 (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Select the representative colors and fill in cinfo->colormap */ + cinfo->colormap = cquantize->sv_colormap; + select_colors(cinfo, cquantize->desired); + /* Force next pass to zero the color index table */ + cquantize->needs_zeroed = TRUE; +} + + +METHODDEF(void) +finish_pass2 (j_decompress_ptr cinfo) +{ + /* no work */ +} + + +/* + * Initialize for each processing pass. + */ + +METHODDEF(void) +start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int i; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + if (is_pre_scan) { + /* Set up method pointers */ + cquantize->pub.color_quantize = prescan_quantize; + cquantize->pub.finish_pass = finish_pass1; + cquantize->needs_zeroed = TRUE; /* Always zero histogram */ + } else { + /* Set up method pointers */ + if (cinfo->dither_mode == JDITHER_FS) + cquantize->pub.color_quantize = pass2_fs_dither; + else + cquantize->pub.color_quantize = pass2_no_dither; + cquantize->pub.finish_pass = finish_pass2; + + /* Make sure color count is acceptable */ + i = cinfo->actual_number_of_colors; + if (i < 1) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1); + if (i > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + if (cinfo->dither_mode == JDITHER_FS) { + size_t arraysize = (size_t) ((cinfo->output_width + 2) * + (3 * SIZEOF(FSERROR))); + /* Allocate Floyd-Steinberg workspace if we didn't already. */ + if (cquantize->fserrors == NULL) + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + /* Initialize the propagated errors to zero. */ + jzero_far((void FAR *) cquantize->fserrors, arraysize); + /* Make the error-limit table if we didn't already. */ + if (cquantize->error_limiter == NULL) + init_error_limit(cinfo); + cquantize->on_odd_row = FALSE; + } + + } + /* Zero the histogram or inverse color map, if necessary */ + if (cquantize->needs_zeroed) { + for (i = 0; i < HIST_C0_ELEMS; i++) { + jzero_far((void FAR *) histogram[i], + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = FALSE; + } +} + + +/* + * Switch to a new external colormap between output passes. + */ + +METHODDEF(void) +new_color_map_2_quant (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Reset the inverse color map */ + cquantize->needs_zeroed = TRUE; +} + + +/* + * Module initialization routine for 2-pass color quantization. + */ + +GLOBAL(void) +jinit_2pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + int i; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_2_quant; + cquantize->pub.new_color_map = new_color_map_2_quant; + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + cquantize->error_limiter = NULL; + + /* Make sure jdmaster didn't give me a case I can't handle */ + if (cinfo->out_color_components != 3) + ERREXIT(cinfo, JERR_NOTIMPL); + + /* Allocate the histogram/inverse colormap storage */ + cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d)); + for (i = 0; i < HIST_C0_ELEMS; i++) { + cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = TRUE; /* histogram is garbage now */ + + /* Allocate storage for the completed colormap, if required. + * We do this now since it is FAR storage and may affect + * the memory manager's space calculations. + */ + if (cinfo->enable_2pass_quant) { + /* Make sure color count is acceptable */ + int desired = cinfo->desired_number_of_colors; + /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ + if (desired < 8) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (desired > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3); + cquantize->desired = desired; + } else + cquantize->sv_colormap = NULL; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + /* Allocate Floyd-Steinberg workspace if necessary. + * This isn't really needed until pass 2, but again it is FAR storage. + * Although we will cope with a later change in dither_mode, + * we do not promise to honor max_memory_to_use if dither_mode changes. + */ + if (cinfo->dither_mode == JDITHER_FS) { + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR)))); + /* Might as well create the error-limiting table too. */ + init_error_limit(cinfo); + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ diff --git a/lib/jpeg/src/jquant2.d b/lib/jpeg/src/jquant2.d new file mode 100644 index 0000000..9baafb6 --- /dev/null +++ b/lib/jpeg/src/jquant2.d @@ -0,0 +1,14 @@ +jquant2.o: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jutils.c b/lib/jpeg/src/jutils.c new file mode 100644 index 0000000..62d103a --- /dev/null +++ b/lib/jpeg/src/jutils.c @@ -0,0 +1,231 @@ +/* + * jutils.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains tables and miscellaneous utility routines needed + * for both compression and decompression. + * Note we prefix all global names with "j" to minimize conflicts with + * a surrounding application. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +#if 0 /* This table is not actually needed in v6a */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#endif + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order7[7*7+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 14, 21, 28, 35, + 42, 49, 50, 43, 36, 29, 22, 30, + 37, 44, 51, 52, 45, 38, 46, 53, + 54, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order6[6*6+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 41, 34, 27, + 20, 13, 21, 28, 35, 42, 43, 36, + 29, 37, 44, 45, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order5[5*5+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 12, + 19, 26, 33, 34, 27, 20, 28, 35, + 36, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order4[4*4+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 25, 18, 11, 19, 26, 27, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order3[3*3+16] = { + 0, 1, 8, 16, 9, 2, 10, 17, + 18, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + +const int jpeg_natural_order2[2*2+16] = { + 0, 1, 8, 9, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +/* + * Arithmetic utilities + */ + +GLOBAL(long) +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + + +GLOBAL(long) +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + + +GLOBAL(void) +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + + +GLOBAL(void) +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + + +GLOBAL(void) +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} diff --git a/lib/jpeg/src/jutils.d b/lib/jpeg/src/jutils.d new file mode 100644 index 0000000..5c1c2a3 --- /dev/null +++ b/lib/jpeg/src/jutils.d @@ -0,0 +1,14 @@ +jutils.o: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h \ + jerror.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: diff --git a/lib/jpeg/src/jversion.h b/lib/jpeg/src/jversion.h new file mode 100644 index 0000000..daf9db2 --- /dev/null +++ b/lib/jpeg/src/jversion.h @@ -0,0 +1,14 @@ +/* + * jversion.h + * + * Copyright (C) 1991-2010, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains software version identification. + */ + + +#define JVERSION "8b 16-May-2010" + +#define JCOPYRIGHT "Copyright (C) 2010, Thomas G. Lane, Guido Vollbeding" diff --git a/lib/jpeg/src/rdbmp.c b/lib/jpeg/src/rdbmp.c new file mode 100644 index 0000000..dfdf96f --- /dev/null +++ b/lib/jpeg/src/rdbmp.c @@ -0,0 +1,480 @@ +/* + * rdbmp.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * Modified 2009-2010 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Microsoft "BMP" + * format (MS Windows 3.x, OS/2 1.x, and OS/2 2.x flavors). + * Currently, only 8-bit and 24-bit images are supported, not 1-bit or + * 4-bit (feeding such low-depth images into JPEG would be silly anyway). + * Also, we don't support RLE-compressed files. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed BMP format). + * + * This code contributed by James Arthur Boucher. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef BMP_SUPPORTED + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* Private version of data source object */ + +typedef struct _bmp_source_struct * bmp_source_ptr; + +typedef struct _bmp_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + JSAMPARRAY colormap; /* BMP colormap (converted to my format) */ + + jvirt_sarray_ptr whole_image; /* Needed to reverse row order */ + JDIMENSION source_row; /* Current source row number */ + JDIMENSION row_width; /* Physical width of scanlines in file */ + + int bits_per_pixel; /* remembers 8- or 24-bit format */ +} bmp_source_struct; + + +LOCAL(int) +read_byte (bmp_source_ptr sinfo) +/* Read next byte from BMP file */ +{ + register FILE *infile = sinfo->pub.input_file; + register int c; + + if ((c = getc(infile)) == EOF) + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + return c; +} + + +LOCAL(void) +read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize) +/* Read the colormap from a BMP file */ +{ + int i; + + switch (mapentrysize) { + case 3: + /* BGR format (occurs in OS/2 files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + } + break; + case 4: + /* BGR0 format (occurs in MS Windows files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + (void) read_byte(sinfo); + } + break; + default: + ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP); + break; + } +} + + +/* + * Read one row of pixels. + * The image has been read into the whole_image array, but is otherwise + * unprocessed. We must read it out in top-to-bottom row order, and if + * it is an 8-bit image, we must expand colormapped pixels to 24bit format. + */ + +METHODDEF(JDIMENSION) +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit colormap indexes */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + register JSAMPARRAY colormap = source->colormap; + JSAMPARRAY image_ptr; + register int t; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Expand the colormap indexes to real data */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + t = GETJSAMPLE(*inptr++); + *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */ + *outptr++ = colormap[1][t]; + *outptr++ = colormap[2][t]; + } + + return 1; +} + + +METHODDEF(JDIMENSION) +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 24-bit pixels */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + outptr += 3; + } + + return 1; +} + + +METHODDEF(JDIMENSION) +get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 32-bit pixels */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + inptr++; /* skip the 4th byte (Alpha channel) */ + outptr += 3; + } + + return 1; +} + + +/* + * This method loads the image into whole_image during the first call on + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + * get_8bit_row, get_24bit_row, or get_32bit_row on subsequent calls. + */ + +METHODDEF(JDIMENSION) +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + register FILE *infile = source->pub.input_file; + register int c; + register JSAMPROW out_ptr; + JSAMPARRAY image_ptr; + JDIMENSION row, col; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Read the data into a virtual array in input-file row order. */ + for (row = 0; row < cinfo->image_height; row++) { + if (progress != NULL) { + progress->pub.pass_counter = (long) row; + progress->pub.pass_limit = (long) cinfo->image_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + row, (JDIMENSION) 1, TRUE); + out_ptr = image_ptr[0]; + for (col = source->row_width; col > 0; col--) { + /* inline copy of read_byte() for speed */ + if ((c = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_INPUT_EOF); + *out_ptr++ = (JSAMPLE) c; + } + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Set up to read from the virtual array in top-to-bottom order */ + switch (source->bits_per_pixel) { + case 8: + source->pub.get_pixel_rows = get_8bit_row; + break; + case 24: + source->pub.get_pixel_rows = get_24bit_row; + break; + case 32: + source->pub.get_pixel_rows = get_32bit_row; + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + } + source->source_row = cinfo->image_height; + + /* And read the first row */ + return (*source->pub.get_pixel_rows) (cinfo, sinfo); +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + U_CHAR bmpfileheader[14]; + U_CHAR bmpinfoheader[64]; +#define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \ + (((unsigned int) UCH(array[offset+1])) << 8)) +#define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \ + (((INT32) UCH(array[offset+1])) << 8) + \ + (((INT32) UCH(array[offset+2])) << 16) + \ + (((INT32) UCH(array[offset+3])) << 24)) + INT32 bfOffBits; + INT32 headerSize; + INT32 biWidth; + INT32 biHeight; + unsigned int biPlanes; + INT32 biCompression; + INT32 biXPelsPerMeter,biYPelsPerMeter; + INT32 biClrUsed = 0; + int mapentrysize = 0; /* 0 indicates no colormap */ + INT32 bPad; + JDIMENSION row_width; + + /* Read and verify the bitmap file header */ + if (! ReadOK(source->pub.input_file, bmpfileheader, 14)) + ERREXIT(cinfo, JERR_INPUT_EOF); + if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */ + ERREXIT(cinfo, JERR_BMP_NOT); + bfOffBits = (INT32) GET_4B(bmpfileheader,10); + /* We ignore the remaining fileheader fields */ + + /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows), + * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which. + */ + if (! ReadOK(source->pub.input_file, bmpinfoheader, 4)) + ERREXIT(cinfo, JERR_INPUT_EOF); + headerSize = (INT32) GET_4B(bmpinfoheader,0); + if (headerSize < 12 || headerSize > 64) + ERREXIT(cinfo, JERR_BMP_BADHEADER); + if (! ReadOK(source->pub.input_file, bmpinfoheader+4, headerSize-4)) + ERREXIT(cinfo, JERR_INPUT_EOF); + + switch ((int) headerSize) { + case 12: + /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */ + biWidth = (INT32) GET_2B(bmpinfoheader,4); + biHeight = (INT32) GET_2B(bmpinfoheader,6); + biPlanes = GET_2B(bmpinfoheader,8); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10); + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */ + TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + TRACEMS2(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight); + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + break; + case 40: + case 64: + /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */ + /* or OS/2 2.x header, which has additional fields that we ignore */ + biWidth = GET_4B(bmpinfoheader,4); + biHeight = GET_4B(bmpinfoheader,8); + biPlanes = GET_2B(bmpinfoheader,12); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14); + biCompression = GET_4B(bmpinfoheader,16); + biXPelsPerMeter = GET_4B(bmpinfoheader,24); + biYPelsPerMeter = GET_4B(bmpinfoheader,28); + biClrUsed = GET_4B(bmpinfoheader,32); + /* biSizeImage, biClrImportant fields are ignored */ + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 4; /* Windows uses RGBQUAD colormap */ + TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + break; + case 32: /* RGB image + Alpha channel */ + TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + if (biCompression != 0) + ERREXIT(cinfo, JERR_BMP_COMPRESSED); + + if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) { + /* Set JFIF density parameters from the BMP data */ + cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */ + cinfo->Y_density = (UINT16) (biYPelsPerMeter/100); + cinfo->density_unit = 2; /* dots/cm */ + } + break; + default: + ERREXIT(cinfo, JERR_BMP_BADHEADER); + return; + } + + if (biWidth <= 0 || biHeight <= 0) + ERREXIT(cinfo, JERR_BMP_EMPTY); + if (biPlanes != 1) + ERREXIT(cinfo, JERR_BMP_BADPLANES); + + /* Compute distance to bitmap data --- will adjust for colormap below */ + bPad = bfOffBits - (headerSize + 14); + + /* Read the colormap, if any */ + if (mapentrysize > 0) { + if (biClrUsed <= 0) + biClrUsed = 256; /* assume it's 256 */ + else if (biClrUsed > 256) + ERREXIT(cinfo, JERR_BMP_BADCMAP); + /* Allocate space to store the colormap */ + source->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) biClrUsed, (JDIMENSION) 3); + /* and read it from the file */ + read_colormap(source, (int) biClrUsed, mapentrysize); + /* account for size of colormap */ + bPad -= biClrUsed * mapentrysize; + } + + /* Skip any remaining pad bytes */ + if (bPad < 0) /* incorrect bfOffBits value? */ + ERREXIT(cinfo, JERR_BMP_BADHEADER); + while (--bPad >= 0) { + (void) read_byte(source); + } + + /* Compute row width in file, including padding to 4-byte boundary */ + if (source->bits_per_pixel == 24) + row_width = (JDIMENSION) (biWidth * 3); + else if (source->bits_per_pixel == 32) + row_width = (JDIMENSION) (biWidth * 4); + else + row_width = (JDIMENSION) biWidth; + while ((row_width & 3) != 0) row_width++; + source->row_width = row_width; + + /* Allocate space for inversion array, prepare for preload pass */ + source->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + row_width, (JDIMENSION) biHeight, (JDIMENSION) 1); + source->pub.get_pixel_rows = preload_image; + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + + /* Allocate one-row buffer for returned data */ + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (biWidth * 3), (JDIMENSION) 1); + source->pub.buffer_height = 1; + + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + cinfo->data_precision = 8; + cinfo->image_width = (JDIMENSION) biWidth; + cinfo->image_height = (JDIMENSION) biHeight; +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for BMP format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_bmp (j_compress_ptr cinfo) +{ + bmp_source_ptr source; + + /* Create module interface object */ + source = (bmp_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(bmp_source_struct)); + source->cinfo = cinfo; /* make back link for subroutines */ + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_bmp; + source->pub.finish_input = finish_input_bmp; + + return (cjpeg_source_ptr) source; +} + +#endif /* BMP_SUPPORTED */ diff --git a/lib/jpeg/src/rdbmp.d b/lib/jpeg/src/rdbmp.d new file mode 100644 index 0000000..c46fb77 --- /dev/null +++ b/lib/jpeg/src/rdbmp.d @@ -0,0 +1,16 @@ +rdbmp.o: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdcolmap.c b/lib/jpeg/src/rdcolmap.c new file mode 100644 index 0000000..eebf834 --- /dev/null +++ b/lib/jpeg/src/rdcolmap.c @@ -0,0 +1,253 @@ +/* + * rdcolmap.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file implements djpeg's "-map file" switch. It reads a source image + * and constructs a colormap to be supplied to the JPEG decompressor. + * + * Currently, these file formats are supported for the map file: + * GIF: the contents of the GIF's global colormap are used. + * PPM (either text or raw flavor): the entire file is read and + * each unique pixel value is entered in the map. + * Note that reading a large PPM file will be horrendously slow. + * Typically, a PPM-format map file should contain just one pixel + * of each desired color. Such a file can be extracted from an + * ordinary image PPM file with ppmtomap(1). + * + * Rescaling a PPM that has a maxval unequal to MAXJSAMPLE is not + * currently implemented. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ + +/* Portions of this code are based on the PBMPLUS library, which is: +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + +/* + * Add a (potentially) new color to the color map. + */ + +LOCAL(void) +add_map_entry (j_decompress_ptr cinfo, int R, int G, int B) +{ + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + int ncolors = cinfo->actual_number_of_colors; + int index; + + /* Check for duplicate color. */ + for (index = 0; index < ncolors; index++) { + if (GETJSAMPLE(colormap0[index]) == R && + GETJSAMPLE(colormap1[index]) == G && + GETJSAMPLE(colormap2[index]) == B) + return; /* color is already in map */ + } + + /* Check for map overflow. */ + if (ncolors >= (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (MAXJSAMPLE+1)); + + /* OK, add color to map. */ + colormap0[ncolors] = (JSAMPLE) R; + colormap1[ncolors] = (JSAMPLE) G; + colormap2[ncolors] = (JSAMPLE) B; + cinfo->actual_number_of_colors++; +} + + +/* + * Extract color map from a GIF file. + */ + +LOCAL(void) +read_gif_map (j_decompress_ptr cinfo, FILE * infile) +{ + int header[13]; + int i, colormaplen; + int R, G, B; + + /* Initial 'G' has already been read by read_color_map */ + /* Read the rest of the GIF header and logical screen descriptor */ + for (i = 1; i < 13; i++) { + if ((header[i] = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + } + + /* Verify GIF Header */ + if (header[1] != 'I' || header[2] != 'F') + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* There must be a global color map. */ + if ((header[10] & 0x80) == 0) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* OK, fetch it. */ + colormaplen = 2 << (header[10] & 0x07); + + for (i = 0; i < colormaplen; i++) { + R = getc(infile); + G = getc(infile); + B = getc(infile); + if (R == EOF || G == EOF || B == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + add_map_entry(cinfo, + R << (BITS_IN_JSAMPLE-8), + G << (BITS_IN_JSAMPLE-8), + B << (BITS_IN_JSAMPLE-8)); + } +} + + +/* Support routines for reading PPM */ + + +LOCAL(int) +pbm_getc (FILE * infile) +/* Read next char, skipping over any comments */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(infile); + if (ch == '#') { + do { + ch = getc(infile); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(unsigned int) +read_pbm_integer (j_decompress_ptr cinfo, FILE * infile) +/* Read an unsigned decimal integer from the PPM file */ +/* Swallows one trailing character after the integer */ +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ +/* This should not be a problem in practice. */ +{ + register int ch; + register unsigned int val; + + /* Skip any leading whitespace */ + do { + ch = pbm_getc(infile); + if (ch == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + val = ch - '0'; + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + val *= 10; + val += ch - '0'; + } + return val; +} + + +/* + * Extract color map from a PPM file. + */ + +LOCAL(void) +read_ppm_map (j_decompress_ptr cinfo, FILE * infile) +{ + int c; + unsigned int w, h, maxval, row, col; + int R, G, B; + + /* Initial 'P' has already been read by read_color_map */ + c = getc(infile); /* save format discriminator for a sec */ + + /* while we fetch the remaining header info */ + w = read_pbm_integer(cinfo, infile); + h = read_pbm_integer(cinfo, infile); + maxval = read_pbm_integer(cinfo, infile); + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* For now, we don't support rescaling from an unusual maxval. */ + if (maxval != (unsigned int) MAXJSAMPLE) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + switch (c) { + case '3': /* it's a text-format PPM file */ + for (row = 0; row < h; row++) { + for (col = 0; col < w; col++) { + R = read_pbm_integer(cinfo, infile); + G = read_pbm_integer(cinfo, infile); + B = read_pbm_integer(cinfo, infile); + add_map_entry(cinfo, R, G, B); + } + } + break; + + case '6': /* it's a raw-format PPM file */ + for (row = 0; row < h; row++) { + for (col = 0; col < w; col++) { + R = getc(infile); + G = getc(infile); + B = getc(infile); + if (R == EOF || G == EOF || B == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + add_map_entry(cinfo, R, G, B); + } + } + break; + + default: + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + break; + } +} + + +/* + * Main entry point from djpeg.c. + * Input: opened input file (from file name argument on command line). + * Output: colormap and actual_number_of_colors fields are set in cinfo. + */ + +GLOBAL(void) +read_color_map (j_decompress_ptr cinfo, FILE * infile) +{ + /* Allocate space for a color map of maximum supported size. */ + cinfo->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1), (JDIMENSION) 3); + cinfo->actual_number_of_colors = 0; /* initialize map to empty */ + + /* Read first byte to determine file format */ + switch (getc(infile)) { + case 'G': + read_gif_map(cinfo, infile); + break; + case 'P': + read_ppm_map(cinfo, infile); + break; + default: + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + break; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ diff --git a/lib/jpeg/src/rdcolmap.d b/lib/jpeg/src/rdcolmap.d new file mode 100644 index 0000000..87d0d36 --- /dev/null +++ b/lib/jpeg/src/rdcolmap.d @@ -0,0 +1,16 @@ +rdcolmap.o: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdgif.c b/lib/jpeg/src/rdgif.c new file mode 100644 index 0000000..b0757e7 --- /dev/null +++ b/lib/jpeg/src/rdgif.c @@ -0,0 +1,38 @@ +/* + * rdgif.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in GIF format. + * + ***************************************************************************** + * NOTE: to avoid entanglements with Unisys' patent on LZW compression, * + * the ability to read GIF files has been removed from the IJG distribution. * + * Sorry about that. * + ***************************************************************************** + * + * We are required to state that + * "The Graphics Interchange Format(c) is the Copyright property of + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + * CompuServe Incorporated." + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef GIF_SUPPORTED + +/* + * The module selection routine for GIF format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_gif (j_compress_ptr cinfo) +{ + fprintf(stderr, "GIF input is unsupported for legal reasons. Sorry.\n"); + exit(EXIT_FAILURE); + return NULL; /* keep compiler happy */ +} + +#endif /* GIF_SUPPORTED */ diff --git a/lib/jpeg/src/rdgif.d b/lib/jpeg/src/rdgif.d new file mode 100644 index 0000000..20a9e47 --- /dev/null +++ b/lib/jpeg/src/rdgif.d @@ -0,0 +1,16 @@ +rdgif.o: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdppm.c b/lib/jpeg/src/rdppm.c new file mode 100644 index 0000000..9dcd2dc --- /dev/null +++ b/lib/jpeg/src/rdppm.c @@ -0,0 +1,459 @@ +/* + * rdppm.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 2009 by Bill Allombert, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in PPM/PGM format. + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + * The PBMPLUS library is NOT required to compile this software + * (but it is highly useful as a set of PPM image manipulation programs). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed PPM format). + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef PPM_SUPPORTED + + +/* Portions of this code are based on the PBMPLUS library, which is: +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* + * On most systems, reading individual bytes with getc() is drastically less + * efficient than buffering a row at a time with fread(). On PCs, we must + * allocate the buffer in near data space, because we are assuming small-data + * memory model, wherein fread() can't reach far memory. If you need to + * process very wide images on a PC, you might have to compile in large-memory + * model, or else replace fread() with a getc() loop --- which will be much + * slower. + */ + + +/* Private version of data source object */ + +typedef struct { + struct cjpeg_source_struct pub; /* public fields */ + + U_CHAR *iobuffer; /* non-FAR pointer to I/O buffer */ + JSAMPROW pixrow; /* FAR pointer to same */ + size_t buffer_width; /* width of I/O buffer */ + JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ +} ppm_source_struct; + +typedef ppm_source_struct * ppm_source_ptr; + + +LOCAL(int) +pbm_getc (FILE * infile) +/* Read next char, skipping over any comments */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(infile); + if (ch == '#') { + do { + ch = getc(infile); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(unsigned int) +read_pbm_integer (j_compress_ptr cinfo, FILE * infile) +/* Read an unsigned decimal integer from the PPM file */ +/* Swallows one trailing character after the integer */ +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ +/* This should not be a problem in practice. */ +{ + register int ch; + register unsigned int val; + + /* Skip any leading whitespace */ + do { + ch = pbm_getc(infile); + if (ch == EOF) + ERREXIT(cinfo, JERR_INPUT_EOF); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') + ERREXIT(cinfo, JERR_PPM_NONNUMERIC); + + val = ch - '0'; + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + val *= 10; + val += ch - '0'; + } + return val; +} + + +/* + * Read one row of pixels. + * + * We provide several different versions depending on input file format. + * In all cases, input is scaled to the size of JSAMPLE. + * + * A really fast path is provided for reading byte/sample raw files with + * maxval = MAXJSAMPLE, which is the normal case for 8-bit data. + */ + + +METHODDEF(JDIMENSION) +get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading text-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + FILE * infile = source->pub.input_file; + register JSAMPROW ptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading text-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + FILE * infile = source->pub.input_file; + register JSAMPROW ptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_scaled_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[UCH(*bufferptr++)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[UCH(*bufferptr++)]; + *ptr++ = rescale[UCH(*bufferptr++)]; + *ptr++ = rescale[UCH(*bufferptr++)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_raw_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format files with maxval = MAXJSAMPLE. + * In this case we just read right into the JSAMPLE buffer! + * Note that same code works for PPM and PGM files. + */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + return 1; +} + + +METHODDEF(JDIMENSION) +get_word_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register int temp; + temp = UCH(*bufferptr++) << 8; + temp |= UCH(*bufferptr++); + *ptr++ = rescale[temp]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_word_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register int temp; + temp = UCH(*bufferptr++) << 8; + temp |= UCH(*bufferptr++); + *ptr++ = rescale[temp]; + temp = UCH(*bufferptr++) << 8; + temp |= UCH(*bufferptr++); + *ptr++ = rescale[temp]; + temp = UCH(*bufferptr++) << 8; + temp |= UCH(*bufferptr++); + *ptr++ = rescale[temp]; + } + return 1; +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + int c; + unsigned int w, h, maxval; + boolean need_iobuffer, use_raw_buffer, need_rescale; + + if (getc(source->pub.input_file) != 'P') + ERREXIT(cinfo, JERR_PPM_NOT); + + c = getc(source->pub.input_file); /* subformat discriminator character */ + + /* detect unsupported variants (ie, PBM) before trying to read header */ + switch (c) { + case '2': /* it's a text-format PGM file */ + case '3': /* it's a text-format PPM file */ + case '5': /* it's a raw-format PGM file */ + case '6': /* it's a raw-format PPM file */ + break; + default: + ERREXIT(cinfo, JERR_PPM_NOT); + break; + } + + /* fetch the remaining header info */ + w = read_pbm_integer(cinfo, source->pub.input_file); + h = read_pbm_integer(cinfo, source->pub.input_file); + maxval = read_pbm_integer(cinfo, source->pub.input_file); + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + ERREXIT(cinfo, JERR_PPM_NOT); + + cinfo->data_precision = BITS_IN_JSAMPLE; /* we always rescale data to this */ + cinfo->image_width = (JDIMENSION) w; + cinfo->image_height = (JDIMENSION) h; + + /* initialize flags to most common settings */ + need_iobuffer = TRUE; /* do we need an I/O buffer? */ + use_raw_buffer = FALSE; /* do we map input buffer onto I/O buffer? */ + need_rescale = TRUE; /* do we need a rescale array? */ + + switch (c) { + case '2': /* it's a text-format PGM file */ + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_PGM_TEXT, w, h); + source->pub.get_pixel_rows = get_text_gray_row; + need_iobuffer = FALSE; + break; + + case '3': /* it's a text-format PPM file */ + cinfo->input_components = 3; + cinfo->in_color_space = JCS_RGB; + TRACEMS2(cinfo, 1, JTRC_PPM_TEXT, w, h); + source->pub.get_pixel_rows = get_text_rgb_row; + need_iobuffer = FALSE; + break; + + case '5': /* it's a raw-format PGM file */ + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_PGM, w, h); + if (maxval > 255) { + source->pub.get_pixel_rows = get_word_gray_row; + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + source->pub.get_pixel_rows = get_raw_row; + use_raw_buffer = TRUE; + need_rescale = FALSE; + } else { + source->pub.get_pixel_rows = get_scaled_gray_row; + } + break; + + case '6': /* it's a raw-format PPM file */ + cinfo->input_components = 3; + cinfo->in_color_space = JCS_RGB; + TRACEMS2(cinfo, 1, JTRC_PPM, w, h); + if (maxval > 255) { + source->pub.get_pixel_rows = get_word_rgb_row; + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + source->pub.get_pixel_rows = get_raw_row; + use_raw_buffer = TRUE; + need_rescale = FALSE; + } else { + source->pub.get_pixel_rows = get_scaled_rgb_row; + } + break; + } + + /* Allocate space for I/O buffer: 1 or 3 bytes or words/pixel. */ + if (need_iobuffer) { + source->buffer_width = (size_t) w * cinfo->input_components * + ((maxval<=255) ? SIZEOF(U_CHAR) : (2*SIZEOF(U_CHAR))); + source->iobuffer = (U_CHAR *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + source->buffer_width); + } + + /* Create compressor input buffer. */ + if (use_raw_buffer) { + /* For unscaled raw-input case, we can just map it onto the I/O buffer. */ + /* Synthesize a JSAMPARRAY pointer structure */ + /* Cast here implies near->far pointer conversion on PCs */ + source->pixrow = (JSAMPROW) source->iobuffer; + source->pub.buffer = & source->pixrow; + source->pub.buffer_height = 1; + } else { + /* Need to translate anyway, so make a separate sample buffer. */ + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) w * cinfo->input_components, (JDIMENSION) 1); + source->pub.buffer_height = 1; + } + + /* Compute the rescaling array if required. */ + if (need_rescale) { + INT32 val, half_maxval; + + /* On 16-bit-int machines we have to be careful of maxval = 65535 */ + source->rescale = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (((long) maxval + 1L) * SIZEOF(JSAMPLE))); + half_maxval = maxval / 2; + for (val = 0; val <= (INT32) maxval; val++) { + /* The multiplication here must be done in 32 bits to avoid overflow */ + source->rescale[val] = (JSAMPLE) ((val*MAXJSAMPLE + half_maxval)/maxval); + } + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for PPM format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_ppm (j_compress_ptr cinfo) +{ + ppm_source_ptr source; + + /* Create module interface object */ + source = (ppm_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ppm_source_struct)); + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_ppm; + source->pub.finish_input = finish_input_ppm; + + return (cjpeg_source_ptr) source; +} + +#endif /* PPM_SUPPORTED */ diff --git a/lib/jpeg/src/rdppm.d b/lib/jpeg/src/rdppm.d new file mode 100644 index 0000000..df58d14 --- /dev/null +++ b/lib/jpeg/src/rdppm.d @@ -0,0 +1,16 @@ +rdppm.o: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdrle.c b/lib/jpeg/src/rdrle.c new file mode 100644 index 0000000..df871e0 --- /dev/null +++ b/lib/jpeg/src/rdrle.c @@ -0,0 +1,387 @@ +/* + * rdrle.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Utah RLE format. + * The Utah Raster Toolkit library is required (version 3.1 or later). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed RLE format). + * + * Based on code contributed by Mike Lijewski, + * with updates from Robert Hutchinson. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef RLE_SUPPORTED + +/* rle.h is provided by the Utah Raster Toolkit. */ + +#include + +/* + * We assume that JSAMPLE has the same representation as rle_pixel, + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * We support the following types of RLE files: + * + * GRAYSCALE - 8 bits, no colormap + * MAPPEDGRAY - 8 bits, 1 channel colomap + * PSEUDOCOLOR - 8 bits, 3 channel colormap + * TRUECOLOR - 24 bits, 3 channel colormap + * DIRECTCOLOR - 24 bits, no colormap + * + * For now, we ignore any alpha channel in the image. + */ + +typedef enum + { GRAYSCALE, MAPPEDGRAY, PSEUDOCOLOR, TRUECOLOR, DIRECTCOLOR } rle_kind; + + +/* + * Since RLE stores scanlines bottom-to-top, we have to invert the image + * to conform to JPEG's top-to-bottom order. To do this, we read the + * incoming image into a virtual array on the first get_pixel_rows call, + * then fetch the required row from the virtual array on subsequent calls. + */ + +typedef struct _rle_source_struct * rle_source_ptr; + +typedef struct _rle_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + rle_kind visual; /* actual type of input file */ + jvirt_sarray_ptr image; /* virtual array to hold the image */ + JDIMENSION row; /* current row # in the virtual array */ + rle_hdr header; /* Input file information */ + rle_pixel** rle_row; /* holds a row returned by rle_getrow() */ + +} rle_source_struct; + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JDIMENSION width, height; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* Use RLE library routine to get the header info */ + source->header = *rle_hdr_init(NULL); + source->header.rle_file = source->pub.input_file; + switch (rle_get_setup(&(source->header))) { + case RLE_SUCCESS: + /* A-OK */ + break; + case RLE_NOT_RLE: + ERREXIT(cinfo, JERR_RLE_NOT); + break; + case RLE_NO_SPACE: + ERREXIT(cinfo, JERR_RLE_MEM); + break; + case RLE_EMPTY: + ERREXIT(cinfo, JERR_RLE_EMPTY); + break; + case RLE_EOF: + ERREXIT(cinfo, JERR_RLE_EOF); + break; + default: + ERREXIT(cinfo, JERR_RLE_BADERROR); + break; + } + + /* Figure out what we have, set private vars and return values accordingly */ + + width = source->header.xmax - source->header.xmin + 1; + height = source->header.ymax - source->header.ymin + 1; + source->header.xmin = 0; /* realign horizontally */ + source->header.xmax = width-1; + + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->data_precision = 8; /* we can only handle 8 bit data */ + + if (source->header.ncolors == 1 && source->header.ncmap == 0) { + source->visual = GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_RLE_GRAY, width, height); + } else if (source->header.ncolors == 1 && source->header.ncmap == 1) { + source->visual = MAPPEDGRAY; + TRACEMS3(cinfo, 1, JTRC_RLE_MAPGRAY, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 1 && source->header.ncmap == 3) { + source->visual = PSEUDOCOLOR; + TRACEMS3(cinfo, 1, JTRC_RLE_MAPPED, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 3 && source->header.ncmap == 3) { + source->visual = TRUECOLOR; + TRACEMS3(cinfo, 1, JTRC_RLE_FULLMAP, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 3 && source->header.ncmap == 0) { + source->visual = DIRECTCOLOR; + TRACEMS2(cinfo, 1, JTRC_RLE, width, height); + } else + ERREXIT(cinfo, JERR_RLE_UNSUPPORTED); + + if (source->visual == GRAYSCALE || source->visual == MAPPEDGRAY) { + cinfo->in_color_space = JCS_GRAYSCALE; + cinfo->input_components = 1; + } else { + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + } + + /* + * A place to hold each scanline while it's converted. + * (GRAYSCALE scanlines don't need converting) + */ + if (source->visual != GRAYSCALE) { + source->rle_row = (rle_pixel**) (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) width, (JDIMENSION) cinfo->input_components); + } + + /* request a virtual array to hold the image */ + source->image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) (width * source->header.ncolors), + (JDIMENSION) height, (JDIMENSION) 1); + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + /* count file input as separate pass */ + progress->total_extra_passes++; + } +#endif + + source->pub.buffer_height = 1; +} + + +/* + * Read one row of pixels. + * Called only after load_image has read the image into the virtual array. + * Used for GRAYSCALE, MAPPEDGRAY, TRUECOLOR, and DIRECTCOLOR images. + */ + +METHODDEF(JDIMENSION) +get_rle_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + + source->row--; + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + return 1; +} + +/* + * Read one row of pixels. + * Called only after load_image has read the image into the virtual array. + * Used for PSEUDOCOLOR images. + */ + +METHODDEF(JDIMENSION) +get_pseudocolor_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JSAMPROW src_row, dest_row; + JDIMENSION col; + rle_map *colormap; + int val; + + colormap = source->header.cmap; + dest_row = source->pub.buffer[0]; + source->row--; + src_row = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + for (col = cinfo->image_width; col > 0; col--) { + val = GETJSAMPLE(*src_row++); + *dest_row++ = (JSAMPLE) (colormap[val ] >> 8); + *dest_row++ = (JSAMPLE) (colormap[val + 256] >> 8); + *dest_row++ = (JSAMPLE) (colormap[val + 512] >> 8); + } + + return 1; +} + + +/* + * Load the image into a virtual array. We have to do this because RLE + * files start at the lower left while the JPEG standard has them starting + * in the upper left. This is called the first time we want to get a row + * of input. What we do is load the RLE data into the array and then call + * the appropriate routine to read one row from the array. Before returning, + * we set source->pub.get_pixel_rows so that subsequent calls go straight to + * the appropriate row-reading routine. + */ + +METHODDEF(JDIMENSION) +load_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JDIMENSION row, col; + JSAMPROW scanline, red_ptr, green_ptr, blue_ptr; + rle_pixel **rle_row; + rle_map *colormap; + char channel; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + colormap = source->header.cmap; + rle_row = source->rle_row; + + /* Read the RLE data into our virtual array. + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + * and (b) we are not on a machine where FAR pointers differ from regular. + */ + RLE_CLR_BIT(source->header, RLE_ALPHA); /* don't read the alpha channel */ + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_limit = cinfo->image_height; + progress->pub.pass_counter = 0; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + + switch (source->visual) { + + case GRAYSCALE: + case PSEUDOCOLOR: + for (row = 0; row < cinfo->image_height; row++) { + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_getrow(&source->header, rle_row); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + break; + + case MAPPEDGRAY: + case TRUECOLOR: + for (row = 0; row < cinfo->image_height; row++) { + scanline = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_row = source->rle_row; + rle_getrow(&source->header, rle_row); + + for (col = 0; col < cinfo->image_width; col++) { + for (channel = 0; channel < source->header.ncolors; channel++) { + *scanline++ = (JSAMPLE) + (colormap[GETJSAMPLE(rle_row[channel][col]) + 256 * channel] >> 8); + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + break; + + case DIRECTCOLOR: + for (row = 0; row < cinfo->image_height; row++) { + scanline = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_getrow(&source->header, rle_row); + + red_ptr = rle_row[0]; + green_ptr = rle_row[1]; + blue_ptr = rle_row[2]; + + for (col = cinfo->image_width; col > 0; col--) { + *scanline++ = *red_ptr++; + *scanline++ = *green_ptr++; + *scanline++ = *blue_ptr++; + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) + progress->completed_extra_passes++; +#endif + + /* Set up to call proper row-extraction routine in future */ + if (source->visual == PSEUDOCOLOR) { + source->pub.buffer = source->rle_row; + source->pub.get_pixel_rows = get_pseudocolor_row; + } else { + source->pub.get_pixel_rows = get_rle_row; + } + source->row = cinfo->image_height; + + /* And fetch the topmost (bottommost) row */ + return (*source->pub.get_pixel_rows) (cinfo, sinfo); +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for RLE format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_rle (j_compress_ptr cinfo) +{ + rle_source_ptr source; + + /* Create module interface object */ + source = (rle_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(rle_source_struct)); + /* Fill in method ptrs */ + source->pub.start_input = start_input_rle; + source->pub.finish_input = finish_input_rle; + source->pub.get_pixel_rows = load_image; + + return (cjpeg_source_ptr) source; +} + +#endif /* RLE_SUPPORTED */ diff --git a/lib/jpeg/src/rdrle.d b/lib/jpeg/src/rdrle.d new file mode 100644 index 0000000..b524625 --- /dev/null +++ b/lib/jpeg/src/rdrle.d @@ -0,0 +1,16 @@ +rdrle.o: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdswitch.c b/lib/jpeg/src/rdswitch.c new file mode 100644 index 0000000..5eebcc1 --- /dev/null +++ b/lib/jpeg/src/rdswitch.c @@ -0,0 +1,365 @@ +/* + * rdswitch.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to process some of cjpeg's more complicated + * command-line switches. Switches processed here are: + * -qtables file Read quantization tables from text file + * -scans file Read scan script from text file + * -quality N[,N,...] Set quality ratings + * -qslots N[,N,...] Set component quantization table selectors + * -sample HxV[,HxV,...] Set component sampling factors + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include /* to declare isdigit(), isspace() */ + + +LOCAL(int) +text_getc (FILE * file) +/* Read next char, skipping over any comments (# to end of line) */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(file); + if (ch == '#') { + do { + ch = getc(file); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(boolean) +read_text_integer (FILE * file, long * result, int * termchar) +/* Read an unsigned decimal integer from a file, store it in result */ +/* Reads one trailing character after the integer; returns it in termchar */ +{ + register int ch; + register long val; + + /* Skip any leading whitespace, detect EOF */ + do { + ch = text_getc(file); + if (ch == EOF) { + *termchar = ch; + return FALSE; + } + } while (isspace(ch)); + + if (! isdigit(ch)) { + *termchar = ch; + return FALSE; + } + + val = ch - '0'; + while ((ch = text_getc(file)) != EOF) { + if (! isdigit(ch)) + break; + val *= 10; + val += ch - '0'; + } + *result = val; + *termchar = ch; + return TRUE; +} + + +GLOBAL(boolean) +read_quant_tables (j_compress_ptr cinfo, char * filename, boolean force_baseline) +/* Read a set of quantization tables from the specified file. + * The file is plain ASCII text: decimal numbers with whitespace between. + * Comments preceded by '#' may be included in the file. + * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values. + * The tables are implicitly numbered 0,1,etc. + * NOTE: does not affect the qslots mapping, which will default to selecting + * table 0 for luminance (or primary) components, 1 for chrominance components. + * You must use -qslots if you want a different component->table mapping. + */ +{ + FILE * fp; + int tblno, i, termchar; + long val; + unsigned int table[DCTSIZE2]; + + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "Can't open table file %s\n", filename); + return FALSE; + } + tblno = 0; + + while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */ + if (tblno >= NUM_QUANT_TBLS) { + fprintf(stderr, "Too many tables in file %s\n", filename); + fclose(fp); + return FALSE; + } + table[0] = (unsigned int) val; + for (i = 1; i < DCTSIZE2; i++) { + if (! read_text_integer(fp, &val, &termchar)) { + fprintf(stderr, "Invalid table data in file %s\n", filename); + fclose(fp); + return FALSE; + } + table[i] = (unsigned int) val; + } + jpeg_add_quant_table(cinfo, tblno, table, cinfo->q_scale_factor[tblno], + force_baseline); + tblno++; + } + + if (termchar != EOF) { + fprintf(stderr, "Non-numeric data in file %s\n", filename); + fclose(fp); + return FALSE; + } + + fclose(fp); + return TRUE; +} + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(boolean) +read_scan_integer (FILE * file, long * result, int * termchar) +/* Variant of read_text_integer that always looks for a non-space termchar; + * this simplifies parsing of punctuation in scan scripts. + */ +{ + register int ch; + + if (! read_text_integer(file, result, termchar)) + return FALSE; + ch = *termchar; + while (ch != EOF && isspace(ch)) + ch = text_getc(file); + if (isdigit(ch)) { /* oops, put it back */ + if (ungetc(ch, file) == EOF) + return FALSE; + ch = ' '; + } else { + /* Any separators other than ';' and ':' are ignored; + * this allows user to insert commas, etc, if desired. + */ + if (ch != EOF && ch != ';' && ch != ':') + ch = ' '; + } + *termchar = ch; + return TRUE; +} + + +GLOBAL(boolean) +read_scan_script (j_compress_ptr cinfo, char * filename) +/* Read a scan script from the specified text file. + * Each entry in the file defines one scan to be emitted. + * Entries are separated by semicolons ';'. + * An entry contains one to four component indexes, + * optionally followed by a colon ':' and four progressive-JPEG parameters. + * The component indexes denote which component(s) are to be transmitted + * in the current scan. The first component has index 0. + * Sequential JPEG is used if the progressive-JPEG parameters are omitted. + * The file is free format text: any whitespace may appear between numbers + * and the ':' and ';' punctuation marks. Also, other punctuation (such + * as commas or dashes) can be placed between numbers if desired. + * Comments preceded by '#' may be included in the file. + * Note: we do very little validity checking here; + * jcmaster.c will validate the script parameters. + */ +{ + FILE * fp; + int scanno, ncomps, termchar; + long val; + jpeg_scan_info * scanptr; +#define MAX_SCANS 100 /* quite arbitrary limit */ + jpeg_scan_info scans[MAX_SCANS]; + + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "Can't open scan definition file %s\n", filename); + return FALSE; + } + scanptr = scans; + scanno = 0; + + while (read_scan_integer(fp, &val, &termchar)) { + if (scanno >= MAX_SCANS) { + fprintf(stderr, "Too many scans defined in file %s\n", filename); + fclose(fp); + return FALSE; + } + scanptr->component_index[0] = (int) val; + ncomps = 1; + while (termchar == ' ') { + if (ncomps >= MAX_COMPS_IN_SCAN) { + fprintf(stderr, "Too many components in one scan in file %s\n", + filename); + fclose(fp); + return FALSE; + } + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scanptr->component_index[ncomps] = (int) val; + ncomps++; + } + scanptr->comps_in_scan = ncomps; + if (termchar == ':') { + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Ss = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Se = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Ah = (int) val; + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scanptr->Al = (int) val; + } else { + /* set non-progressive parameters */ + scanptr->Ss = 0; + scanptr->Se = DCTSIZE2-1; + scanptr->Ah = 0; + scanptr->Al = 0; + } + if (termchar != ';' && termchar != EOF) { +bogus: + fprintf(stderr, "Invalid scan entry format in file %s\n", filename); + fclose(fp); + return FALSE; + } + scanptr++, scanno++; + } + + if (termchar != EOF) { + fprintf(stderr, "Non-numeric data in file %s\n", filename); + fclose(fp); + return FALSE; + } + + if (scanno > 0) { + /* Stash completed scan list in cinfo structure. + * NOTE: for cjpeg's use, JPOOL_IMAGE is the right lifetime for this data, + * but if you want to compress multiple images you'd want JPOOL_PERMANENT. + */ + scanptr = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + scanno * SIZEOF(jpeg_scan_info)); + MEMCOPY(scanptr, scans, scanno * SIZEOF(jpeg_scan_info)); + cinfo->scan_info = scanptr; + cinfo->num_scans = scanno; + } + + fclose(fp); + return TRUE; +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + +GLOBAL(boolean) +set_quality_ratings (j_compress_ptr cinfo, char *arg, boolean force_baseline) +/* Process a quality-ratings parameter string, of the form + * N[,N,...] + * If there are more q-table slots than parameters, the last value is replicated. + */ +{ + int val = 75; /* default value */ + int tblno; + char ch; + + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (*arg) { + ch = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c", &val, &ch) < 1) + return FALSE; + if (ch != ',') /* syntax check */ + return FALSE; + /* Convert user 0-100 rating to percentage scaling */ + cinfo->q_scale_factor[tblno] = jpeg_quality_scaling(val); + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining factors to last value */ + cinfo->q_scale_factor[tblno] = jpeg_quality_scaling(val); + } + } + jpeg_default_qtables(cinfo, force_baseline); + return TRUE; +} + + +GLOBAL(boolean) +set_quant_slots (j_compress_ptr cinfo, char *arg) +/* Process a quantization-table-selectors parameter string, of the form + * N[,N,...] + * If there are more components than parameters, the last value is replicated. + */ +{ + int val = 0; /* default table # */ + int ci; + char ch; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c", &val, &ch) < 1) + return FALSE; + if (ch != ',') /* syntax check */ + return FALSE; + if (val < 0 || val >= NUM_QUANT_TBLS) { + fprintf(stderr, "JPEG quantization tables are numbered 0..%d\n", + NUM_QUANT_TBLS-1); + return FALSE; + } + cinfo->comp_info[ci].quant_tbl_no = val; + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components to last table */ + cinfo->comp_info[ci].quant_tbl_no = val; + } + } + return TRUE; +} + + +GLOBAL(boolean) +set_sample_factors (j_compress_ptr cinfo, char *arg) +/* Process a sample-factors parameter string, of the form + * HxV[,HxV,...] + * If there are more components than parameters, "1x1" is assumed for the rest. + */ +{ + int ci, val1, val2; + char ch1, ch2; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch2 = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3) + return FALSE; + if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */ + return FALSE; + if (val1 <= 0 || val1 > 4 || val2 <= 0 || val2 > 4) { + fprintf(stderr, "JPEG sampling factors must be 1..4\n"); + return FALSE; + } + cinfo->comp_info[ci].h_samp_factor = val1; + cinfo->comp_info[ci].v_samp_factor = val2; + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components to 1x1 sampling */ + cinfo->comp_info[ci].h_samp_factor = 1; + cinfo->comp_info[ci].v_samp_factor = 1; + } + } + return TRUE; +} diff --git a/lib/jpeg/src/rdswitch.d b/lib/jpeg/src/rdswitch.d new file mode 100644 index 0000000..77041bf --- /dev/null +++ b/lib/jpeg/src/rdswitch.d @@ -0,0 +1,16 @@ +rdswitch.o: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/rdtarga.c b/lib/jpeg/src/rdtarga.c new file mode 100644 index 0000000..d7ffc33 --- /dev/null +++ b/lib/jpeg/src/rdtarga.c @@ -0,0 +1,500 @@ +/* + * rdtarga.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Targa format. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed Targa format). + * + * Based on code contributed by Lee Daniel Crocker. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef TARGA_SUPPORTED + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* Private version of data source object */ + +typedef struct _tga_source_struct * tga_source_ptr; + +typedef struct _tga_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + JSAMPARRAY colormap; /* Targa colormap (converted to my format) */ + + jvirt_sarray_ptr whole_image; /* Needed if funny input row order */ + JDIMENSION current_row; /* Current logical row number to read */ + + /* Pointer to routine to extract next Targa pixel from input file */ + JMETHOD(void, read_pixel, (tga_source_ptr sinfo)); + + /* Result of read_pixel is delivered here: */ + U_CHAR tga_pixel[4]; + + int pixel_size; /* Bytes per Targa pixel (1 to 4) */ + + /* State info for reading RLE-coded pixels; both counts must be init to 0 */ + int block_count; /* # of pixels remaining in RLE block */ + int dup_pixel_count; /* # of times to duplicate previous pixel */ + + /* This saves the correct pixel-row-expansion method for preload_image */ + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); +} tga_source_struct; + + +/* For expanding 5-bit pixel values to 8-bit with best rounding */ + +static const UINT8 c5to8bits[32] = { + 0, 8, 16, 25, 33, 41, 49, 58, + 66, 74, 82, 90, 99, 107, 115, 123, + 132, 140, 148, 156, 165, 173, 181, 189, + 197, 206, 214, 222, 230, 239, 247, 255 +}; + + + +LOCAL(int) +read_byte (tga_source_ptr sinfo) +/* Read next byte from Targa file */ +{ + register FILE *infile = sinfo->pub.input_file; + register int c; + + if ((c = getc(infile)) == EOF) + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + return c; +} + + +LOCAL(void) +read_colormap (tga_source_ptr sinfo, int cmaplen, int mapentrysize) +/* Read the colormap from a Targa file */ +{ + int i; + + /* Presently only handles 24-bit BGR format */ + if (mapentrysize != 24) + ERREXIT(sinfo->cinfo, JERR_TGA_BADCMAP); + + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + } +} + + +/* + * read_pixel methods: get a single pixel from Targa file into tga_pixel[] + */ + +METHODDEF(void) +read_non_rle_pixel (tga_source_ptr sinfo) +/* Read one Targa pixel from the input file; no RLE expansion */ +{ + register FILE *infile = sinfo->pub.input_file; + register int i; + + for (i = 0; i < sinfo->pixel_size; i++) { + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + } +} + + +METHODDEF(void) +read_rle_pixel (tga_source_ptr sinfo) +/* Read one Targa pixel from the input file, expanding RLE data as needed */ +{ + register FILE *infile = sinfo->pub.input_file; + register int i; + + /* Duplicate previously read pixel? */ + if (sinfo->dup_pixel_count > 0) { + sinfo->dup_pixel_count--; + return; + } + + /* Time to read RLE block header? */ + if (--sinfo->block_count < 0) { /* decrement pixels remaining in block */ + i = read_byte(sinfo); + if (i & 0x80) { /* Start of duplicate-pixel block? */ + sinfo->dup_pixel_count = i & 0x7F; /* number of dups after this one */ + sinfo->block_count = 0; /* then read new block header */ + } else { + sinfo->block_count = i & 0x7F; /* number of pixels after this one */ + } + } + + /* Read next pixel */ + for (i = 0; i < sinfo->pixel_size; i++) { + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + } +} + + +/* + * Read one row of pixels. + * + * We provide several different versions depending on input file format. + */ + + +METHODDEF(JDIMENSION) +get_8bit_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit grayscale pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + } + return 1; +} + +METHODDEF(JDIMENSION) +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit colormap indexes */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register int t; + register JSAMPROW ptr; + register JDIMENSION col; + register JSAMPARRAY colormap = source->colormap; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + t = UCH(source->tga_pixel[0]); + *ptr++ = colormap[0][t]; + *ptr++ = colormap[1][t]; + *ptr++ = colormap[2][t]; + } + return 1; +} + +METHODDEF(JDIMENSION) +get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 16-bit pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register int t; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + t = UCH(source->tga_pixel[0]); + t += UCH(source->tga_pixel[1]) << 8; + /* We expand 5 bit data to 8 bit sample width. + * The format of the 16-bit (LSB first) input word is + * xRRRRRGGGGGBBBBB + */ + ptr[2] = (JSAMPLE) c5to8bits[t & 0x1F]; + t >>= 5; + ptr[1] = (JSAMPLE) c5to8bits[t & 0x1F]; + t >>= 5; + ptr[0] = (JSAMPLE) c5to8bits[t & 0x1F]; + ptr += 3; + } + return 1; +} + +METHODDEF(JDIMENSION) +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 24-bit pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[2]); /* change BGR to RGB order */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[1]); + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + } + return 1; +} + +/* + * Targa also defines a 32-bit pixel format with order B,G,R,A. + * We presently ignore the attribute byte, so the code for reading + * these pixels is identical to the 24-bit routine above. + * This works because the actual pixel length is only known to read_pixel. + */ + +#define get_32bit_row get_24bit_row + + +/* + * This method is for re-reading the input data in standard top-down + * row order. The entire image has already been read into whole_image + * with proper conversion of pixel format, but it's in a funny row order. + */ + +METHODDEF(JDIMENSION) +get_memory_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + JDIMENSION source_row; + + /* Compute row of source that maps to current_row of normal order */ + /* For now, assume image is bottom-up and not interlaced. */ + /* NEEDS WORK to support interlaced images! */ + source_row = cinfo->image_height - source->current_row - 1; + + /* Fetch that row from virtual array */ + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source_row, (JDIMENSION) 1, FALSE); + + source->current_row++; + return 1; +} + + +/* + * This method loads the image into whole_image during the first call on + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + * get_memory_row on subsequent calls. + */ + +METHODDEF(JDIMENSION) +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + JDIMENSION row; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Read the data into a virtual array in input-file row order. */ + for (row = 0; row < cinfo->image_height; row++) { + if (progress != NULL) { + progress->pub.pass_counter = (long) row; + progress->pub.pass_limit = (long) cinfo->image_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, row, (JDIMENSION) 1, TRUE); + (*source->get_pixel_rows) (cinfo, sinfo); + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Set up to read from the virtual array in unscrambled order */ + source->pub.get_pixel_rows = get_memory_row; + source->current_row = 0; + /* And read the first row */ + return get_memory_row(cinfo, sinfo); +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + U_CHAR targaheader[18]; + int idlen, cmaptype, subtype, flags, interlace_type, components; + unsigned int width, height, maplen; + boolean is_bottom_up; + +#define GET_2B(offset) ((unsigned int) UCH(targaheader[offset]) + \ + (((unsigned int) UCH(targaheader[offset+1])) << 8)) + + if (! ReadOK(source->pub.input_file, targaheader, 18)) + ERREXIT(cinfo, JERR_INPUT_EOF); + + /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */ + if (targaheader[16] == 15) + targaheader[16] = 16; + + idlen = UCH(targaheader[0]); + cmaptype = UCH(targaheader[1]); + subtype = UCH(targaheader[2]); + maplen = GET_2B(5); + width = GET_2B(12); + height = GET_2B(14); + source->pixel_size = UCH(targaheader[16]) >> 3; + flags = UCH(targaheader[17]); /* Image Descriptor byte */ + + is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */ + interlace_type = flags >> 6; /* bits 6/7 are interlace code */ + + if (cmaptype > 1 || /* cmaptype must be 0 or 1 */ + source->pixel_size < 1 || source->pixel_size > 4 || + (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */ + interlace_type != 0) /* currently don't allow interlaced image */ + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + if (subtype > 8) { + /* It's an RLE-coded file */ + source->read_pixel = read_rle_pixel; + source->block_count = source->dup_pixel_count = 0; + subtype -= 8; + } else { + /* Non-RLE file */ + source->read_pixel = read_non_rle_pixel; + } + + /* Now should have subtype 1, 2, or 3 */ + components = 3; /* until proven different */ + cinfo->in_color_space = JCS_RGB; + + switch (subtype) { + case 1: /* Colormapped image */ + if (source->pixel_size == 1 && cmaptype == 1) + source->get_pixel_rows = get_8bit_row; + else + ERREXIT(cinfo, JERR_TGA_BADPARMS); + TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height); + break; + case 2: /* RGB image */ + switch (source->pixel_size) { + case 2: + source->get_pixel_rows = get_16bit_row; + break; + case 3: + source->get_pixel_rows = get_24bit_row; + break; + case 4: + source->get_pixel_rows = get_32bit_row; + break; + default: + ERREXIT(cinfo, JERR_TGA_BADPARMS); + break; + } + TRACEMS2(cinfo, 1, JTRC_TGA, width, height); + break; + case 3: /* Grayscale image */ + components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + if (source->pixel_size == 1) + source->get_pixel_rows = get_8bit_gray_row; + else + ERREXIT(cinfo, JERR_TGA_BADPARMS); + TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height); + break; + default: + ERREXIT(cinfo, JERR_TGA_BADPARMS); + break; + } + + if (is_bottom_up) { + /* Create a virtual array to buffer the upside-down image. */ + source->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1); + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + /* source->pub.buffer will point to the virtual array. */ + source->pub.buffer_height = 1; /* in case anyone looks at it */ + source->pub.get_pixel_rows = preload_image; + } else { + /* Don't need a virtual array, but do need a one-row input buffer. */ + source->whole_image = NULL; + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) width * components, (JDIMENSION) 1); + source->pub.buffer_height = 1; + source->pub.get_pixel_rows = source->get_pixel_rows; + } + + while (idlen--) /* Throw away ID field */ + (void) read_byte(source); + + if (maplen > 0) { + if (maplen > 256 || GET_2B(3) != 0) + ERREXIT(cinfo, JERR_TGA_BADCMAP); + /* Allocate space to store the colormap */ + source->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3); + /* and read it from the file */ + read_colormap(source, (int) maplen, UCH(targaheader[7])); + } else { + if (cmaptype) /* but you promised a cmap! */ + ERREXIT(cinfo, JERR_TGA_BADPARMS); + source->colormap = NULL; + } + + cinfo->input_components = components; + cinfo->data_precision = 8; + cinfo->image_width = width; + cinfo->image_height = height; +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for Targa format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_targa (j_compress_ptr cinfo) +{ + tga_source_ptr source; + + /* Create module interface object */ + source = (tga_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(tga_source_struct)); + source->cinfo = cinfo; /* make back link for subroutines */ + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_tga; + source->pub.finish_input = finish_input_tga; + + return (cjpeg_source_ptr) source; +} + +#endif /* TARGA_SUPPORTED */ diff --git a/lib/jpeg/src/rdtarga.d b/lib/jpeg/src/rdtarga.d new file mode 100644 index 0000000..3f20738 --- /dev/null +++ b/lib/jpeg/src/rdtarga.d @@ -0,0 +1,16 @@ +rdtarga.o: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/transupp.c b/lib/jpeg/src/transupp.c new file mode 100644 index 0000000..986aded --- /dev/null +++ b/lib/jpeg/src/transupp.c @@ -0,0 +1,1583 @@ +/* + * transupp.c + * + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +#include "jinclude.h" +#include "jpeglib.h" +#include "transupp.h" /* My own external interface */ +#include /* to declare isdigit() */ + + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature, + * and to Ben Jackson for introducing the cropping feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * If cropping or trimming is involved, the destination arrays may be smaller + * than the source arrays. Note it is not possible to do horizontal flip + * in-place when a nonzero Y crop offset is specified, since we'd have to move + * data from one block row to another but the virtual array manager doesn't + * guarantee we can touch more than one row at a time. So in that case, + * we have to use a separate destination array. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. When "crop" is in effect, the destination's dimensions will be the + * cropped values but the source's will be uncropped. Each transform + * routine is responsible for picking up source data starting at the + * correct X and Y offset for the crop region. (The X and Y offsets + * passed to the transform routines are measured in iMCU blocks of the + * destination.) + * 6. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + */ + + +LOCAL(void) +do_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Crop. This is only used when no rotate/flip is requested with the crop. */ +{ + JDIMENSION dst_blk_y, x_crop_blocks, y_crop_blocks; + int ci, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + jpeg_component_info *compptr; + + /* We simply have to copy the right amount of data (the destination's + * image size) starting at the given X and Y offsets in the source. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, + dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } +} + + +LOCAL(void) +do_flip_h_no_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required. + * NB: this only works when y_crop_offset is zero. + */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y, x_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + /* Do the mirroring */ + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + if (x_crop_blocks > 0) { + /* Now left-justify the portion of the data to be kept. + * We can't use a single jcopy_block_row() call because that routine + * depends on memcpy(), whose behavior is unspecified for overlapping + * source and destination areas. Sigh. + */ + for (blk_x = 0; blk_x < compptr->width_in_blocks; blk_x++) { + jcopy_block_row(buffer[offset_y] + blk_x + x_crop_blocks, + buffer[offset_y] + blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Horizontal flip in general cropping case */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Here we must output into a separate array because we can't touch + * different rows of a single virtual array simultaneously. Otherwise, + * this is essentially the same as the routine above. + */ + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Do the mirrorable blocks */ + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + *dst_ptr++ = *src_ptr++; /* copy even column */ + *dst_ptr++ = - *src_ptr++; /* copy odd column with sign change */ + } + } else { + /* Copy last partial block(s) verbatim */ + jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, + dst_row_ptr + dst_blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = srcinfo->output_height / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - y_crop_blocks - dst_blk_y - + (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + src_row_ptr += x_crop_blocks; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, + dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y, x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = srcinfo->output_height / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_width - x_crop_blocks - dst_blk_x - + (JDIMENSION) compptr->h_samp_factor, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } else { + /* Edge blocks are transposed but not mirrored. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = srcinfo->output_width / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_height / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - y_crop_blocks - dst_blk_y - + (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + dst_row_ptr = dst_buffer[offset_y]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Process the blocks that can be mirrored both ways. */ + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } else { + /* Any remaining right-edge blocks are only mirrored vertically. */ + src_ptr = src_row_ptr[x_crop_blocks + dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + src_row_ptr = src_buffer[offset_y]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Process the blocks that can be mirrored. */ + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } else { + /* Any remaining right-edge blocks are only copied. */ + jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, + dst_row_ptr + dst_blk_x, + (JDIMENSION) 1); + } + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = srcinfo->output_height / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_width / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_width - x_crop_blocks - dst_blk_x - + (JDIMENSION) compptr->h_samp_factor, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } else { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (y_crop_blocks + dst_blk_y < comp_height) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + src_ptr = src_buffer[offset_x] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Parse an unsigned integer: subroutine for jtransform_parse_crop_spec. + * Returns TRUE if valid integer found, FALSE if not. + * *strptr is advanced over the digit string, and *result is set to its value. + */ + +LOCAL(boolean) +jt_read_integer (const char ** strptr, JDIMENSION * result) +{ + const char * ptr = *strptr; + JDIMENSION val = 0; + + for (; isdigit(*ptr); ptr++) { + val = val * 10 + (JDIMENSION) (*ptr - '0'); + } + *result = val; + if (ptr == *strptr) + return FALSE; /* oops, no digits */ + *strptr = ptr; + return TRUE; +} + + +/* Parse a crop specification (written in X11 geometry style). + * The routine returns TRUE if the spec string is valid, FALSE if not. + * + * The crop spec string should have the format + * x{+-}{+-} + * where width, height, xoffset, and yoffset are unsigned integers. + * Each of the elements can be omitted to indicate a default value. + * (A weakness of this style is that it is not possible to omit xoffset + * while specifying yoffset, since they look alike.) + * + * This code is loosely based on XParseGeometry from the X11 distribution. + */ + +GLOBAL(boolean) +jtransform_parse_crop_spec (jpeg_transform_info *info, const char *spec) +{ + info->crop = FALSE; + info->crop_width_set = JCROP_UNSET; + info->crop_height_set = JCROP_UNSET; + info->crop_xoffset_set = JCROP_UNSET; + info->crop_yoffset_set = JCROP_UNSET; + + if (isdigit(*spec)) { + /* fetch width */ + if (! jt_read_integer(&spec, &info->crop_width)) + return FALSE; + info->crop_width_set = JCROP_POS; + } + if (*spec == 'x' || *spec == 'X') { + /* fetch height */ + spec++; + if (! jt_read_integer(&spec, &info->crop_height)) + return FALSE; + info->crop_height_set = JCROP_POS; + } + if (*spec == '+' || *spec == '-') { + /* fetch xoffset */ + info->crop_xoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; + spec++; + if (! jt_read_integer(&spec, &info->crop_xoffset)) + return FALSE; + } + if (*spec == '+' || *spec == '-') { + /* fetch yoffset */ + info->crop_yoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; + spec++; + if (! jt_read_integer(&spec, &info->crop_yoffset)) + return FALSE; + } + /* We had better have gotten to the end of the string. */ + if (*spec != '\0') + return FALSE; + info->crop = TRUE; + return TRUE; +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (jpeg_transform_info *info, JDIMENSION full_width) +{ + JDIMENSION MCU_cols; + + MCU_cols = info->output_width / info->iMCU_sample_width; + if (MCU_cols > 0 && info->x_crop_offset + MCU_cols == + full_width / info->iMCU_sample_width) + info->output_width = MCU_cols * info->iMCU_sample_width; +} + +LOCAL(void) +trim_bottom_edge (jpeg_transform_info *info, JDIMENSION full_height) +{ + JDIMENSION MCU_rows; + + MCU_rows = info->output_height / info->iMCU_sample_height; + if (MCU_rows > 0 && info->y_crop_offset + MCU_rows == + full_height / info->iMCU_sample_height) + info->output_height = MCU_rows * info->iMCU_sample_height; +} + + +/* Request any required workspace. + * + * This routine figures out the size that the output image will be + * (which implies that all the transform parameters must be set before + * it is called). + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + * + * This function returns FALSE right away if -perfect is given + * and transformation is not perfect. Otherwise returns TRUE. + */ + +GLOBAL(boolean) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays; + boolean need_workspace, transpose_it; + jpeg_component_info *compptr; + JDIMENSION xoffset, yoffset; + JDIMENSION width_in_iMCUs, height_in_iMCUs; + JDIMENSION width_in_blocks, height_in_blocks; + int ci, h_samp_factor, v_samp_factor; + + /* Determine number of components in output image */ + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) + /* We'll only process the first component */ + info->num_components = 1; + else + /* Process all the components */ + info->num_components = srcinfo->num_components; + + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(srcinfo); + + /* Return right away if -perfect is given and transformation is not perfect. + */ + if (info->perfect) { + if (info->num_components == 1) { + if (!jtransform_perfect_transform(srcinfo->output_width, + srcinfo->output_height, + srcinfo->min_DCT_h_scaled_size, + srcinfo->min_DCT_v_scaled_size, + info->transform)) + return FALSE; + } else { + if (!jtransform_perfect_transform(srcinfo->output_width, + srcinfo->output_height, + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size, + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size, + info->transform)) + return FALSE; + } + } + + /* If there is only one output component, force the iMCU size to be 1; + * else use the source iMCU size. (This allows us to do the right thing + * when reducing color to grayscale, and also provides a handy way of + * cleaning up "funny" grayscale images whose sampling factors are not 1x1.) + */ + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + info->output_width = srcinfo->output_height; + info->output_height = srcinfo->output_width; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_h_scaled_size; + } else { + info->iMCU_sample_width = + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + } + break; + default: + info->output_width = srcinfo->output_width; + info->output_height = srcinfo->output_height; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_v_scaled_size; + } else { + info->iMCU_sample_width = + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + } + break; + } + + /* If cropping has been requested, compute the crop area's position and + * dimensions, ensuring that its upper left corner falls at an iMCU boundary. + */ + if (info->crop) { + /* Insert default values for unset crop parameters */ + if (info->crop_xoffset_set == JCROP_UNSET) + info->crop_xoffset = 0; /* default to +0 */ + if (info->crop_yoffset_set == JCROP_UNSET) + info->crop_yoffset = 0; /* default to +0 */ + if (info->crop_xoffset >= info->output_width || + info->crop_yoffset >= info->output_height) + ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); + if (info->crop_width_set == JCROP_UNSET) + info->crop_width = info->output_width - info->crop_xoffset; + if (info->crop_height_set == JCROP_UNSET) + info->crop_height = info->output_height - info->crop_yoffset; + /* Ensure parameters are valid */ + if (info->crop_width <= 0 || info->crop_width > info->output_width || + info->crop_height <= 0 || info->crop_height > info->output_height || + info->crop_xoffset > info->output_width - info->crop_width || + info->crop_yoffset > info->output_height - info->crop_height) + ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); + /* Convert negative crop offsets into regular offsets */ + if (info->crop_xoffset_set == JCROP_NEG) + xoffset = info->output_width - info->crop_width - info->crop_xoffset; + else + xoffset = info->crop_xoffset; + if (info->crop_yoffset_set == JCROP_NEG) + yoffset = info->output_height - info->crop_height - info->crop_yoffset; + else + yoffset = info->crop_yoffset; + /* Now adjust so that upper left corner falls at an iMCU boundary */ + info->output_width = + info->crop_width + (xoffset % info->iMCU_sample_width); + info->output_height = + info->crop_height + (yoffset % info->iMCU_sample_height); + /* Save x/y offsets measured in iMCUs */ + info->x_crop_offset = xoffset / info->iMCU_sample_width; + info->y_crop_offset = yoffset / info->iMCU_sample_height; + } else { + info->x_crop_offset = 0; + info->y_crop_offset = 0; + } + + /* Figure out whether we need workspace arrays, + * and if so whether they are transposed relative to the source. + */ + need_workspace = FALSE; + transpose_it = FALSE; + switch (info->transform) { + case JXFORM_NONE: + if (info->x_crop_offset != 0 || info->y_crop_offset != 0) + need_workspace = TRUE; + /* No workspace needed if neither cropping nor transforming */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(info, srcinfo->output_width); + if (info->y_crop_offset != 0) + need_workspace = TRUE; + /* do_flip_h_no_crop doesn't need a workspace array */ + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(info, srcinfo->output_height); + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_TRANSPOSE: + /* transpose does NOT have to trim anything */ + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_TRANSVERSE: + if (info->trim) { + trim_right_edge(info, srcinfo->output_height); + trim_bottom_edge(info, srcinfo->output_width); + } + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_90: + if (info->trim) + trim_right_edge(info, srcinfo->output_height); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(info, srcinfo->output_width); + trim_bottom_edge(info, srcinfo->output_height); + } + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_ROT_270: + if (info->trim) + trim_bottom_edge(info, srcinfo->output_width); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + } + + /* Allocate workspace if needed. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + if (need_workspace) { + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + width_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_width, + (long) info->iMCU_sample_width); + height_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_height, + (long) info->iMCU_sample_height); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + if (info->num_components == 1) { + /* we're going to force samp factors to 1x1 in this case */ + h_samp_factor = v_samp_factor = 1; + } else if (transpose_it) { + h_samp_factor = compptr->v_samp_factor; + v_samp_factor = compptr->h_samp_factor; + } else { + h_samp_factor = compptr->h_samp_factor; + v_samp_factor = compptr->v_samp_factor; + } + width_in_blocks = width_in_iMCUs * h_samp_factor; + height_in_blocks = height_in_iMCUs * v_samp_factor; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); + } + info->workspace_coef_arrays = coef_arrays; + } else + info->workspace_coef_arrays = NULL; + + return TRUE; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION jtemp; + UINT16 qtemp; + + /* Transpose image dimensions */ + jtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = jtemp; + itemp = dstinfo->min_DCT_h_scaled_size; + dstinfo->min_DCT_h_scaled_size = dstinfo->min_DCT_v_scaled_size; + dstinfo->min_DCT_v_scaled_size = itemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Adjust Exif image parameters. + * + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. + */ + +LOCAL(void) +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, + JDIMENSION new_width, JDIMENSION new_height) +{ + boolean is_motorola; /* Flag for byte order */ + unsigned int number_of_tags, tagnum; + unsigned int firstoffset, offset; + JDIMENSION new_value; + + if (length < 12) return; /* Length of an IFD entry */ + + /* Discover byte order */ + if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) + is_motorola = FALSE; + else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) + is_motorola = TRUE; + else + return; + + /* Check Tag Mark */ + if (is_motorola) { + if (GETJOCTET(data[2]) != 0) return; + if (GETJOCTET(data[3]) != 0x2A) return; + } else { + if (GETJOCTET(data[3]) != 0) return; + if (GETJOCTET(data[2]) != 0x2A) return; + } + + /* Get first IFD offset (offset to IFD0) */ + if (is_motorola) { + if (GETJOCTET(data[4]) != 0) return; + if (GETJOCTET(data[5]) != 0) return; + firstoffset = GETJOCTET(data[6]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[7]); + } else { + if (GETJOCTET(data[7]) != 0) return; + if (GETJOCTET(data[6]) != 0) return; + firstoffset = GETJOCTET(data[5]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[4]); + } + if (firstoffset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this IFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[firstoffset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset+1]); + } else { + number_of_tags = GETJOCTET(data[firstoffset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset]); + } + if (number_of_tags == 0) return; + firstoffset += 2; + + /* Search for ExifSubIFD offset Tag in IFD0 */ + for (;;) { + if (firstoffset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[firstoffset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset+1]); + } else { + tagnum = GETJOCTET(data[firstoffset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset]); + } + if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */ + if (--number_of_tags == 0) return; + firstoffset += 12; + } + + /* Get the ExifSubIFD offset */ + if (is_motorola) { + if (GETJOCTET(data[firstoffset+8]) != 0) return; + if (GETJOCTET(data[firstoffset+9]) != 0) return; + offset = GETJOCTET(data[firstoffset+10]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+11]); + } else { + if (GETJOCTET(data[firstoffset+11]) != 0) return; + if (GETJOCTET(data[firstoffset+10]) != 0) return; + offset = GETJOCTET(data[firstoffset+9]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+8]); + } + if (offset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this SubIFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[offset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset+1]); + } else { + number_of_tags = GETJOCTET(data[offset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset]); + } + if (number_of_tags < 2) return; + offset += 2; + + /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */ + do { + if (offset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[offset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset+1]); + } else { + tagnum = GETJOCTET(data[offset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset]); + } + if (tagnum == 0xA002 || tagnum == 0xA003) { + if (tagnum == 0xA002) + new_value = new_width; /* ExifImageWidth Tag */ + else + new_value = new_height; /* ExifImageHeight Tag */ + if (is_motorola) { + data[offset+2] = 0; /* Format = unsigned long (4 octets) */ + data[offset+3] = 4; + data[offset+4] = 0; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 1; + data[offset+8] = 0; + data[offset+9] = 0; + data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+11] = (JOCTET)(new_value & 0xFF); + } else { + data[offset+2] = 4; /* Format = unsigned long (4 octets) */ + data[offset+3] = 0; + data[offset+4] = 1; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 0; + data[offset+8] = (JOCTET)(new_value & 0xFF); + data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+10] = 0; + data[offset+11] = 0; + } + } + offset += 12; + } while (--number_of_tags); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* First, ensure we have YCbCr or grayscale data, and that the source's + * Y channel is full resolution. (No reasonable person would make Y + * be less than full resolution, so actually coping with that case + * isn't worth extra code space. But we check it to avoid crashing.) + */ + if (((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) && + srcinfo->comp_info[0].h_samp_factor == srcinfo->max_h_samp_factor && + srcinfo->comp_info[0].v_samp_factor == srcinfo->max_v_samp_factor) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, it sets the target h_samp_factor & + * v_samp_factor to 1, which typically won't match the source. + * We have to preserve the source's quantization table number, however. + */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } else if (info->num_components == 1) { + /* For a single-component source, we force the destination sampling factors + * to 1x1, with or without force_grayscale. This is useful because some + * decoders choke on grayscale images with other sampling factors. + */ + dstinfo->comp_info[0].h_samp_factor = 1; + dstinfo->comp_info[0].v_samp_factor = 1; + } + + /* Correct the destination's image dimensions as necessary + * for rotate/flip, resize, and crop operations. + */ + dstinfo->jpeg_width = info->output_width; + dstinfo->jpeg_height = info->output_height; + + /* Transpose destination image parameters */ + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + break; + default: + break; + } + + /* Adjust Exif properties */ + if (srcinfo->marker_list != NULL && + srcinfo->marker_list->marker == JPEG_APP0+1 && + srcinfo->marker_list->data_length >= 6 && + GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && + GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && + GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && + GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && + GETJOCTET(srcinfo->marker_list->data[4]) == 0 && + GETJOCTET(srcinfo->marker_list->data[5]) == 0) { + /* Suppress output of JFIF marker */ + dstinfo->write_JFIF_header = FALSE; + /* Adjust Exif image parameters */ + if (dstinfo->jpeg_width != srcinfo->image_width || + dstinfo->jpeg_height != srcinfo->image_height) + /* Align data segment to start of TIFF structure for parsing */ + adjust_exif_parameters(srcinfo->marker_list->data + 6, + srcinfo->marker_list->data_length - 6, + dstinfo->jpeg_width, dstinfo->jpeg_height); + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transform (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + /* Note: conditions tested here should match those in switch statement + * in jtransform_request_workspace() + */ + switch (info->transform) { + case JXFORM_NONE: + if (info->x_crop_offset != 0 || info->y_crop_offset != 0) + do_crop(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_FLIP_H: + if (info->y_crop_offset != 0) + do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + else + do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset, + src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + } +} + +/* jtransform_perfect_transform + * + * Determine whether lossless transformation is perfectly + * possible for a specified image and transformation. + * + * Inputs: + * image_width, image_height: source image dimensions. + * MCU_width, MCU_height: pixel dimensions of MCU. + * transform: transformation identifier. + * Parameter sources from initialized jpeg_struct + * (after reading source header): + * image_width = cinfo.image_width + * image_height = cinfo.image_height + * MCU_width = cinfo.max_h_samp_factor * cinfo.block_size + * MCU_height = cinfo.max_v_samp_factor * cinfo.block_size + * Result: + * TRUE = perfect transformation possible + * FALSE = perfect transformation not possible + * (may use custom action then) + */ + +GLOBAL(boolean) +jtransform_perfect_transform(JDIMENSION image_width, JDIMENSION image_height, + int MCU_width, int MCU_height, + JXFORM_CODE transform) +{ + boolean result = TRUE; /* initialize TRUE */ + + switch (transform) { + case JXFORM_FLIP_H: + case JXFORM_ROT_270: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_90: + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + case JXFORM_TRANSVERSE: + case JXFORM_ROT_180: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + default: + break; + } + + return result; +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} diff --git a/lib/jpeg/src/transupp.d b/lib/jpeg/src/transupp.d new file mode 100644 index 0000000..0931521 --- /dev/null +++ b/lib/jpeg/src/transupp.d @@ -0,0 +1,16 @@ +transupp.o: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jpegint.h jerror.h transupp.h + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jpegint.h: + +jerror.h: + +transupp.h: diff --git a/lib/jpeg/src/transupp.h b/lib/jpeg/src/transupp.h new file mode 100644 index 0000000..5206e1d --- /dev/null +++ b/lib/jpeg/src/transupp.h @@ -0,0 +1,210 @@ +/* + * transupp.h + * + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a lossless-crop option, which discards data outside a given + * image region but losslessly preserves what is inside. Like the rotate and + * flip transforms, lossless crop is restricted by the JPEG format: the upper + * left corner of the selected region must fall on an iMCU boundary. If this + * does not hold for the given crop parameters, we silently move the upper left + * corner up and/or left to make it so, simultaneously increasing the region + * dimensions to keep the lower right crop corner unchanged. (Thus, the + * output image covers at least the requested region, but may cover more.) + * + * We also provide a lossless-resize option, which is kind of a lossless-crop + * operation in the DCT coefficient block domain - it discards higher-order + * coefficients and losslessly preserves lower-order coefficients of a + * sub-block. + * + * Rotate/flip transform, resize, and crop can be requested together in a + * single invocation. The crop is applied last --- that is, the crop region + * is specified in terms of the destination image after transform/resize. + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_parse_crop_spec jTrParCrop +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transform jTrExec +#define jtransform_perfect_transform jTrPerfect +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Codes for crop parameters, which can individually be unspecified, + * positive, or negative. (Negative width or height makes no sense, though.) + */ + +typedef enum { + JCROP_UNSET, + JCROP_POS, + JCROP_NEG +} JCROP_CODE; + +/* + * Transform parameters struct. + * NB: application must not change any elements of this struct after + * calling jtransform_request_workspace. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean perfect; /* if TRUE, fail if partial MCUs are requested */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + boolean crop; /* if TRUE, crop source image */ + + /* Crop parameters: application need not set these unless crop is TRUE. + * These can be filled in by jtransform_parse_crop_spec(). + */ + JDIMENSION crop_width; /* Width of selected region */ + JCROP_CODE crop_width_set; + JDIMENSION crop_height; /* Height of selected region */ + JCROP_CODE crop_height_set; + JDIMENSION crop_xoffset; /* X offset of selected region */ + JCROP_CODE crop_xoffset_set; /* (negative measures from right edge) */ + JDIMENSION crop_yoffset; /* Y offset of selected region */ + JCROP_CODE crop_yoffset_set; /* (negative measures from bottom edge) */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ + JDIMENSION output_width; /* cropped destination dimensions */ + JDIMENSION output_height; + JDIMENSION x_crop_offset; /* destination crop offsets measured in iMCUs */ + JDIMENSION y_crop_offset; + int iMCU_sample_width; /* destination iMCU size */ + int iMCU_sample_height; +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Parse a crop specification (written in X11 geometry style) */ +EXTERN(boolean) jtransform_parse_crop_spec + JPP((jpeg_transform_info *info, const char *spec)); +/* Request any required workspace */ +EXTERN(boolean) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transform + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Determine whether lossless transformation is perfectly + * possible for a specified image and transformation. + */ +EXTERN(boolean) jtransform_perfect_transform + JPP((JDIMENSION image_width, JDIMENSION image_height, + int MCU_width, int MCU_height, + JXFORM_CODE transform)); + +/* jtransform_execute_transform used to be called + * jtransform_execute_transformation, but some compilers complain about + * routine names that long. This macro is here to avoid breaking any + * old source code that uses the original name... + */ +#define jtransform_execute_transformation jtransform_execute_transform + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); diff --git a/lib/jpeg/src/wrbmp.c b/lib/jpeg/src/wrbmp.c new file mode 100644 index 0000000..2b8146e --- /dev/null +++ b/lib/jpeg/src/wrbmp.c @@ -0,0 +1,442 @@ +/* + * wrbmp.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in Microsoft "BMP" + * format (MS Windows 3.x and OS/2 1.x flavors). + * Either 8-bit colormapped or 24-bit full-color format can be written. + * No compression is supported. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * This code contributed by James Arthur Boucher. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef BMP_SUPPORTED + + +/* + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + * This is not yet implemented. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * Since BMP stores scanlines bottom-to-top, we have to invert the image + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + * in a virtual array during put_pixel_row calls, then actually emit the + * BMP file during finish_output. The virtual array contains one JSAMPLE per + * pixel if the output is grayscale or colormapped, three if it is full color. + */ + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + boolean is_os2; /* saves the OS2 format request flag */ + + jvirt_sarray_ptr whole_image; /* needed to reverse row order */ + JDIMENSION data_width; /* JSAMPLEs per row */ + JDIMENSION row_width; /* physical width of one row in the BMP file */ + int pad_bytes; /* number of padding bytes needed per row */ + JDIMENSION cur_output_row; /* next row# to write to virtual array */ +} bmp_dest_struct; + +typedef bmp_dest_struct * bmp_dest_ptr; + + +/* Forward declarations */ +LOCAL(void) write_colormap + JPP((j_decompress_ptr cinfo, bmp_dest_ptr dest, + int map_colors, int map_entry_size)); + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* This version is for writing 24-bit pixels */ +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + int pad; + + /* Access next row in virtual array */ + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, + dest->cur_output_row, (JDIMENSION) 1, TRUE); + dest->cur_output_row++; + + /* Transfer data. Note destination values must be in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = dest->pub.buffer[0]; + outptr = image_ptr[0]; + for (col = cinfo->output_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + outptr += 3; + } + + /* Zero out the pad bytes. */ + pad = dest->pad_bytes; + while (--pad >= 0) + *outptr++ = 0; +} + +METHODDEF(void) +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* This version is for grayscale OR quantized color output */ +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + int pad; + + /* Access next row in virtual array */ + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, + dest->cur_output_row, (JDIMENSION) 1, TRUE); + dest->cur_output_row++; + + /* Transfer data. */ + inptr = dest->pub.buffer[0]; + outptr = image_ptr[0]; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = *inptr++; /* can omit GETJSAMPLE() safely */ + } + + /* Zero out the pad bytes. */ + pad = dest->pad_bytes; + while (--pad >= 0) + *outptr++ = 0; +} + + +/* + * Startup: normally writes the file header. + * In this module we may as well postpone everything until finish_output. + */ + +METHODDEF(void) +start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* no work here */ +} + + +/* + * Finish up at the end of the file. + * + * Here is where we really output the BMP file. + * + * First, routines to write the Windows and OS/2 variants of the file header. + */ + +LOCAL(void) +write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) +/* Write a Windows-style BMP file header, including colormap if needed */ +{ + char bmpfileheader[14]; + char bmpinfoheader[40]; +#define PUT_2B(array,offset,value) \ + (array[offset] = (char) ((value) & 0xFF), \ + array[offset+1] = (char) (((value) >> 8) & 0xFF)) +#define PUT_4B(array,offset,value) \ + (array[offset] = (char) ((value) & 0xFF), \ + array[offset+1] = (char) (((value) >> 8) & 0xFF), \ + array[offset+2] = (char) (((value) >> 16) & 0xFF), \ + array[offset+3] = (char) (((value) >> 24) & 0xFF)) + INT32 headersize, bfSize; + int bits_per_pixel, cmap_entries; + + /* Compute colormap size and total file size */ + if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* Colormapped RGB */ + bits_per_pixel = 8; + cmap_entries = 256; + } else { + /* Unquantized, full color RGB */ + bits_per_pixel = 24; + cmap_entries = 0; + } + } else { + /* Grayscale output. We need to fake a 256-entry colormap. */ + bits_per_pixel = 8; + cmap_entries = 256; + } + /* File size */ + headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */ + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + /* Set unused fields of header to 0 */ + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader)); + + /* Fill the file header */ + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + bmpfileheader[1] = 0x4D; + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + /* we leave bfReserved1 & bfReserved2 = 0 */ + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ + PUT_2B(bmpinfoheader, 0, 40); /* biSize */ + PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */ + PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */ + PUT_2B(bmpinfoheader, 12, 1); /* biPlanes - must be 1 */ + PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */ + /* we leave biCompression = 0, for none */ + /* we leave biSizeImage = 0; this is correct for uncompressed data */ + if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */ + PUT_4B(bmpinfoheader, 24, (INT32) (cinfo->X_density*100)); /* XPels/M */ + PUT_4B(bmpinfoheader, 28, (INT32) (cinfo->Y_density*100)); /* XPels/M */ + } + PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */ + /* we leave biClrImportant = 0 */ + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + ERREXIT(cinfo, JERR_FILE_WRITE); + if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40) + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (cmap_entries > 0) + write_colormap(cinfo, dest, cmap_entries, 4); +} + + +LOCAL(void) +write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) +/* Write an OS2-style BMP file header, including colormap if needed */ +{ + char bmpfileheader[14]; + char bmpcoreheader[12]; + INT32 headersize, bfSize; + int bits_per_pixel, cmap_entries; + + /* Compute colormap size and total file size */ + if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* Colormapped RGB */ + bits_per_pixel = 8; + cmap_entries = 256; + } else { + /* Unquantized, full color RGB */ + bits_per_pixel = 24; + cmap_entries = 0; + } + } else { + /* Grayscale output. We need to fake a 256-entry colormap. */ + bits_per_pixel = 8; + cmap_entries = 256; + } + /* File size */ + headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */ + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + /* Set unused fields of header to 0 */ + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader)); + + /* Fill the file header */ + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + bmpfileheader[1] = 0x4D; + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + /* we leave bfReserved1 & bfReserved2 = 0 */ + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */ + PUT_2B(bmpcoreheader, 0, 12); /* bcSize */ + PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */ + PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */ + PUT_2B(bmpcoreheader, 8, 1); /* bcPlanes - must be 1 */ + PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */ + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + ERREXIT(cinfo, JERR_FILE_WRITE); + if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12) + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (cmap_entries > 0) + write_colormap(cinfo, dest, cmap_entries, 3); +} + + +/* + * Write the colormap. + * Windows uses BGR0 map entries; OS/2 uses BGR entries. + */ + +LOCAL(void) +write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest, + int map_colors, int map_entry_size) +{ + JSAMPARRAY colormap = cinfo->colormap; + int num_colors = cinfo->actual_number_of_colors; + FILE * outfile = dest->pub.output_file; + int i; + + if (colormap != NULL) { + if (cinfo->out_color_components == 3) { + /* Normal case with RGB colormap */ + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(colormap[2][i]), outfile); + putc(GETJSAMPLE(colormap[1][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } else { + /* Grayscale colormap (only happens with grayscale quantization) */ + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(colormap[0][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } + } else { + /* If no colormap, must be grayscale data. Generate a linear "map". */ + for (i = 0; i < 256; i++) { + putc(i, outfile); + putc(i, outfile); + putc(i, outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } + /* Pad colormap with zeros to ensure specified number of colormap entries */ + if (i > map_colors) + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i); + for (; i < map_colors; i++) { + putc(0, outfile); + putc(0, outfile); + putc(0, outfile); + if (map_entry_size == 4) + putc(0, outfile); + } +} + + +METHODDEF(void) +finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + register FILE * outfile = dest->pub.output_file; + JSAMPARRAY image_ptr; + register JSAMPROW data_ptr; + JDIMENSION row; + register JDIMENSION col; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Write the header and colormap */ + if (dest->is_os2) + write_os2_header(cinfo, dest); + else + write_bmp_header(cinfo, dest); + + /* Write the file body from our virtual array */ + for (row = cinfo->output_height; row > 0; row--) { + if (progress != NULL) { + progress->pub.pass_counter = (long) (cinfo->output_height - row); + progress->pub.pass_limit = (long) cinfo->output_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE); + data_ptr = image_ptr[0]; + for (col = dest->row_width; col > 0; col--) { + putc(GETJSAMPLE(*data_ptr), outfile); + data_ptr++; + } + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Make sure we wrote the output file OK */ + fflush(outfile); + if (ferror(outfile)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for BMP format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2) +{ + bmp_dest_ptr dest; + JDIMENSION row_width; + + /* Create module interface object, fill in method pointers */ + dest = (bmp_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(bmp_dest_struct)); + dest->pub.start_output = start_output_bmp; + dest->pub.finish_output = finish_output_bmp; + dest->is_os2 = is_os2; + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + dest->pub.put_pixel_rows = put_gray_rows; + } else if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) + dest->pub.put_pixel_rows = put_gray_rows; + else + dest->pub.put_pixel_rows = put_pixel_rows; + } else { + ERREXIT(cinfo, JERR_BMP_COLORSPACE); + } + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Determine width of rows in the BMP file (padded to 4-byte boundary). */ + row_width = cinfo->output_width * cinfo->output_components; + dest->data_width = row_width; + while ((row_width & 3) != 0) row_width++; + dest->row_width = row_width; + dest->pad_bytes = (int) (row_width - dest->data_width); + + /* Allocate space for inversion array, prepare for write pass */ + dest->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + row_width, cinfo->output_height, (JDIMENSION) 1); + dest->cur_output_row = 0; + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* BMP_SUPPORTED */ diff --git a/lib/jpeg/src/wrbmp.d b/lib/jpeg/src/wrbmp.d new file mode 100644 index 0000000..f269a3c --- /dev/null +++ b/lib/jpeg/src/wrbmp.d @@ -0,0 +1,16 @@ +wrbmp.o: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/wrgif.c b/lib/jpeg/src/wrgif.c new file mode 100644 index 0000000..13f953b --- /dev/null +++ b/lib/jpeg/src/wrgif.c @@ -0,0 +1,399 @@ +/* + * wrgif.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in GIF format. + * + ************************************************************************** + * NOTE: to avoid entanglements with Unisys' patent on LZW compression, * + * this code has been modified to output "uncompressed GIF" files. * + * There is no trace of the LZW algorithm in this file. * + ************************************************************************** + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + */ + +/* + * This code is loosely based on ppmtogif from the PBMPLUS distribution + * of Feb. 1991. That file contains the following copyright notice: + * Based on GIFENCODE by David Rowley . + * Lempel-Ziv compression based on "compress" by Spencer W. Thomas et al. + * Copyright (C) 1989 by Jef Poskanzer. + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * We are also required to state that + * "The Graphics Interchange Format(c) is the Copyright property of + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + * CompuServe Incorporated." + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef GIF_SUPPORTED + + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + j_decompress_ptr cinfo; /* back link saves passing separate parm */ + + /* State for packing variable-width codes into a bitstream */ + int n_bits; /* current number of bits/code */ + int maxcode; /* maximum code, given n_bits */ + INT32 cur_accum; /* holds bits not yet output */ + int cur_bits; /* # of bits in cur_accum */ + + /* State for GIF code assignment */ + int ClearCode; /* clear code (doesn't change) */ + int EOFCode; /* EOF code (ditto) */ + int code_counter; /* counts output symbols */ + + /* GIF data packet construction buffer */ + int bytesinpkt; /* # of bytes in current packet */ + char packetbuf[256]; /* workspace for accumulating packet */ + +} gif_dest_struct; + +typedef gif_dest_struct * gif_dest_ptr; + +/* Largest value that will fit in N bits */ +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + + +/* + * Routines to package finished data bytes into GIF data blocks. + * A data block consists of a count byte (1..255) and that many data bytes. + */ + +LOCAL(void) +flush_packet (gif_dest_ptr dinfo) +/* flush any accumulated data */ +{ + if (dinfo->bytesinpkt > 0) { /* never write zero-length packet */ + dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++; + if (JFWRITE(dinfo->pub.output_file, dinfo->packetbuf, dinfo->bytesinpkt) + != (size_t) dinfo->bytesinpkt) + ERREXIT(dinfo->cinfo, JERR_FILE_WRITE); + dinfo->bytesinpkt = 0; + } +} + + +/* Add a character to current packet; flush to disk if necessary */ +#define CHAR_OUT(dinfo,c) \ + { (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (c); \ + if ((dinfo)->bytesinpkt >= 255) \ + flush_packet(dinfo); \ + } + + +/* Routine to convert variable-width codes into a byte stream */ + +LOCAL(void) +output (gif_dest_ptr dinfo, int code) +/* Emit a code of n_bits bits */ +/* Uses cur_accum and cur_bits to reblock into 8-bit bytes */ +{ + dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits; + dinfo->cur_bits += dinfo->n_bits; + + while (dinfo->cur_bits >= 8) { + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + dinfo->cur_accum >>= 8; + dinfo->cur_bits -= 8; + } +} + + +/* The pseudo-compression algorithm. + * + * In this module we simply output each pixel value as a separate symbol; + * thus, no compression occurs. In fact, there is expansion of one bit per + * pixel, because we use a symbol width one bit wider than the pixel width. + * + * GIF ordinarily uses variable-width symbols, and the decoder will expect + * to ratchet up the symbol width after a fixed number of symbols. + * To simplify the logic and keep the expansion penalty down, we emit a + * GIF Clear code to reset the decoder just before the width would ratchet up. + * Thus, all the symbols in the output file will have the same bit width. + * Note that emitting the Clear codes at the right times is a mere matter of + * counting output symbols and is in no way dependent on the LZW patent. + * + * With a small basic pixel width (low color count), Clear codes will be + * needed very frequently, causing the file to expand even more. So this + * simplistic approach wouldn't work too well on bilevel images, for example. + * But for output of JPEG conversions the pixel width will usually be 8 bits + * (129 to 256 colors), so the overhead added by Clear symbols is only about + * one symbol in every 256. + */ + +LOCAL(void) +compress_init (gif_dest_ptr dinfo, int i_bits) +/* Initialize pseudo-compressor */ +{ + /* init all the state variables */ + dinfo->n_bits = i_bits; + dinfo->maxcode = MAXCODE(dinfo->n_bits); + dinfo->ClearCode = (1 << (i_bits - 1)); + dinfo->EOFCode = dinfo->ClearCode + 1; + dinfo->code_counter = dinfo->ClearCode + 2; + /* init output buffering vars */ + dinfo->bytesinpkt = 0; + dinfo->cur_accum = 0; + dinfo->cur_bits = 0; + /* GIF specifies an initial Clear code */ + output(dinfo, dinfo->ClearCode); +} + + +LOCAL(void) +compress_pixel (gif_dest_ptr dinfo, int c) +/* Accept and "compress" one pixel value. + * The given value must be less than n_bits wide. + */ +{ + /* Output the given pixel value as a symbol. */ + output(dinfo, c); + /* Issue Clear codes often enough to keep the reader from ratcheting up + * its symbol size. + */ + if (dinfo->code_counter < dinfo->maxcode) { + dinfo->code_counter++; + } else { + output(dinfo, dinfo->ClearCode); + dinfo->code_counter = dinfo->ClearCode + 2; /* reset the counter */ + } +} + + +LOCAL(void) +compress_term (gif_dest_ptr dinfo) +/* Clean up at end */ +{ + /* Send an EOF code */ + output(dinfo, dinfo->EOFCode); + /* Flush the bit-packing buffer */ + if (dinfo->cur_bits > 0) { + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + } + /* Flush the packet buffer */ + flush_packet(dinfo); +} + + +/* GIF header construction */ + + +LOCAL(void) +put_word (gif_dest_ptr dinfo, unsigned int w) +/* Emit a 16-bit word, LSB first */ +{ + putc(w & 0xFF, dinfo->pub.output_file); + putc((w >> 8) & 0xFF, dinfo->pub.output_file); +} + + +LOCAL(void) +put_3bytes (gif_dest_ptr dinfo, int val) +/* Emit 3 copies of same byte value --- handy subr for colormap construction */ +{ + putc(val, dinfo->pub.output_file); + putc(val, dinfo->pub.output_file); + putc(val, dinfo->pub.output_file); +} + + +LOCAL(void) +emit_header (gif_dest_ptr dinfo, int num_colors, JSAMPARRAY colormap) +/* Output the GIF file header, including color map */ +/* If colormap==NULL, synthesize a gray-scale colormap */ +{ + int BitsPerPixel, ColorMapSize, InitCodeSize, FlagByte; + int cshift = dinfo->cinfo->data_precision - 8; + int i; + + if (num_colors > 256) + ERREXIT1(dinfo->cinfo, JERR_TOO_MANY_COLORS, num_colors); + /* Compute bits/pixel and related values */ + BitsPerPixel = 1; + while (num_colors > (1 << BitsPerPixel)) + BitsPerPixel++; + ColorMapSize = 1 << BitsPerPixel; + if (BitsPerPixel <= 1) + InitCodeSize = 2; + else + InitCodeSize = BitsPerPixel; + /* + * Write the GIF header. + * Note that we generate a plain GIF87 header for maximum compatibility. + */ + putc('G', dinfo->pub.output_file); + putc('I', dinfo->pub.output_file); + putc('F', dinfo->pub.output_file); + putc('8', dinfo->pub.output_file); + putc('7', dinfo->pub.output_file); + putc('a', dinfo->pub.output_file); + /* Write the Logical Screen Descriptor */ + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + FlagByte = 0x80; /* Yes, there is a global color table */ + FlagByte |= (BitsPerPixel-1) << 4; /* color resolution */ + FlagByte |= (BitsPerPixel-1); /* size of global color table */ + putc(FlagByte, dinfo->pub.output_file); + putc(0, dinfo->pub.output_file); /* Background color index */ + putc(0, dinfo->pub.output_file); /* Reserved (aspect ratio in GIF89) */ + /* Write the Global Color Map */ + /* If the color map is more than 8 bits precision, */ + /* we reduce it to 8 bits by shifting */ + for (i=0; i < ColorMapSize; i++) { + if (i < num_colors) { + if (colormap != NULL) { + if (dinfo->cinfo->out_color_space == JCS_RGB) { + /* Normal case: RGB color map */ + putc(GETJSAMPLE(colormap[0][i]) >> cshift, dinfo->pub.output_file); + putc(GETJSAMPLE(colormap[1][i]) >> cshift, dinfo->pub.output_file); + putc(GETJSAMPLE(colormap[2][i]) >> cshift, dinfo->pub.output_file); + } else { + /* Grayscale "color map": possible if quantizing grayscale image */ + put_3bytes(dinfo, GETJSAMPLE(colormap[0][i]) >> cshift); + } + } else { + /* Create a gray-scale map of num_colors values, range 0..255 */ + put_3bytes(dinfo, (i * 255 + (num_colors-1)/2) / (num_colors-1)); + } + } else { + /* fill out the map to a power of 2 */ + put_3bytes(dinfo, 0); + } + } + /* Write image separator and Image Descriptor */ + putc(',', dinfo->pub.output_file); /* separator */ + put_word(dinfo, 0); /* left/top offset */ + put_word(dinfo, 0); + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); /* image size */ + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + /* flag byte: not interlaced, no local color map */ + putc(0x00, dinfo->pub.output_file); + /* Write Initial Code Size byte */ + putc(InitCodeSize, dinfo->pub.output_file); + + /* Initialize for "compression" of image data */ + compress_init(dinfo, InitCodeSize+1); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + if (cinfo->quantize_colors) + emit_header(dest, cinfo->actual_number_of_colors, cinfo->colormap); + else + emit_header(dest, 256, (JSAMPARRAY) NULL); +} + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + for (col = cinfo->output_width; col > 0; col--) { + compress_pixel(dest, GETJSAMPLE(*ptr++)); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + /* Flush "compression" mechanism */ + compress_term(dest); + /* Write a zero-length data block to end the series */ + putc(0, dest->pub.output_file); + /* Write the GIF terminator mark */ + putc(';', dest->pub.output_file); + /* Make sure we wrote the output file OK */ + fflush(dest->pub.output_file); + if (ferror(dest->pub.output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for GIF format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_gif (j_decompress_ptr cinfo) +{ + gif_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (gif_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(gif_dest_struct)); + dest->cinfo = cinfo; /* make back link for subroutines */ + dest->pub.start_output = start_output_gif; + dest->pub.put_pixel_rows = put_pixel_rows; + dest->pub.finish_output = finish_output_gif; + + if (cinfo->out_color_space != JCS_GRAYSCALE && + cinfo->out_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_GIF_COLORSPACE); + + /* Force quantization if color or if > 8 bits input */ + if (cinfo->out_color_space != JCS_GRAYSCALE || cinfo->data_precision > 8) { + /* Force quantization to at most 256 colors */ + cinfo->quantize_colors = TRUE; + if (cinfo->desired_number_of_colors > 256) + cinfo->desired_number_of_colors = 256; + } + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + if (cinfo->output_components != 1) /* safety check: just one component? */ + ERREXIT(cinfo, JERR_GIF_BUG); + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, cinfo->output_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* GIF_SUPPORTED */ diff --git a/lib/jpeg/src/wrgif.d b/lib/jpeg/src/wrgif.d new file mode 100644 index 0000000..accdb8b --- /dev/null +++ b/lib/jpeg/src/wrgif.d @@ -0,0 +1,16 @@ +wrgif.o: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/wrppm.c b/lib/jpeg/src/wrppm.c new file mode 100644 index 0000000..1e56d9c --- /dev/null +++ b/lib/jpeg/src/wrppm.c @@ -0,0 +1,269 @@ +/* + * wrppm.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * Modified 2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in PPM/PGM format. + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + * The PBMPLUS library is NOT required to compile this software + * (but it is highly useful as a set of PPM image manipulation programs). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef PPM_SUPPORTED + + +/* + * For 12-bit JPEG data, we either downscale the values to 8 bits + * (to write standard byte-per-sample PPM/PGM files), or output + * nonstandard word-per-sample PPM/PGM files. Downscaling is done + * if PPM_NORAWWORD is defined (this can be done in the Makefile + * or in jconfig.h). + * (When the core library supports data precision reduction, a cleaner + * implementation will be to ask for that instead.) + */ + +#if BITS_IN_JSAMPLE == 8 +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) (v) +#define BYTESPERSAMPLE 1 +#define PPM_MAXVAL 255 +#else +#ifdef PPM_NORAWWORD +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) ((v) >> (BITS_IN_JSAMPLE-8)) +#define BYTESPERSAMPLE 1 +#define PPM_MAXVAL 255 +#else +/* The word-per-sample format always puts the MSB first. */ +#define PUTPPMSAMPLE(ptr,v) \ + { register int val_ = v; \ + *ptr++ = (char) ((val_ >> 8) & 0xFF); \ + *ptr++ = (char) (val_ & 0xFF); \ + } +#define BYTESPERSAMPLE 2 +#define PPM_MAXVAL ((1<pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * This code is used when we have to copy the data and apply a pixel + * format translation. Typically this only happens in 12-bit mode. + */ + +METHODDEF(void) +copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = dest->samples_per_row; col > 0; col--) { + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(*ptr++)); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Write some pixel data when color quantization is in effect. + * We have to demap the color index values to straight data. + */ + +METHODDEF(void) +put_demapped_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register int pixval; + register JSAMPROW ptr; + register JSAMPROW color_map0 = cinfo->colormap[0]; + register JSAMPROW color_map1 = cinfo->colormap[1]; + register JSAMPROW color_map2 = cinfo->colormap[2]; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + pixval = GETJSAMPLE(*ptr++); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map0[pixval])); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map1[pixval])); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map2[pixval])); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +METHODDEF(void) +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register JSAMPROW ptr; + register JSAMPROW color_map = cinfo->colormap[0]; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map[GETJSAMPLE(*ptr++)])); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + /* Emit file header */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + /* emit header for raw PGM format */ + fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n", + (long) cinfo->output_width, (long) cinfo->output_height, + PPM_MAXVAL); + break; + case JCS_RGB: + /* emit header for raw PPM format */ + fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n", + (long) cinfo->output_width, (long) cinfo->output_height, + PPM_MAXVAL); + break; + default: + ERREXIT(cinfo, JERR_PPM_COLORSPACE); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* Make sure we wrote the output file OK */ + fflush(dinfo->output_file); + if (ferror(dinfo->output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for PPM format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_ppm (j_decompress_ptr cinfo) +{ + ppm_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (ppm_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ppm_dest_struct)); + dest->pub.start_output = start_output_ppm; + dest->pub.finish_output = finish_output_ppm; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Create physical I/O buffer. Note we make this near on a PC. */ + dest->samples_per_row = cinfo->output_width * cinfo->out_color_components; + dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * SIZEOF(char)); + dest->iobuffer = (char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width); + + if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 || + SIZEOF(JSAMPLE) != SIZEOF(char)) { + /* When quantizing, we need an output buffer for colormap indexes + * that's separate from the physical I/O buffer. We also need a + * separate buffer if pixel format translation must take place. + */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->output_components, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + if (! cinfo->quantize_colors) + dest->pub.put_pixel_rows = copy_pixel_rows; + else if (cinfo->out_color_space == JCS_GRAYSCALE) + dest->pub.put_pixel_rows = put_demapped_gray; + else + dest->pub.put_pixel_rows = put_demapped_rgb; + } else { + /* We will fwrite() directly from decompressor output buffer. */ + /* Synthesize a JSAMPARRAY pointer structure */ + /* Cast here implies near->far pointer conversion on PCs */ + dest->pixrow = (JSAMPROW) dest->iobuffer; + dest->pub.buffer = & dest->pixrow; + dest->pub.buffer_height = 1; + dest->pub.put_pixel_rows = put_pixel_rows; + } + + return (djpeg_dest_ptr) dest; +} + +#endif /* PPM_SUPPORTED */ diff --git a/lib/jpeg/src/wrppm.d b/lib/jpeg/src/wrppm.d new file mode 100644 index 0000000..2286e0c --- /dev/null +++ b/lib/jpeg/src/wrppm.d @@ -0,0 +1,16 @@ +wrppm.o: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/wrrle.c b/lib/jpeg/src/wrrle.c new file mode 100644 index 0000000..7a00c0d --- /dev/null +++ b/lib/jpeg/src/wrrle.c @@ -0,0 +1,305 @@ +/* + * wrrle.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in RLE format. + * The Utah Raster Toolkit library is required (version 3.1 or later). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * Based on code contributed by Mike Lijewski, + * with updates from Robert Hutchinson. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef RLE_SUPPORTED + +/* rle.h is provided by the Utah Raster Toolkit. */ + +#include + +/* + * We assume that JSAMPLE has the same representation as rle_pixel, + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + + +/* + * Since RLE stores scanlines bottom-to-top, we have to invert the image + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + * in a virtual array during put_pixel_row calls, then actually emit the + * RLE file during finish_output. + */ + + +/* + * For now, if we emit an RLE color map then it is always 256 entries long, + * though not all of the entries need be used. + */ + +#define CMAPBITS 8 +#define CMAPLENGTH (1<<(CMAPBITS)) + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + jvirt_sarray_ptr image; /* virtual array to store the output image */ + rle_map *colormap; /* RLE-style color map, or NULL if none */ + rle_pixel **rle_row; /* To pass rows to rle_putrow() */ + +} rle_dest_struct; + +typedef rle_dest_struct * rle_dest_ptr; + +/* Forward declarations */ +METHODDEF(void) rle_put_pixel_rows + JPP((j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied)); + + +/* + * Write the file header. + * + * In this module it's easier to wait till finish_output to write anything. + */ + +METHODDEF(void) +start_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + size_t cmapsize; + int i, ci; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* + * Make sure the image can be stored in RLE format. + * + * - RLE stores image dimensions as *signed* 16 bit integers. JPEG + * uses unsigned, so we have to check the width. + * + * - Colorspace is expected to be grayscale or RGB. + * + * - The number of channels (components) is expected to be 1 (grayscale/ + * pseudocolor) or 3 (truecolor/directcolor). + * (could be 2 or 4 if using an alpha channel, but we aren't) + */ + + if (cinfo->output_width > 32767 || cinfo->output_height > 32767) + ERREXIT2(cinfo, JERR_RLE_DIMENSIONS, cinfo->output_width, + cinfo->output_height); + + if (cinfo->out_color_space != JCS_GRAYSCALE && + cinfo->out_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_RLE_COLORSPACE); + + if (cinfo->output_components != 1 && cinfo->output_components != 3) + ERREXIT1(cinfo, JERR_RLE_TOOMANYCHANNELS, cinfo->num_components); + + /* Convert colormap, if any, to RLE format. */ + + dest->colormap = NULL; + + if (cinfo->quantize_colors) { + /* Allocate storage for RLE-style cmap, zero any extra entries */ + cmapsize = cinfo->out_color_components * CMAPLENGTH * SIZEOF(rle_map); + dest->colormap = (rle_map *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, cmapsize); + MEMZERO(dest->colormap, cmapsize); + + /* Save away data in RLE format --- note 8-bit left shift! */ + /* Shifting would need adjustment for JSAMPLEs wider than 8 bits. */ + for (ci = 0; ci < cinfo->out_color_components; ci++) { + for (i = 0; i < cinfo->actual_number_of_colors; i++) { + dest->colormap[ci * CMAPLENGTH + i] = + GETJSAMPLE(cinfo->colormap[ci][i]) << 8; + } + } + } + + /* Set the output buffer to the first row */ + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, (JDIMENSION) 0, (JDIMENSION) 1, TRUE); + dest->pub.buffer_height = 1; + + dest->pub.put_pixel_rows = rle_put_pixel_rows; + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->total_extra_passes++; /* count file writing as separate pass */ + } +#endif +} + + +/* + * Write some pixel data. + * + * This routine just saves the data away in a virtual array. + */ + +METHODDEF(void) +rle_put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + + if (cinfo->output_scanline < cinfo->output_height) { + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + cinfo->output_scanline, (JDIMENSION) 1, TRUE); + } +} + +/* + * Finish up at the end of the file. + * + * Here is where we really output the RLE file. + */ + +METHODDEF(void) +finish_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + rle_hdr header; /* Output file information */ + rle_pixel **rle_row, *red, *green, *blue; + JSAMPROW output_row; + char cmapcomment[80]; + int row, col; + int ci; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* Initialize the header info */ + header = *rle_hdr_init(NULL); + header.rle_file = dest->pub.output_file; + header.xmin = 0; + header.xmax = cinfo->output_width - 1; + header.ymin = 0; + header.ymax = cinfo->output_height - 1; + header.alpha = 0; + header.ncolors = cinfo->output_components; + for (ci = 0; ci < cinfo->output_components; ci++) { + RLE_SET_BIT(header, ci); + } + if (cinfo->quantize_colors) { + header.ncmap = cinfo->out_color_components; + header.cmaplen = CMAPBITS; + header.cmap = dest->colormap; + /* Add a comment to the output image with the true colormap length. */ + sprintf(cmapcomment, "color_map_length=%d", cinfo->actual_number_of_colors); + rle_putcom(cmapcomment, &header); + } + + /* Emit the RLE header and color map (if any) */ + rle_put_setup(&header); + + /* Now output the RLE data from our virtual array. + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + * and (b) we are not on a machine where FAR pointers differ from regular. + */ + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_limit = cinfo->output_height; + progress->pub.pass_counter = 0; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + + if (cinfo->output_components == 1) { + for (row = cinfo->output_height-1; row >= 0; row--) { + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + rle_putrow(rle_row, (int) cinfo->output_width, &header); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } else { + for (row = cinfo->output_height-1; row >= 0; row--) { + rle_row = (rle_pixel **) dest->rle_row; + output_row = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + red = rle_row[0]; + green = rle_row[1]; + blue = rle_row[2]; + for (col = cinfo->output_width; col > 0; col--) { + *red++ = GETJSAMPLE(*output_row++); + *green++ = GETJSAMPLE(*output_row++); + *blue++ = GETJSAMPLE(*output_row++); + } + rle_putrow(rle_row, (int) cinfo->output_width, &header); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) + progress->completed_extra_passes++; +#endif + + /* Emit file trailer */ + rle_puteof(&header); + fflush(dest->pub.output_file); + if (ferror(dest->pub.output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for RLE format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_rle (j_decompress_ptr cinfo) +{ + rle_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (rle_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(rle_dest_struct)); + dest->pub.start_output = start_output_rle; + dest->pub.finish_output = finish_output_rle; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Allocate a work array for output to the RLE library. */ + dest->rle_row = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width, (JDIMENSION) cinfo->output_components); + + /* Allocate a virtual array to hold the image. */ + dest->image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) (cinfo->output_width * cinfo->output_components), + cinfo->output_height, (JDIMENSION) 1); + + return (djpeg_dest_ptr) dest; +} + +#endif /* RLE_SUPPORTED */ diff --git a/lib/jpeg/src/wrrle.d b/lib/jpeg/src/wrrle.d new file mode 100644 index 0000000..8629442 --- /dev/null +++ b/lib/jpeg/src/wrrle.d @@ -0,0 +1,16 @@ +wrrle.o: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg/src/wrtarga.c b/lib/jpeg/src/wrtarga.c new file mode 100644 index 0000000..6566273 --- /dev/null +++ b/lib/jpeg/src/wrtarga.c @@ -0,0 +1,253 @@ +/* + * wrtarga.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in Targa format. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * Based on code contributed by Lee Daniel Crocker. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef TARGA_SUPPORTED + + +/* + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + * This is not yet implemented. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * The output buffer needs to be writable by fwrite(). On PCs, we must + * allocate the buffer in near data space, because we are assuming small-data + * memory model, wherein fwrite() can't reach far memory. If you need to + * process very wide images on a PC, you might have to compile in large-memory + * model, or else replace fwrite() with a putc() loop --- which will be much + * slower. + */ + + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + char *iobuffer; /* physical I/O buffer */ + JDIMENSION buffer_width; /* width of one row */ +} tga_dest_struct; + +typedef tga_dest_struct * tga_dest_ptr; + + +LOCAL(void) +write_header (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, int num_colors) +/* Create and write a Targa header */ +{ + char targaheader[18]; + + /* Set unused fields of header to 0 */ + MEMZERO(targaheader, SIZEOF(targaheader)); + + if (num_colors > 0) { + targaheader[1] = 1; /* color map type 1 */ + targaheader[5] = (char) (num_colors & 0xFF); + targaheader[6] = (char) (num_colors >> 8); + targaheader[7] = 24; /* 24 bits per cmap entry */ + } + + targaheader[12] = (char) (cinfo->output_width & 0xFF); + targaheader[13] = (char) (cinfo->output_width >> 8); + targaheader[14] = (char) (cinfo->output_height & 0xFF); + targaheader[15] = (char) (cinfo->output_height >> 8); + targaheader[17] = 0x20; /* Top-down, non-interlaced */ + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + targaheader[2] = 3; /* image type = uncompressed gray-scale */ + targaheader[16] = 8; /* bits per pixel */ + } else { /* must be RGB */ + if (num_colors > 0) { + targaheader[2] = 1; /* image type = colormapped RGB */ + targaheader[16] = 8; + } else { + targaheader[2] = 2; /* image type = uncompressed RGB */ + targaheader[16] = 24; + } + } + + if (JFWRITE(dinfo->output_file, targaheader, 18) != (size_t) 18) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* used for unquantized full-color output */ +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + outptr[0] = (char) GETJSAMPLE(inptr[2]); /* RGB to BGR order */ + outptr[1] = (char) GETJSAMPLE(inptr[1]); + outptr[2] = (char) GETJSAMPLE(inptr[0]); + inptr += 3, outptr += 3; + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + +METHODDEF(void) +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* used for grayscale OR quantized color output */ +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = (char) GETJSAMPLE(*inptr++); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Write some demapped pixel data when color quantization is in effect. + * For Targa, this is only applied to grayscale data. + */ + +METHODDEF(void) +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JSAMPROW color_map0 = cinfo->colormap[0]; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = (char) GETJSAMPLE(color_map0[GETJSAMPLE(*inptr++)]); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + int num_colors, i; + FILE *outfile; + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + /* Targa doesn't have a mapped grayscale format, so we will */ + /* demap quantized gray output. Never emit a colormap. */ + write_header(cinfo, dinfo, 0); + if (cinfo->quantize_colors) + dest->pub.put_pixel_rows = put_demapped_gray; + else + dest->pub.put_pixel_rows = put_gray_rows; + } else if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* We only support 8-bit colormap indexes, so only 256 colors */ + num_colors = cinfo->actual_number_of_colors; + if (num_colors > 256) + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, num_colors); + write_header(cinfo, dinfo, num_colors); + /* Write the colormap. Note Targa uses BGR byte order */ + outfile = dest->pub.output_file; + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(cinfo->colormap[2][i]), outfile); + putc(GETJSAMPLE(cinfo->colormap[1][i]), outfile); + putc(GETJSAMPLE(cinfo->colormap[0][i]), outfile); + } + dest->pub.put_pixel_rows = put_gray_rows; + } else { + write_header(cinfo, dinfo, 0); + dest->pub.put_pixel_rows = put_pixel_rows; + } + } else { + ERREXIT(cinfo, JERR_TGA_COLORSPACE); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* Make sure we wrote the output file OK */ + fflush(dinfo->output_file); + if (ferror(dinfo->output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for Targa format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_targa (j_decompress_ptr cinfo) +{ + tga_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (tga_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(tga_dest_struct)); + dest->pub.start_output = start_output_tga; + dest->pub.finish_output = finish_output_tga; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Create I/O buffer. Note we make this near on a PC. */ + dest->buffer_width = cinfo->output_width * cinfo->output_components; + dest->iobuffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (dest->buffer_width * SIZEOF(char))); + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* TARGA_SUPPORTED */ diff --git a/lib/jpeg/src/wrtarga.d b/lib/jpeg/src/wrtarga.d new file mode 100644 index 0000000..587a562 --- /dev/null +++ b/lib/jpeg/src/wrtarga.d @@ -0,0 +1,16 @@ +wrtarga.o: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h \ + jerror.h cderror.h + +cdjpeg.h: + +jinclude.h: + +jconfig.h: + +jpeglib.h: + +jmorecfg.h: + +jerror.h: + +cderror.h: diff --git a/lib/jpeg8a/include/jconfig.h b/lib/jpeg8a/include/jconfig.h new file mode 100644 index 0000000..97ca026 --- /dev/null +++ b/lib/jpeg8a/include/jconfig.h @@ -0,0 +1,46 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/lib/jpeg8a/include/jerror.h b/lib/jpeg8a/include/jerror.h new file mode 100644 index 0000000..1cfb2b1 --- /dev/null +++ b/lib/jpeg8a/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/lib/jpeg8a/include/jmorecfg.h b/lib/jpeg8a/include/jmorecfg.h new file mode 100644 index 0000000..928d052 --- /dev/null +++ b/lib/jpeg8a/include/jmorecfg.h @@ -0,0 +1,371 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/lib/jpeg8a/include/jpeglib.h b/lib/jpeg8a/include/jpeglib.h new file mode 100644 index 0000000..5039d4b --- /dev/null +++ b/lib/jpeg8a/include/jpeglib.h @@ -0,0 +1,1158 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 80". + */ + +#define JPEG_LIB_VERSION 80 /* Version 8.0 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_h_scaled_size/DCTSIZE) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/lib/jpeg8a/include/jpgogc.h b/lib/jpeg8a/include/jpgogc.h new file mode 100644 index 0000000..33380eb --- /dev/null +++ b/lib/jpeg8a/include/jpgogc.h @@ -0,0 +1,29 @@ +/**************************************************************************** +* libjpeg - 6b wrapper +* +* The version of libjpeg used in libOGC has been modified to include a memory +* source data manager (jmemsrc.c). +* +* softdev November 2006 +****************************************************************************/ +#ifndef __JPGLIB__ +#define __JPGLIB__ + +#include + +typedef struct { + char *inbuffer; + char *outbuffer; + int inbufferlength; + int outbufferlength; + int width; + int height; + int num_colours; + int dct_method; + int dither_mode; + int greyscale; +} JPEGIMG; + +int JPEG_Decompress(JPEGIMG * jpgimg); + +#endif diff --git a/lib/libext2fs/AUTHORS b/lib/libext2fs/AUTHORS new file mode 100644 index 0000000..2291767 --- /dev/null +++ b/lib/libext2fs/AUTHORS @@ -0,0 +1,11 @@ + +Present author and main programmer of ext2fs in alphabetical order: + +Theodore Ts'o + +Many more are contributing to this project. Read it all up at http://e2fsprogs.sourceforge.net/ext2.html + + +Nintendo GameCube/Wii port author: + +Dimok diff --git a/lib/libext2fs/CREDITS b/lib/libext2fs/CREDITS new file mode 100644 index 0000000..32fb026 --- /dev/null +++ b/lib/libext2fs/CREDITS @@ -0,0 +1,9 @@ +First of all thanks goes to everyone who contributed to ext2fs directly or indirectly. +Visit the site to inform yourself who was involved in it http://e2fsprogs.sourceforge.net/ext2.html + +The following people have contributed directly or indirectly +to the Nintendo GameCube/Wii port of ext2fs. + +Michael "Chishm" Chisholm +rodries +Rhys "Shareese" Koedijk \ No newline at end of file diff --git a/lib/libext2fs/LICENSE b/lib/libext2fs/LICENSE new file mode 100644 index 0000000..623b625 --- /dev/null +++ b/lib/libext2fs/LICENSE @@ -0,0 +1,340 @@ + 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 Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 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/lib/libext2fs/Makefile b/lib/libext2fs/Makefile new file mode 100644 index 0000000..13fcc2f --- /dev/null +++ b/lib/libext2fs/Makefile @@ -0,0 +1,41 @@ + +#default: cube-release wii-release +default: wii-release + +all: debug release + +debug: cube-debug wii-debug + +release: cube-release wii-release + +cube-debug: + $(MAKE) -C source PLATFORM=cube BUILD=cube_debug + +wii-debug: + $(MAKE) -C source PLATFORM=wii BUILD=wii_debug + +cube-release: + $(MAKE) -C source PLATFORM=cube BUILD=cube_release + +wii-release: + $(MAKE) -C source PLATFORM=wii BUILD=wii_release + +clean: + $(MAKE) -C source clean + +install_ogc: cube-release wii-release + $(MAKE) -C source install + +run: install + $(MAKE) -C example + $(MAKE) -C example run + +include $(DEVKITPPC)/wii_rules +DKV := $(shell $(DEVKITPPC)/bin/$(CC) --version | sed 's/^.*(devkitPPC release \([0-9]*\)).*$$/\1/;q') +DEST_INC := include +DEST_LIB := lib$(DKV) + +install: wii-release + mkdir -p $(DEST_LIB) + cp lib/wii/libext2fs.a $(DEST_LIB) + diff --git a/lib/libext2fs/include/ext2.h b/lib/libext2fs/include/ext2.h new file mode 100644 index 0000000..059e18b --- /dev/null +++ b/lib/libext2fs/include/ext2.h @@ -0,0 +1,99 @@ + /** + * ext2.h - devoptab file routines for EXT2/3/4-based devices. + * + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __EXT2_H_ +#define __EXT2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* EXT2 cache options */ +#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */ +#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */ + +/* EXT2 mount flags */ +#define EXT2_FLAG_RW 0x00001 /* Open the filesystem for reading and writing. Without this flag, the filesystem is opened for reading only. */ +#define EXT2_FLAG_FORCE 0x00400 /* Open the filesystem regardless of the feature sets listed in the superblock */ +#define EXT2_FLAG_JOURNAL_DEV_OK 0x01000 /* Only open external journal devices if this flag is set (e.g. ext3/ext4) */ +#define EXT2_FLAG_64BITS 0x20000 /* Use the new style 64-Bit bitmaps. For more information see gen_bitmap64.c */ +#define EXT2_FLAG_PRINT_PROGRESS 0x40000 /* If this flag is set the progress of file operations will be printed to stdout */ +#define EXT2_FLAG_DEFAULT (EXT2_FLAG_RW | EXT2_FLAG_64BITS | EXT2_FLAG_JOURNAL_DEV_OK) + +/** + * Find all EXT2/3/4 partitions on a block device. + * + * @param INTERFACE The block device to search + * @param PARTITIONS (out) A pointer to receive the array of partition start sectors + * + * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) + * @note The caller is responsible for freeing PARTITIONS when finished with it + */ +int ext2FindPartitions(const DISC_INTERFACE *interface, sec_t **partitions); + +/** + * Mount a EXT2/3/4 partition from a specific sector on a block device. + * + * @param NAME The name to mount the device under (can then be accessed as "NAME:/") + * @param INTERFACE The block device to mount + * @param STARTSECTOR The sector the partition begins at + * @param CACHEPAGECOUNT The total number of pages in the device cache + * @param CACHEPAGESIZE The number of sectors per cache page + * @param FLAGS Additional mounting flags (see above) + * + * @return True if mount was successful, false if no partition was found or an error occurred (see errno) + */ +bool ext2Mount(const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags); + +/** + * Unmount a EXT2/3/4 partition. + * + * @param NAME The name of mount used in ext2Mount() + */ +void ext2Unmount(const char *name); + +/** + * Get the volume name of a mounted EXT2/3/4 partition. + * + * @param NAME The name of mount + * + * @return The volumes name if successful or NULL if an error occurred (see errno) + */ +const char *ext2GetVolumeName (const char *name); + +/** + * Set the volume name of a mounted EXT2/3/4 partition. + * + * @param NAME The name of mount + * @param VOLUMENAME The new volume name + * + * @return True if mount was successful, false if an error occurred (see errno) + * @note The mount must be write-enabled else this will fail + */ +bool ext2SetVolumeName (const char *name, const char *volumeName); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libext2fs/include/ext2_frag.h b/lib/libext2fs/include/ext2_frag.h new file mode 100644 index 0000000..5073277 --- /dev/null +++ b/lib/libext2fs/include/ext2_frag.h @@ -0,0 +1,16 @@ +#ifndef EXT2_FRAG_H_ +#define EXT2_FRAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*_ext2_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); + +int _EXT2_get_fragments(const char *in_path, _ext2_frag_append_t append_fragment, void *callback_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libext2fs/orig/Makefile b/lib/libext2fs/orig/Makefile new file mode 100644 index 0000000..9edb946 --- /dev/null +++ b/lib/libext2fs/orig/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +ifeq ($(PLATFORM),wii) +include $(DEVKITPPC)/wii_rules +endif + +ifeq ($(PLATFORM),cube) +include $(DEVKITPPC)/gamecube_rules +endif + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +BUILD ?= wii_release +SOURCES := . +INCLUDES := ../include +LIBDIR := ../lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -Os -Wall $(MACHDEP) $(INCLUDE) -DGEKKO \ + -DHAVE_UNISTD_H -DHAVE_SYS_STAT_H -DHAVE_SYS_TYPES_H -DHAVE_UTIME_H -DWORDS_BIGENDIAN \ + -DHAVE_ERRNO_H -DHAVE_STRDUP -DHAVE_SYS_RESOURCE_H +CXXFLAGS = $(CFLAGS) +ASFLAGS := -g +export EXT2BIN := $(LIBDIR)/$(PLATFORM)/libext2fs.a + +ifeq ($(BUILD),cube_debug) +CFLAGS += -DDEBUG_GEKKO +CXXFLAGS += -DDEBUG_GEKKO +endif +ifeq ($(BUILD),wii_debug) +CFLAGS += -DDEBUG_GEKKO +CXXFLAGS += -DDEBUG_GEKKO +endif + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) + +all: $(EXT2BIN) + +install: + cp ../include/ext2.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libext2fs.a $(DEVKITPRO)/libogc/lib/wii + cp ../lib/cube/libext2fs.a $(DEVKITPRO)/libogc/lib/cube + +wii-install: + cp ../include/ext2.h $(DEVKITPRO)/libogc/include + cp ../lib/wii/libext2fs.a $(DEVKITPRO)/libogc/lib/wii + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(EXT2BIN): $(OFILES) $(LIBDIR)/$(PLATFORM) + @rm -f "../$(EXT2BIN)" + @$(AR) rcs "../$(EXT2BIN)" $(OFILES) + @echo built ... $(notdir $@) + +$(LIBDIR)/$(PLATFORM): + mkdir -p ../$(LIBDIR)/$(PLATFORM) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/lib/libext2fs/orig/alloc.c b/lib/libext2fs/orig/alloc.c new file mode 100644 index 0000000..3a8f332 --- /dev/null +++ b/lib/libext2fs/orig/alloc.c @@ -0,0 +1,308 @@ +/* + * alloc.c --- allocate new inodes, blocks for ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Check for uninit block bitmaps and deal with them appropriately + */ +static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map, + dgrp_t group) +{ + blk_t i; + blk64_t blk, super_blk, old_desc_blk, new_desc_blk; + int old_desc_blocks; + + if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) + return; + + blk = (group * fs->super->s_blocks_per_group) + + fs->super->s_first_data_block; + + ext2fs_super_and_bgd_loc2(fs, group, &super_blk, + &old_desc_blk, &new_desc_blk, 0); + + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) { + if ((blk == super_blk) || + (old_desc_blk && old_desc_blocks && + (blk >= old_desc_blk) && + (blk < old_desc_blk + old_desc_blocks)) || + (new_desc_blk && (blk == new_desc_blk)) || + (blk == ext2fs_block_bitmap_loc(fs, group)) || + (blk == ext2fs_inode_bitmap_loc(fs, group)) || + (blk >= ext2fs_inode_table_loc(fs, group) && + (blk < ext2fs_inode_table_loc(fs, group) + + fs->inode_blocks_per_group))) + ext2fs_fast_mark_block_bitmap2(map, blk); + else + ext2fs_fast_unmark_block_bitmap2(map, blk); + } + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, group); +} + +/* + * Check for uninit inode bitmaps and deal with them appropriately + */ +static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, + dgrp_t group) +{ + ext2_ino_t i, ino; + + if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || + !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) + return; + + ino = (group * fs->super->s_inodes_per_group) + 1; + for (i=0; i < fs->super->s_inodes_per_group; i++, ino++) + ext2fs_fast_unmark_inode_bitmap2(map, ino); + + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + check_block_uninit(fs, fs->block_map, group); +} + +/* + * Right now, just search forward from the parent directory's block + * group to find the next free inode. + * + * Should have a special policy for directories. + */ +errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, + int mode EXT2FS_ATTR((unused)), + ext2fs_inode_bitmap map, ext2_ino_t *ret) +{ + ext2_ino_t dir_group = 0; + ext2_ino_t i; + ext2_ino_t start_inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->inode_map; + if (!map) + return EXT2_ET_NO_INODE_BITMAP; + + if (dir > 0) + dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); + + start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; + if (start_inode < EXT2_FIRST_INODE(fs->super)) + start_inode = EXT2_FIRST_INODE(fs->super); + if (start_inode > fs->super->s_inodes_count) + return EXT2_ET_INODE_ALLOC_FAIL; + i = start_inode; + + do { + if (((i - 1) % EXT2_INODES_PER_GROUP(fs->super)) == 0) + check_inode_uninit(fs, map, (i - 1) / + EXT2_INODES_PER_GROUP(fs->super)); + + if (!ext2fs_fast_test_inode_bitmap2(map, i)) + break; + i++; + if (i > fs->super->s_inodes_count) + i = EXT2_FIRST_INODE(fs->super); + } while (i != start_inode); + + if (ext2fs_test_inode_bitmap2(map, i)) + return EXT2_ET_INODE_ALLOC_FAIL; + *ret = i; + return 0; +} + +/* + * Stupid algorithm --- we now just search forward starting from the + * goal. Should put in a smarter one someday.... + */ +errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret) +{ + blk64_t i; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!goal || (goal >= ext2fs_blocks_count(fs->super))) + goal = fs->super->s_first_data_block; + i = goal; + check_block_uninit(fs, map, + (i - fs->super->s_first_data_block) / + EXT2_BLOCKS_PER_GROUP(fs->super)); + do { + if (((i - fs->super->s_first_data_block) % + EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) + check_block_uninit(fs, map, + (i - fs->super->s_first_data_block) / + EXT2_BLOCKS_PER_GROUP(fs->super)); + + if (!ext2fs_fast_test_block_bitmap2(map, i)) { + *ret = i; + return 0; + } + i++; + if (i >= ext2fs_blocks_count(fs->super)) + i = fs->super->s_first_data_block; + } while (i != goal); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} + +errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_new_block2(fs, goal, map, &val); + if (!retval) + *ret = (blk_t) val; + return retval; +} + +/* + * This function zeros out the allocated block, and updates all of the + * appropriate filesystem records. + */ +errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, + char *block_buf, blk64_t *ret) +{ + errcode_t retval; + blk64_t block; + char *buf = 0; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + memset(block_buf, 0, fs->blocksize); + + if (fs->get_alloc_block) { + retval = (fs->get_alloc_block)(fs, goal, &block); + if (retval) + goto fail; + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + goto fail; + } + + retval = ext2fs_new_block2(fs, goal, 0, &block); + if (retval) + goto fail; + } + + retval = io_channel_write_blk64(fs->io, block, 1, block_buf); + if (retval) + goto fail; + + ext2fs_block_alloc_stats2(fs, block, +1); + *ret = block; + +fail: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_alloc_block2(fs, goal, block_buf, &val); + if (!retval) + *ret = (blk_t) val; + return retval; +} + +errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish, + int num, ext2fs_block_bitmap map, blk64_t *ret) +{ + blk64_t b = start; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!b) + b = fs->super->s_first_data_block; + if (!finish) + finish = start; + if (!num) + num = 1; + do { + if (b+num-1 > ext2fs_blocks_count(fs->super)) + b = fs->super->s_first_data_block; + if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) { + *ret = b; + return 0; + } + b++; + } while (b != finish); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} + +errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, + int num, ext2fs_block_bitmap map, blk_t *ret) +{ + errcode_t retval; + blk64_t val; + retval = ext2fs_get_free_blocks2(fs, start, finish, num, map, &val); + if(!retval) + *ret = (blk_t) val; + return retval; +} + +void ext2fs_set_alloc_block_callback(ext2_filsys fs, + errcode_t (*func)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret), + errcode_t (**old)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret)) +{ + if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) + return; + + if (old) + *old = fs->get_alloc_block; + + fs->get_alloc_block = func; +} diff --git a/lib/libext2fs/orig/alloc_sb.c b/lib/libext2fs/orig/alloc_sb.c new file mode 100644 index 0000000..d5fca3b --- /dev/null +++ b/lib/libext2fs/orig/alloc_sb.c @@ -0,0 +1,86 @@ +/* + * alloc_sb.c --- Allocate the superblock and block group descriptors for a + * newly initialized filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This function reserves the superblock and block group descriptors + * for a given block group. It currently returns the number of free + * blocks assuming that inode table and allocation bitmaps will be in + * the group. This is not necessarily the case when the flex_bg + * feature is enabled, so callers should take care! It was only + * really intended for use by mke2fs, and even there it's not that + * useful. In the future, when we redo this function for 64-bit block + * numbers, we should probably return the number of blocks used by the + * super block and group descriptors instead. + * + * See also the comment for ext2fs_super_and_bgd_loc() + */ +int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap) +{ + blk64_t super_blk, old_desc_blk, new_desc_blk; + blk_t used_blks; + int j, old_desc_blocks, num_blocks; + + ext2fs_super_and_bgd_loc2(fs, group, &super_blk, + &old_desc_blk, &new_desc_blk, &used_blks); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + if (super_blk || (group == 0)) + ext2fs_mark_block_bitmap2(bmap, super_blk); + + if (old_desc_blk) { + if (fs->super->s_reserved_gdt_blocks && fs->block_map == bmap) + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + for (j=0; j < old_desc_blocks; j++) + if (old_desc_blk + j < ext2fs_blocks_count(fs->super)) + ext2fs_mark_block_bitmap2(bmap, + old_desc_blk + j); + } + if (new_desc_blk) + ext2fs_mark_block_bitmap2(bmap, new_desc_blk); + + if (group == fs->group_desc_count-1) { + num_blocks = (ext2fs_blocks_count(fs->super) - + fs->super->s_first_data_block) % + fs->super->s_blocks_per_group; + if (!num_blocks) + num_blocks = fs->super->s_blocks_per_group; + } else + num_blocks = fs->super->s_blocks_per_group; + + num_blocks -= 2 + fs->inode_blocks_per_group + used_blks; + + return num_blocks ; +} diff --git a/lib/libext2fs/orig/alloc_stats.c b/lib/libext2fs/orig/alloc_stats.c new file mode 100644 index 0000000..0f27665 --- /dev/null +++ b/lib/libext2fs/orig/alloc_stats.c @@ -0,0 +1,106 @@ +/* + * alloc_stats.c --- Update allocation statistics for ext2fs + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir) +{ + int group = ext2fs_group_of_ino(fs, ino); + +#ifndef OMIT_COM_ERR + if (ino > fs->super->s_inodes_count) { + com_err("ext2fs_inode_alloc_stats2", 0, + "Illegal inode number: %lu", (unsigned long) ino); + return; + } +#endif + if (inuse > 0) + ext2fs_mark_inode_bitmap2(fs->inode_map, ino); + else + ext2fs_unmark_inode_bitmap2(fs->inode_map, ino); + ext2fs_bg_free_inodes_count_set(fs, group, ext2fs_bg_free_inodes_count(fs, group) - inuse); + if (isdir) + ext2fs_bg_used_dirs_count_set(fs, group, ext2fs_bg_used_dirs_count(fs, group) + inuse); + + /* We don't strictly need to be clearing the uninit flag if inuse < 0 + * (i.e. freeing inodes) but it also means something is bad. */ + ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group - + ext2fs_bg_itable_unused(fs, group) + + group * fs->super->s_inodes_per_group + 1; + + if (ino >= first_unused_inode) + ext2fs_bg_itable_unused_set(fs, group, group * fs->super->s_inodes_per_group + fs->super->s_inodes_per_group - ino); + ext2fs_group_desc_csum_set(fs, group); + } + + fs->super->s_free_inodes_count -= inuse; + ext2fs_mark_super_dirty(fs); + ext2fs_mark_ib_dirty(fs); +} + +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) +{ + ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); +} + +void ext2fs_block_alloc_stats2(ext2_filsys fs, blk64_t blk, int inuse) +{ + int group = ext2fs_group_of_blk2(fs, blk); + +#ifndef OMIT_COM_ERR + if (blk >= ext2fs_blocks_count(fs->super)) { + com_err("ext2fs_block_alloc_stats", 0, + "Illegal block number: %lu", (unsigned long) blk); + return; + } +#endif + if (inuse > 0) + ext2fs_mark_block_bitmap2(fs->block_map, blk); + else + ext2fs_unmark_block_bitmap2(fs->block_map, blk); + ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) - inuse); + ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, group); + + ext2fs_free_blocks_count_add(fs->super, -inuse); + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); + if (fs->block_alloc_stats) + (fs->block_alloc_stats)(fs, (blk64_t) blk, inuse); +} + +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) +{ + ext2fs_block_alloc_stats2(fs, blk, inuse); +} + +void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, + void (*func)(ext2_filsys fs, + blk64_t blk, + int inuse), + void (**old)(ext2_filsys fs, + blk64_t blk, + int inuse)) +{ + if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) + return; + if (old) + *old = fs->block_alloc_stats; + + fs->block_alloc_stats = func; +} diff --git a/lib/libext2fs/orig/alloc_tables.c b/lib/libext2fs/orig/alloc_tables.c new file mode 100644 index 0000000..1c4532b --- /dev/null +++ b/lib/libext2fs/orig/alloc_tables.c @@ -0,0 +1,239 @@ +/* + * alloc_tables.c --- Allocate tables for a newly initialized + * filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +/* + * This routine searches for free blocks that can allocate a full + * group of bitmaps or inode tables for a flexbg group. Returns the + * block number with a correct offset were the bitmaps and inode + * tables can be allocated continously and in order. + */ +static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk, + ext2fs_block_bitmap bmap, int offset, int size, + int elem_size) +{ + int flexbg, flexbg_size; + blk64_t last_blk, first_free = 0; + dgrp_t last_grp; + + flexbg_size = 1 << fs->super->s_log_groups_per_flex; + flexbg = group / flexbg_size; + + if (size > (int) (fs->super->s_blocks_per_group / 8)) + size = (int) fs->super->s_blocks_per_group / 8; + + if (offset) + offset -= 1; + + /* + * Don't do a long search if the previous block + * search is still valid. + */ + if (start_blk && group % flexbg_size) { + if (ext2fs_test_block_bitmap_range2(bmap, start_blk + elem_size, + size)) + return start_blk + elem_size; + } + + start_blk = ext2fs_group_first_block2(fs, flexbg_size * flexbg); + last_grp = group | (flexbg_size - 1); + if (last_grp > fs->group_desc_count) + last_grp = fs->group_desc_count; + last_blk = ext2fs_group_last_block2(fs, last_grp); + + /* Find the first available block */ + if (ext2fs_get_free_blocks2(fs, start_blk, last_blk, 1, bmap, + &first_free)) + return first_free; + + if (ext2fs_get_free_blocks2(fs, first_free + offset, last_blk, size, + bmap, &first_free)) + return first_free; + + return first_free; +} + +errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap) +{ + errcode_t retval; + blk64_t group_blk, start_blk, last_blk, new_blk, blk; + dgrp_t last_grp = 0; + int j, rem_grps = 0, flexbg_size = 0; + + group_blk = ext2fs_group_first_block2(fs, group); + last_blk = ext2fs_group_last_block2(fs, group); + + if (!bmap) + bmap = fs->block_map; + + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_FLEX_BG) && + fs->super->s_log_groups_per_flex) { + flexbg_size = 1 << fs->super->s_log_groups_per_flex; + last_grp = group | (flexbg_size - 1); + rem_grps = last_grp - group; + if (last_grp > fs->group_desc_count) + last_grp = fs->group_desc_count; + } + + /* + * Allocate the block and inode bitmaps, if necessary + */ + if (fs->stride) { + retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk, + 1, bmap, &start_blk); + if (retval) + return retval; + start_blk += fs->inode_blocks_per_group; + start_blk += ((fs->stride * group) % + (last_blk - start_blk + 1)); + if (start_blk >= last_blk) + start_blk = group_blk; + } else + start_blk = group_blk; + + if (flexbg_size) { + blk64_t prev_block = 0; + + if (group && ext2fs_block_bitmap_loc(fs, group - 1)) + prev_block = ext2fs_block_bitmap_loc(fs, group - 1); + start_blk = flexbg_offset(fs, group, prev_block, bmap, + 0, rem_grps, 1); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_block_bitmap_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks2(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap2(bmap, new_blk); + ext2fs_block_bitmap_loc_set(fs, group, new_blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + + if (flexbg_size) { + blk64_t prev_block = 0; + if (group && ext2fs_inode_bitmap_loc(fs, group - 1)) + prev_block = ext2fs_inode_bitmap_loc(fs, group - 1); + start_blk = flexbg_offset(fs, group, prev_block, bmap, + flexbg_size, rem_grps, 1); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_inode_bitmap_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks2(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap2(bmap, new_blk); + ext2fs_inode_bitmap_loc_set(fs, group, new_blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + + /* + * Allocate the inode table + */ + if (flexbg_size) { + blk64_t prev_block = 0; + if (group && ext2fs_inode_table_loc(fs, group - 1)) + prev_block = ext2fs_inode_table_loc(fs, group - 1); + if (last_grp == fs->group_desc_count) + rem_grps = last_grp - group; + group_blk = flexbg_offset(fs, group, prev_block, bmap, + flexbg_size * 2, + fs->inode_blocks_per_group * + rem_grps, + fs->inode_blocks_per_group); + last_blk = ext2fs_group_last_block2(fs, last_grp); + } + + if (!ext2fs_inode_table_loc(fs, group)) { + retval = ext2fs_get_free_blocks2(fs, group_blk, last_blk, + fs->inode_blocks_per_group, + bmap, &new_blk); + if (retval) + return retval; + for (j=0, blk = new_blk; + j < fs->inode_blocks_per_group; + j++, blk++) { + ext2fs_mark_block_bitmap2(bmap, blk); + if (flexbg_size) { + dgrp_t gr = ext2fs_group_of_blk2(fs, blk); + ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1); + ext2fs_free_blocks_count_add(fs->super, -1); + ext2fs_bg_flags_clear(fs, gr, + EXT2_BG_BLOCK_UNINIT); + ext2fs_group_desc_csum_set(fs, gr); + } + } + ext2fs_inode_table_loc_set(fs, group, new_blk); + } + ext2fs_group_desc_csum_set(fs, group); + return 0; +} + +errcode_t ext2fs_allocate_tables(ext2_filsys fs) +{ + errcode_t retval; + dgrp_t i; + struct ext2fs_numeric_progress_struct progress; + + ext2fs_numeric_progress_init(fs, &progress, NULL, + fs->group_desc_count); + + for (i = 0; i < fs->group_desc_count; i++) { + ext2fs_numeric_progress_update(fs, &progress, i); + retval = ext2fs_allocate_group_table(fs, i, fs->block_map); + if (retval) + return retval; + } + ext2fs_numeric_progress_close(fs, &progress, NULL); + return 0; +} + diff --git a/lib/libext2fs/orig/badblocks.c b/lib/libext2fs/orig/badblocks.c new file mode 100644 index 0000000..5eb28b7 --- /dev/null +++ b/lib/libext2fs/orig/badblocks.c @@ -0,0 +1,327 @@ +/* + * badblocks.c --- routines to manipulate the bad block structure + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Helper function for making a badblocks list + */ +static errcode_t make_u32_list(int size, int num, __u32 *list, + ext2_u32_list *ret) +{ + ext2_u32_list bb; + errcode_t retval; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); + if (retval) + return retval; + memset(bb, 0, sizeof(struct ext2_struct_u32_list)); + bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; + bb->size = size ? size : 10; + bb->num = num; + retval = ext2fs_get_array(bb->size, sizeof(blk_t), &bb->list); + if (retval) { + ext2fs_free_mem(&bb); + return retval; + } + if (list) + memcpy(bb->list, list, bb->size * sizeof(blk_t)); + else + memset(bb->list, 0, bb->size * sizeof(blk_t)); + *ret = bb; + return 0; +} + + +/* + * This procedure creates an empty u32 list. + */ +errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) +{ + return make_u32_list(size, 0, 0, ret); +} + +/* + * This procedure creates an empty badblocks list. + */ +errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) +{ + return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); +} + + +/* + * This procedure copies a badblocks list + */ +errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) +{ + errcode_t retval; + + retval = make_u32_list(src->size, src->num, src->list, dest); + if (retval) + return retval; + (*dest)->badblocks_flags = src->badblocks_flags; + return 0; +} + +errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest) +{ + return ext2fs_u32_copy((ext2_u32_list) src, + (ext2_u32_list *) dest); +} + +/* + * This procedure frees a badblocks list. + * + * (note: moved to closefs.c) + */ + + +/* + * This procedure adds a block to a badblocks list. + */ +errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) +{ + errcode_t retval; + int i, j; + unsigned long old_size; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb->num >= bb->size) { + old_size = bb->size * sizeof(__u32); + bb->size += 100; + retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), + &bb->list); + if (retval) { + bb->size -= 100; + return retval; + } + } + + /* + * Add special case code for appending to the end of the list + */ + i = bb->num-1; + if ((bb->num != 0) && (bb->list[i] == blk)) + return 0; + if ((bb->num == 0) || (bb->list[i] < blk)) { + bb->list[bb->num++] = blk; + return 0; + } + + j = bb->num; + for (i=0; i < bb->num; i++) { + if (bb->list[i] == blk) + return 0; + if (bb->list[i] > blk) { + j = i; + break; + } + } + for (i=bb->num; i > j; i--) + bb->list[i] = bb->list[i-1]; + bb->list[j] = blk; + bb->num++; + return 0; +} + +errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); +} + +/* + * This procedure finds a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) +{ + int low, high, mid; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return -1; + + if (bb->num == 0) + return -1; + + low = 0; + high = bb->num-1; + if (blk == bb->list[low]) + return low; + if (blk == bb->list[high]) + return high; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (blk == bb->list[mid]) + return mid; + if (blk < bb->list[mid]) + high = mid; + else + low = mid; + } + return -1; +} + +/* + * This procedure tests to see if a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) +{ + if (ext2fs_u32_list_find(bb, blk) < 0) + return 0; + else + return 1; +} + +int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); +} + + +/* + * Remove a block from the badblock list + */ +int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) +{ + int remloc, i; + + if (bb->num == 0) + return -1; + + remloc = ext2fs_u32_list_find(bb, blk); + if (remloc < 0) + return -1; + + for (i = remloc ; i < bb->num-1; i++) + bb->list[i] = bb->list[i+1]; + bb->num--; + return 0; +} + +void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) +{ + ext2fs_u32_list_del(bb, blk); +} + +errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret) +{ + ext2_u32_iterate iter; + errcode_t retval; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); + if (retval) + return retval; + + iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; + iter->bb = bb; + iter->ptr = 0; + *ret = iter; + return 0; +} + +errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret) +{ + return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, + (ext2_u32_iterate *) ret); +} + + +int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) +{ + ext2_u32_list bb; + + if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) + return 0; + + bb = iter->bb; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return 0; + + if (iter->ptr < bb->num) { + *blk = bb->list[iter->ptr++]; + return 1; + } + *blk = 0; + return 0; +} + +int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, + (__u32 *) blk); +} + + +void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) +{ + if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) + return; + + iter->bb = 0; + ext2fs_free_mem(&iter); +} + +void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) +{ + ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); +} + + +int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) +{ + EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); + EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb1->num != bb2->num) + return 0; + + if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) + return 0; + return 1; +} + +int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) +{ + return ext2fs_u32_list_equal((ext2_u32_list) bb1, + (ext2_u32_list) bb2); +} + +int ext2fs_u32_list_count(ext2_u32_list bb) +{ + return bb->num; +} diff --git a/lib/libext2fs/orig/bb_compat.c b/lib/libext2fs/orig/bb_compat.c new file mode 100644 index 0000000..a94e3e4 --- /dev/null +++ b/lib/libext2fs/orig/bb_compat.c @@ -0,0 +1,63 @@ +/* + * bb_compat.c --- compatibility badblocks routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t badblocks_list_create(badblocks_list *ret, int size) +{ + return ext2fs_badblocks_list_create(ret, size); +} + +void badblocks_list_free(badblocks_list bb) +{ + ext2fs_badblocks_list_free(bb); +} + +errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_add(bb, blk); +} + +int badblocks_list_test(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_test(bb, blk); +} + +errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret) +{ + return ext2fs_badblocks_list_iterate_begin(bb, ret); +} + +int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_badblocks_list_iterate(iter, blk); +} + +void badblocks_list_iterate_end(badblocks_iterate iter) +{ + ext2fs_badblocks_list_iterate_end(iter); +} diff --git a/lib/libext2fs/orig/bb_inode.c b/lib/libext2fs/orig/bb_inode.c new file mode 100644 index 0000000..0b6c3dd --- /dev/null +++ b/lib/libext2fs/orig/bb_inode.c @@ -0,0 +1,266 @@ +/* + * bb_inode.c --- routines to update the bad block inode. + * + * WARNING: This routine modifies a lot of state in the filesystem; if + * this routine returns an error, the bad block inode may be in an + * inconsistent state. + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct set_badblock_record { + ext2_badblocks_iterate bb_iter; + int bad_block_count; + blk_t *ind_blocks; + int max_ind_blocks; + int ind_blocks_size; + int ind_blocks_ptr; + char *block_buf; + errcode_t err; +}; + +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); + +/* + * Given a bad blocks bitmap, update the bad blocks inode to reflect + * the map. + */ +errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) +{ + errcode_t retval; + struct set_badblock_record rec; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + memset(&rec, 0, sizeof(rec)); + rec.max_ind_blocks = 10; + retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t), + &rec.ind_blocks); + if (retval) + return retval; + memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); + retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); + if (retval) + goto cleanup; + memset(rec.block_buf, 0, fs->blocksize); + rec.err = 0; + + /* + * First clear the old bad blocks (while saving the indirect blocks) + */ + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_DEPTH_TRAVERSE, 0, + clear_bad_block_proc, &rec); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + + /* + * Now set the bad blocks! + * + * First, mark the bad blocks as used. This prevents a bad + * block from being used as an indirecto block for the bad + * block inode (!). + */ + if (bb_list) { + retval = ext2fs_badblocks_list_iterate_begin(bb_list, + &rec.bb_iter); + if (retval) + goto cleanup; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_APPEND, 0, + set_bad_block_proc, &rec); + ext2fs_badblocks_list_iterate_end(rec.bb_iter); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + } + + /* + * Update the bad block inode's mod time and block count + * field. + */ + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + + inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); + if (!inode.i_ctime) + inode.i_ctime = fs->now ? fs->now : time(0); + ext2fs_iblk_set(fs, &inode, rec.bad_block_count); + inode.i_size = rec.bad_block_count * fs->blocksize; + + retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + +cleanup: + ext2fs_free_mem(&rec.ind_blocks); + ext2fs_free_mem(&rec.block_buf); + return retval; +} + +/* + * Helper function for update_bb_inode() + * + * Clear the bad blocks in the bad block inode, while saving the + * indirect blocks. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + unsigned long old_size; + + if (!*block_nr) + return 0; + + /* + * If the block number is outrageous, clear it and ignore it. + */ + if (*block_nr >= ext2fs_blocks_count(fs->super) || + *block_nr < fs->super->s_first_data_block) { + *block_nr = 0; + return BLOCK_CHANGED; + } + + if (blockcnt < 0) { + if (rec->ind_blocks_size >= rec->max_ind_blocks) { + old_size = rec->max_ind_blocks * sizeof(blk_t); + rec->max_ind_blocks += 10; + retval = ext2fs_resize_mem(old_size, + rec->max_ind_blocks * sizeof(blk_t), + &rec->ind_blocks); + if (retval) { + rec->max_ind_blocks -= 10; + rec->err = retval; + return BLOCK_ABORT; + } + } + rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; + } + + /* + * Mark the block as unused, and update accounting information + */ + ext2fs_block_alloc_stats2(fs, *block_nr, -1); + + *block_nr = 0; + return BLOCK_CHANGED; +} + + +/* + * Helper function for update_bb_inode() + * + * Set the block list in the bad block inode, using the supplied bitmap. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + blk_t blk; + + if (blockcnt >= 0) { + /* + * Get the next bad block. + */ + if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) + return BLOCK_ABORT; + rec->bad_block_count++; + } else { + /* + * An indirect block; fetch a block from the + * previously used indirect block list. The block + * most be not marked as used; if so, get another one. + * If we run out of reserved indirect blocks, allocate + * a new one. + */ + retry: + if (rec->ind_blocks_ptr < rec->ind_blocks_size) { + blk = rec->ind_blocks[rec->ind_blocks_ptr++]; + if (ext2fs_test_block_bitmap2(fs->block_map, blk)) + goto retry; + } else { + retval = ext2fs_new_block(fs, 0, 0, &blk); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + retval = io_channel_write_blk64(fs->io, blk, 1, rec->block_buf); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + + /* + * Update block counts + */ + ext2fs_block_alloc_stats2(fs, blk, +1); + + *block_nr = blk; + return BLOCK_CHANGED; +} + + + + + + diff --git a/lib/libext2fs/orig/bit_ops.h b/lib/libext2fs/orig/bit_ops.h new file mode 100644 index 0000000..762be0b --- /dev/null +++ b/lib/libext2fs/orig/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/lib/libext2fs/orig/bitmaps.c b/lib/libext2fs/orig/bitmaps.c new file mode 100644 index 0000000..c53d61e --- /dev/null +++ b/lib/libext2fs/orig/bitmaps.c @@ -0,0 +1,256 @@ +/* + * bitmaps.c --- routines to read, write, and manipulate the inode and + * block bitmaps. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + ext2fs_free_generic_bmap(bitmap); +} + +void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) +{ + ext2fs_free_generic_bmap(bitmap); +} + +errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + return (ext2fs_copy_generic_bmap(src, dest)); +} +void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) +{ + ext2fs_set_generic_bmap_padding(map); +} + +errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret) +{ + __u64 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = 1; + end = fs->super->s_inodes_count; + real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); + + /* Are we permitted to use new-style bitmaps? */ + if (fs->flags & EXT2_FLAG_64BITS) + return (ext2fs_alloc_generic_bmap(fs, + EXT2_ET_MAGIC_INODE_BITMAP64, + EXT2FS_BMAP64_BITARRAY, + start, end, real_end, descr, ret)); + + /* Otherwise, check to see if the file system is small enough + * to use old-style 32-bit bitmaps */ + if ((end > ~0U) || (real_end > ~0U)) + return EXT2_ET_CANT_USE_LEGACY_BITMAPS; + + return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, fs, + start, end, real_end, + descr, 0, + (ext2fs_generic_bitmap *) ret)); +} + +errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret) +{ + __u64 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = fs->super->s_first_data_block; + end = ext2fs_blocks_count(fs->super)-1; + real_end = ((__u64) EXT2_BLOCKS_PER_GROUP(fs->super) + * (__u64) fs->group_desc_count)-1 + start; + + if (fs->flags & EXT2_FLAG_64BITS) + return (ext2fs_alloc_generic_bmap(fs, + EXT2_ET_MAGIC_BLOCK_BITMAP64, + EXT2FS_BMAP64_BITARRAY, + start, end, real_end, descr, ret)); + + if ((end > ~0U) || (real_end > ~0U)) + return EXT2_ET_CANT_USE_LEGACY_BITMAPS; + + return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, fs, + start, end, real_end, + descr, 0, + (ext2fs_generic_bitmap *) ret)); +} + +errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend) +{ + __u64 tmp_oend; + int retval; + + retval = ext2fs_fudge_generic_bmap_end((ext2fs_generic_bitmap) bitmap, + EXT2_ET_FUDGE_INODE_BITMAP_END, + end, &tmp_oend); + if (oend) + *oend = tmp_oend; + return retval; +} + +errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend) +{ + return (ext2fs_fudge_generic_bitmap_end(bitmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + EXT2_ET_FUDGE_BLOCK_BITMAP_END, + end, oend)); +} + +errcode_t ext2fs_fudge_block_bitmap_end2(ext2fs_block_bitmap bitmap, + blk64_t end, blk64_t *oend) +{ + return (ext2fs_fudge_generic_bmap_end(bitmap, + EXT2_ET_FUDGE_BLOCK_BITMAP_END, + end, oend)); +} + +void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + ext2fs_clear_generic_bmap(bitmap); +} + +void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) +{ + ext2fs_clear_generic_bmap(bitmap); +} + +errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap) +{ + return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, + new_end, new_real_end, bmap)); +} + +errcode_t ext2fs_resize_inode_bitmap2(__u64 new_end, __u64 new_real_end, + ext2fs_inode_bitmap bmap) +{ + return (ext2fs_resize_generic_bmap(bmap, new_end, new_real_end)); +} + +errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap) +{ + return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, + new_end, new_real_end, bmap)); +} + +errcode_t ext2fs_resize_block_bitmap2(__u64 new_end, __u64 new_real_end, + ext2fs_block_bitmap bmap) +{ + return (ext2fs_resize_generic_bmap(bmap, new_end, new_real_end)); +} + +errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2) +{ + return (ext2fs_compare_generic_bmap(EXT2_ET_NEQ_BLOCK_BITMAP, + bm1, bm2)); +} + +errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2) +{ + return (ext2fs_compare_generic_bmap(EXT2_ET_NEQ_INODE_BITMAP, + bm1, bm2)); +} + +errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *in) +{ + return (ext2fs_set_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_INODE_BITMAP, + start, num, in)); +} + +errcode_t ext2fs_set_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *in) +{ + return (ext2fs_set_generic_bmap_range(bmap, start, num, in)); +} + +errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *out) +{ + return (ext2fs_get_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_INODE_BITMAP, + start, num, out)); +} + +errcode_t ext2fs_get_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *out) +{ + return (ext2fs_get_generic_bmap_range(bmap, start, num, out)); +} + +errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *in) +{ + return (ext2fs_set_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + start, num, in)); +} + +errcode_t ext2fs_set_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *in) +{ + return (ext2fs_set_generic_bmap_range(bmap, start, num, in)); +} + +errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *out) +{ + return (ext2fs_get_generic_bitmap_range(bmap, + EXT2_ET_MAGIC_BLOCK_BITMAP, + start, num, out)); +} + +errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *out) +{ + return (ext2fs_get_generic_bmap_range(bmap, start, num, out)); +} diff --git a/lib/libext2fs/orig/bitops.c b/lib/libext2fs/orig/bitops.c new file mode 100644 index 0000000..a3f72c3 --- /dev/null +++ b/lib/libext2fs/orig/bitops.c @@ -0,0 +1,117 @@ +/* + * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined + * routines. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef _EXT2_HAVE_ASM_BITOPS_ + +/* + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. You should + * recode these in the native assmebly language, if at all possible. + * + * C language equivalents written by Theodore Ts'o, 9/26/92. + * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian + * systems, as well as non-32 bit systems. + */ + +int ext2fs_set_bit(unsigned int nr,void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR |= mask; + return retval; +} + +int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR &= ~mask; + return retval; +} + +int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + int mask; + const unsigned char *ADDR = (const unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + return (mask & *ADDR); +} + +#endif /* !_EXT2_HAVE_ASM_BITOPS_ */ + +void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description) +{ +#ifndef OMIT_COM_ERR + if (description) + com_err(0, errcode, "#%lu for %s", arg, description); + else + com_err(0, errcode, "#%lu", arg); +#endif +} + +/* + * C-only 64 bit ops. + */ + +int ext2fs_set_bit64(__u64 nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR |= mask; + return retval; +} + +int ext2fs_clear_bit64(__u64 nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR &= ~mask; + return retval; +} + +int ext2fs_test_bit64(__u64 nr, const void * addr) +{ + int mask; + const unsigned char *ADDR = (const unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + return (mask & *ADDR); +} + diff --git a/lib/libext2fs/orig/bitops.h b/lib/libext2fs/orig/bitops.h new file mode 100644 index 0000000..bf6ee82 --- /dev/null +++ b/lib/libext2fs/orig/bitops.h @@ -0,0 +1,638 @@ +/* + * bitops.h --- Bitmap frobbing code. The byte swapping routines are + * also included here. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ +#ifndef _BITOPS_H_ +#define _BITOPS_H_ + +extern int ext2fs_set_bit(unsigned int nr,void * addr); +extern int ext2fs_clear_bit(unsigned int nr, void * addr); +extern int ext2fs_test_bit(unsigned int nr, const void * addr); +extern void ext2fs_fast_set_bit(unsigned int nr,void * addr); +extern void ext2fs_fast_clear_bit(unsigned int nr, void * addr); +extern int ext2fs_set_bit64(__u64 nr,void * addr); +extern int ext2fs_clear_bit64(__u64 nr, void * addr); +extern int ext2fs_test_bit64(__u64 nr, const void * addr); +extern void ext2fs_fast_set_bit64(__u64 nr,void * addr); +extern void ext2fs_fast_clear_bit64(__u64 nr, void * addr); +extern __u16 ext2fs_swab16(__u16 val); +extern __u32 ext2fs_swab32(__u32 val); +extern __u64 ext2fs_swab64(__u64 val); + +#ifdef WORDS_BIGENDIAN +#define ext2fs_cpu_to_le64(x) ext2fs_swab64((x)) +#define ext2fs_le64_to_cpu(x) ext2fs_swab64((x)) +#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) +#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) +#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) +#define ext2fs_cpu_to_be32(x) ((__u32)(x)) +#define ext2fs_be32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_be16(x) ((__u16)(x)) +#define ext2fs_be16_to_cpu(x) ((__u16)(x)) +#else +#define ext2fs_cpu_to_le64(x) ((__u64)(x)) +#define ext2fs_le64_to_cpu(x) ((__u64)(x)) +#define ext2fs_cpu_to_le32(x) ((__u32)(x)) +#define ext2fs_le32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_le16(x) ((__u16)(x)) +#define ext2fs_le16_to_cpu(x) ((__u16)(x)) +#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) +#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) +#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) +#endif + +/* + * EXT2FS bitmap manipulation routines. + */ + +/* Support for sending warning messages from the inline subroutines */ +extern const char *ext2fs_block_string; +extern const char *ext2fs_inode_string; +extern const char *ext2fs_mark_string; +extern const char *ext2fs_unmark_string; +extern const char *ext2fs_test_string; +extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description); +extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg); + +extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); +extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); + +extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); +extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); + +extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); + +extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); +extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); + +extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, + ino_t inode, int num); +extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); + +/* These routines moved to gen_bitmap.c (actually, some of the above, too) */ +extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno); +extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); +extern int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap); +extern __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap); + +/* 64-bit versions */ + +extern int ext2fs_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); + +extern int ext2fs_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); + +extern void ext2fs_fast_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern void ext2fs_fast_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); +extern int ext2fs_fast_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block); + +extern void ext2fs_fast_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern void ext2fs_fast_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_start2(ext2fs_inode_bitmap bitmap); +extern blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_end2(ext2fs_inode_bitmap bitmap); + +extern int ext2fs_fast_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +extern void ext2fs_fast_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +extern void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num); +/* These routines moved to gen_bitmap64.c */ +extern void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap); +extern errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +extern void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap); +extern int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + blk64_t bitno); +extern int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern __u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap); +extern __u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap); +extern int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); +extern void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, unsigned int num); + +/* + * The inline routines themselves... + * + * If NO_INLINE_FUNCS is defined, then we won't try to do inline + * functions at all; they will be included as normal functions in + * inline.c + */ +#ifdef NO_INLINE_FUNCS +#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__mc68000__))) + /* This prevents bitops.c from trying to include the C */ + /* function version of these functions */ +#define _EXT2_HAVE_ASM_BITOPS_ +#endif +#endif /* NO_INLINE_FUNCS */ + +#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef INCLUDE_INLINE_FUNCS +#define _INLINE_ extern +#else +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif +#endif + +/* + * Fast bit set/clear functions that doesn't need to return the + * previous bit value. + */ + +_INLINE_ void ext2fs_fast_set_bit(unsigned int nr,void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR |= (1 << (nr & 0x07)); +} + +_INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR &= ~(1 << (nr & 0x07)); +} + + +_INLINE_ void ext2fs_fast_set_bit64(__u64 nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR |= (1 << (nr & 0x07)); +} + +_INLINE_ void ext2fs_fast_clear_bit64(__u64 nr, void * addr) +{ + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + *ADDR &= ~(1 << (nr & 0x07)); +} + + +#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ + (defined(__i386__) || defined(__i486__) || defined(__i586__))) + +#define _EXT2_HAVE_ASM_BITOPS_ +#define _EXT2_HAVE_ASM_SWAB_ + +/* + * These are done by inline assembly for speed reasons..... + * + * All bitoperations return 0 if the bit was cleared before the + * operation and != 0 if it was not. Bit 0 is the LSB of addr; bit 32 + * is the LSB of (addr+1). + */ + +/* + * Some hacks to defeat gcc over-optimizations.. + */ +struct __dummy_h { unsigned long a[100]; }; +#define EXT2FS_ADDR (*(struct __dummy_h *) addr) +#define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr) + +_INLINE_ int ext2fs_set_bit(unsigned int nr, void * addr) +{ + int oldbit; + + addr = (void *) (((unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (EXT2FS_ADDR) + :"r" (nr & 7)); + return oldbit; +} + +_INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + int oldbit; + + addr = (void *) (((unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (EXT2FS_ADDR) + :"r" (nr & 7)); + return oldbit; +} + +_INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + int oldbit; + + addr = (const void *) (((const unsigned char *) addr) + (nr >> 3)); + __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit) + :"m" (EXT2FS_CONST_ADDR),"r" (nr & 7)); + return oldbit; +} + +_INLINE_ __u32 ext2fs_swab32(__u32 val) +{ +#ifdef EXT2FS_REQUIRE_486 + __asm__("bswap %0" : "=r" (val) : "0" (val)); +#else + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (val) + : "0" (val)); +#endif + return val; +} + +_INLINE_ __u16 ext2fs_swab16(__u16 val) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ \ + : "=q" (val) \ + : "0" (val)); \ + return val; +} + +#undef EXT2FS_ADDR + +#endif /* i386 */ + +#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ + (defined(__mc68000__))) + +#define _EXT2_HAVE_ASM_BITOPS_ + +_INLINE_ int ext2fs_set_bit(unsigned int nr,void * addr) +{ + char retval; + + __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +_INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + char retval; + + __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +_INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + char retval; + + __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^7), "a" (addr)); + + return retval; +} + +#endif /* __mc68000__ */ + + +#if !defined(_EXT2_HAVE_ASM_SWAB_) + +_INLINE_ __u16 ext2fs_swab16(__u16 val) +{ + return (val >> 8) | (val << 8); +} + +_INLINE_ __u32 ext2fs_swab32(__u32 val) +{ + return ((val>>24) | ((val>>8)&0xFF00) | + ((val<<8)&0xFF0000) | (val<<24)); +} + +#endif /* !_EXT2_HAVE_ASM_SWAB */ + +_INLINE_ __u64 ext2fs_swab64(__u64 val) +{ + return (ext2fs_swab32(val >> 32) | + (((__u64)ext2fs_swab32(val & 0xFFFFFFFFUL)) << 32)); +} + +_INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + return ext2fs_test_block_bitmap_range(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + ext2fs_mark_block_bitmap_range(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + ext2fs_unmark_block_bitmap_range(bitmap, block, num); +} + +/* 64-bit versions */ + +_INLINE_ int ext2fs_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ int ext2fs_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ int ext2fs_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, block); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap2(ext2fs_block_bitmap bitmap, + blk64_t block) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + block); +} + +_INLINE_ void ext2fs_fast_mark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_mark_generic_bmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ void ext2fs_fast_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_unmark_generic_bmap((ext2fs_generic_bitmap) bitmap, inode); +} + +_INLINE_ int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +_INLINE_ blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start2(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end2(ext2fs_inode_bitmap bitmap) +{ + return ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap); +} + +_INLINE_ int ext2fs_fast_test_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + return ext2fs_test_block_bitmap_range2(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + ext2fs_mark_block_bitmap_range2(bitmap, block, num); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap, + blk64_t block, + unsigned int num) +{ + ext2fs_unmark_block_bitmap_range2(bitmap, block, num); +} + +#undef _INLINE_ +#endif + +#endif diff --git a/lib/libext2fs/orig/blkmap64_ba.c b/lib/libext2fs/orig/blkmap64_ba.c new file mode 100644 index 0000000..395aba9 --- /dev/null +++ b/lib/libext2fs/orig/blkmap64_ba.c @@ -0,0 +1,327 @@ +/* + * blkmap64_ba.c --- Simple bitarray implementation for bitmaps + * + * Copyright (C) 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "bmap64.h" + +/* + * Private data for bit array implementation of bitmap ops. + * Currently, this is just a pointer to our big flat hunk of memory, + * exactly equivalent to the old-skool char * bitmap member. + */ + +struct ext2fs_ba_private_struct { + char *bitarray; +}; + +typedef struct ext2fs_ba_private_struct *ext2fs_ba_private; + +static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp; + errcode_t retval; + size_t size; + + /* + * Since we only have the one pointer, we could just shove our + * private data in the void *private field itself, but then + * we'd have to do a fair bit of rewriting if we ever added a + * field. I'm agnostic. + */ + retval = ext2fs_get_mem(sizeof (ext2fs_ba_private), &bp); + if (retval) + return retval; + + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + + retval = ext2fs_get_mem(size, &bp->bitarray); + if (retval) { + ext2fs_free_mem(&bp); + bp = 0; + return retval; + } + bitmap->private = (void *) bp; + return 0; +} + +static errcode_t ba_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)), + ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp; + errcode_t retval; + size_t size; + + retval = ba_alloc_private_data (bitmap); + if (retval) + return retval; + + bp = (ext2fs_ba_private) bitmap->private; + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + memset(bp->bitarray, 0, size); + + return 0; +} + +static void ba_free_bmap(ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + if (!bp) + return; + + if (bp->bitarray) { + ext2fs_free_mem (&bp->bitarray); + bp->bitarray = 0; + } + ext2fs_free_mem (&bp); + bp = 0; +} + +static errcode_t ba_copy_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap dest) +{ + ext2fs_ba_private src_bp = (ext2fs_ba_private) src->private; + ext2fs_ba_private dest_bp; + errcode_t retval; + size_t size; + + retval = ba_alloc_private_data (dest); + if (retval) + return retval; + + dest_bp = (ext2fs_ba_private) dest->private; + + size = (size_t) (((src->real_end - src->start) / 8) + 1); + memcpy (dest_bp->bitarray, src_bp->bitarray, size); + + return 0; +} + +static errcode_t ba_resize_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, __u64 new_real_end) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bmap->private; + errcode_t retval; + size_t size, new_size; + __u64 bitno; + + /* + * If we're expanding the bitmap, make sure all of the new + * parts of the bitmap are zero. + */ + if (new_end > bmap->end) { + bitno = bmap->real_end; + if (bitno > new_end) + bitno = new_end; + for (; bitno > bmap->end; bitno--) + ext2fs_clear_bit64(bitno - bmap->start, bp->bitarray); + } + if (new_real_end == bmap->real_end) { + bmap->end = new_end; + return 0; + } + + size = ((bmap->real_end - bmap->start) / 8) + 1; + new_size = ((new_real_end - bmap->start) / 8) + 1; + + if (size != new_size) { + retval = ext2fs_resize_mem(size, new_size, &bp->bitarray); + if (retval) + return retval; + } + if (new_size > size) + memset(bp->bitarray + size, 0, new_size - size); + + bmap->end = new_end; + bmap->real_end = new_real_end; + return 0; + +} + +static int ba_mark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_set_bit64(bitno - bitmap->start, bp->bitarray); +} + +static int ba_unmark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_clear_bit64(bitno - bitmap->start, bp->bitarray); +} + +static int ba_test_bmap(ext2fs_generic_bitmap bitmap, __u64 arg) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + + return ext2fs_test_bit64(bitno - bitmap->start, bp->bitarray); +} + +static void ba_mark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + unsigned int i; + + for (i = 0; i < num; i++) + ext2fs_fast_set_bit64(bitno + i - bitmap->start, bp->bitarray); +} + +static void ba_unmark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + blk64_t bitno = (blk64_t) arg; + unsigned int i; + + for (i = 0; i < num; i++) + ext2fs_fast_clear_bit64(bitno + i - bitmap->start, bp->bitarray); +} + +static int ba_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int len) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + __u64 start_byte, len_byte = len >> 3; + unsigned int start_bit, len_bit = len % 8; + unsigned int first_bit = 0; + unsigned int last_bit = 0; + int mark_count = 0; + int mark_bit = 0; + int i; + const char *ADDR; + + ADDR = bp->bitarray; + start -= bitmap->start; + start_byte = start >> 3; + start_bit = start % 8; + + if (start_bit != 0) { + /* + * The compared start block number or start inode number + * is not the first bit in a byte. + */ + mark_count = 8 - start_bit; + if (len < 8 - start_bit) { + mark_count = (int)len; + mark_bit = len + start_bit - 1; + } else + mark_bit = 7; + + for (i = mark_count; i > 0; i--, mark_bit--) + first_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the first byte. + * If there is any marked bit, this function returns 0. + */ + if (first_bit & ADDR[start_byte]) + return 0; + else if (len <= 8 - start_bit) + return 1; + + start_byte++; + len_bit = (len - mark_count) % 8; + len_byte = (len - mark_count) >> 3; + } + + /* + * The compared start block number or start inode number is + * the first bit in a byte. + */ + if (len_bit != 0) { + /* + * The compared end block number or end inode number is + * not the last bit in a byte. + */ + for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--) + last_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the last byte. + * If there is any marked bit, this function returns 0. + */ + if (last_bit & ADDR[start_byte + len_byte]) + return 0; + else if (len_byte == 0) + return 1; + } + + /* Check whether all bytes are 0 */ + return ext2fs_mem_is_zero(ADDR + start_byte, len_byte); +} + + +static errcode_t ba_set_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *in) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memcpy (bp->bitarray + (start >> 3), in, (num + 7) >> 3); + + return 0; +} + +static errcode_t ba_get_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *out) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memcpy (out, bp->bitarray + (start >> 3), (num + 7) >> 3); + + return 0; +} + +static void ba_clear_bmap(ext2fs_generic_bitmap bitmap) +{ + ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private; + + memset(bp->bitarray, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} + +struct ext2_bitmap_ops ext2fs_blkmap64_bitarray = { + .type = EXT2FS_BMAP64_BITARRAY, + .new_bmap = ba_new_bmap, + .free_bmap = ba_free_bmap, + .copy_bmap = ba_copy_bmap, + .resize_bmap = ba_resize_bmap, + .mark_bmap = ba_mark_bmap, + .unmark_bmap = ba_unmark_bmap, + .test_bmap = ba_test_bmap, + .test_clear_bmap_extent = ba_test_clear_bmap_extent, + .mark_bmap_extent = ba_mark_bmap_extent, + .unmark_bmap_extent = ba_unmark_bmap_extent, + .set_bmap_range = ba_set_bmap_range, + .get_bmap_range = ba_get_bmap_range, + .clear_bmap = ba_clear_bmap, +}; diff --git a/lib/libext2fs/orig/blknum.c b/lib/libext2fs/orig/blknum.c new file mode 100644 index 0000000..b3e6dca --- /dev/null +++ b/lib/libext2fs/orig/blknum.c @@ -0,0 +1,477 @@ +/* + * blknum.c --- Functions to handle blk64_t and high/low 64-bit block + * number. + * + * Copyright IBM Corporation, 2007 + * Author Jose R. Santos + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" + +/* + * Return the group # of a block + */ +dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t blk) +{ + return (blk - fs->super->s_first_data_block) / + fs->super->s_blocks_per_group; +} + +/* + * Return the first block (inclusive) in a group + */ +blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group) +{ + return fs->super->s_first_data_block + + ((blk64_t)group * fs->super->s_blocks_per_group); +} + +/* + * Return the last block (inclusive) in a group + */ +blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group) +{ + return (group == fs->group_desc_count - 1 ? + ext2fs_blocks_count(fs->super) - 1 : + ext2fs_group_first_block2(fs, group) + + (fs->super->s_blocks_per_group - 1)); +} + +/* + * Return the inode data block count + */ +blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs, + struct ext2_inode *inode) +{ + return (inode->i_blocks | + ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ? + (__u64) inode->osd2.linux2.l_i_blocks_hi << 32 : 0)) - + (inode->i_file_acl ? fs->blocksize >> 9 : 0); +} + +/* + * Return the inode i_blocks count + */ +blk64_t ext2fs_inode_i_blocks(ext2_filsys fs, + struct ext2_inode *inode) +{ + return (inode->i_blocks | + ((fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ? + (__u64)inode->osd2.linux2.l_i_blocks_hi << 32 : 0)); +} + +/* + * Return the fs block count + */ +blk64_t ext2fs_blocks_count(struct ext2_super_block *super) +{ + return super->s_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_blocks_count_hi << 32 : 0); +} + +/* + * Set the fs block count + */ +void ext2fs_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_blocks_count_hi = (__u64) blk >> 32; +} + +/* + * Add to the current fs block count + */ +void ext2fs_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_blocks_count(super) + blk; + ext2fs_blocks_count_set(super, tmp); +} + +/* + * Return the fs reserved block count + */ +blk64_t ext2fs_r_blocks_count(struct ext2_super_block *super) +{ + return super->s_r_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_r_blocks_count_hi << 32 : 0); +} + +/* + * Set the fs reserved block count + */ +void ext2fs_r_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_r_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_r_blocks_count_hi = (__u64) blk >> 32; +} + +/* + * Add to the current reserved fs block count + */ +void ext2fs_r_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_r_blocks_count(super) + blk; + ext2fs_r_blocks_count_set(super, tmp); +} + +/* + * Return the fs free block count + */ +blk64_t ext2fs_free_blocks_count(struct ext2_super_block *super) +{ + return super->s_free_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_free_blocks_hi << 32 : 0); +} + +/* + * Set the fs free block count + */ +void ext2fs_free_blocks_count_set(struct ext2_super_block *super, blk64_t blk) +{ + super->s_free_blocks_count = blk; + if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + super->s_free_blocks_hi = (__u64) blk >> 32; +} + +/* + * Add to the current free fs block count + */ +void ext2fs_free_blocks_count_add(struct ext2_super_block *super, blk64_t blk) +{ + blk64_t tmp; + tmp = ext2fs_free_blocks_count(super) + blk; + ext2fs_free_blocks_count_set(super, tmp); +} + +/* + * Get a pointer to a block group descriptor. We need the explicit + * pointer to the group desc for code that swaps block group + * descriptors before writing them out, as it wants to make a copy and + * do the swap there. + */ +struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group) +{ + if (fs->super->s_desc_size >= EXT2_MIN_DESC_SIZE_64BIT) + return (struct ext2_group_desc *) + ((struct ext4_group_desc *) gdp + group); + else + return (struct ext2_group_desc *) gdp + group; +} + +/* Do the same but as an ext4 group desc for internal use here */ +static struct ext4_group_desc *ext4fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group) +{ + return (struct ext4_group_desc *)ext2fs_group_desc(fs, gdp, group); +} + +/* + * Return the block bitmap block of a group + */ +blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_block_bitmap | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64)gdp->bg_block_bitmap_hi << 32 : 0); +} + +/* + * Set the block bitmap block of a group + */ +void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_block_bitmap = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_block_bitmap_hi = (__u64) blk >> 32; +} + +/* + * Return the inode bitmap block of a group + */ +blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_inode_bitmap | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) gdp->bg_inode_bitmap_hi << 32 : 0); +} + +/* + * Set the inode bitmap block of a group + */ +void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_inode_bitmap = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_inode_bitmap_hi = (__u64) blk >> 32; +} + +/* + * Return the inode table block of a group + */ +blk64_t ext2fs_inode_table_loc(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_inode_table | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) gdp->bg_inode_table_hi << 32 : 0); +} + +/* + * Set the inode table block of a group + */ +void ext2fs_inode_table_loc_set(ext2_filsys fs, dgrp_t group, blk64_t blk) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_inode_table = blk; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_inode_table_hi = (__u64) blk >> 32; +} + +/* + * Return the free blocks count of a group + */ +__u32 ext2fs_bg_free_blocks_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_free_blocks_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_free_blocks_count_hi << 16 : 0); +} + +/* + * Set the free blocks count of a group + */ +void ext2fs_bg_free_blocks_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_free_blocks_count = n; + + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_free_blocks_count_hi = (__u32) n >> 16; +} + +/* + * Return the free inodes count of a group + */ +__u32 ext2fs_bg_free_inodes_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_free_inodes_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_free_inodes_count_hi << 16 : 0); +} + +/* + * Set the free inodes count of a group + */ +void ext2fs_bg_free_inodes_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_free_inodes_count = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_free_inodes_count_hi = (__u32) n >> 16; +} + +/* + * Return the used dirs count of a group + */ +__u32 ext2fs_bg_used_dirs_count(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_used_dirs_count | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_used_dirs_count_hi << 16 : 0); +} + +/* + * Set the used dirs count of a group + */ +void ext2fs_bg_used_dirs_count_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_used_dirs_count = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_used_dirs_count_hi = (__u32) n >> 16; +} + +/* + * Return the unused inodes count of a group + */ +__u32 ext2fs_bg_itable_unused(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_itable_unused | + (fs->super->s_feature_incompat + & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u32) gdp->bg_itable_unused_hi << 16 : 0); +} + +/* + * Set the unused inodes count of a group + */ +void ext2fs_bg_itable_unused_set(ext2_filsys fs, dgrp_t group, __u32 n) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_itable_unused = n; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) + gdp->bg_itable_unused_hi = (__u32) n >> 16; +} + +/* + * Get the flags for this block group + */ +__u16 ext2fs_bg_flags(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_flags; +} + +/* + * Zero out the flags for this block group + */ +void ext2fs_bg_flags_zap(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags = 0; + return; +} + +/* + * Get the value of a particular flag for this block group + */ +int ext2fs_bg_flags_test(ext2_filsys fs, dgrp_t group, __u16 bg_flag) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_flags & bg_flag; +} + +/* + * Set a flag or set of flags for this block group + */ +void ext2fs_bg_flags_set(ext2_filsys fs, dgrp_t group, __u16 bg_flags) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags |= bg_flags; + return; +} + +/* + * Clear a flag or set of flags for this block group + */ +void ext2fs_bg_flags_clear(ext2_filsys fs, dgrp_t group, __u16 bg_flags) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_flags &= ~bg_flags; + return; +} + +/* + * Get the checksum for this block group + */ +__u16 ext2fs_bg_checksum(ext2_filsys fs, dgrp_t group) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + return gdp->bg_checksum; +} + +/* + * Set the checksum for this block group to a previously calculated value + */ +void ext2fs_bg_checksum_set(ext2_filsys fs, dgrp_t group, __u16 checksum) +{ + struct ext4_group_desc *gdp; + + gdp = ext4fs_group_desc(fs, fs->group_desc, group); + gdp->bg_checksum = checksum; + return; +} + +/* + * Get the acl block of a file + * + * XXX Ignoring 64-bit file system flag - most places where this is + * called don't have access to the fs struct, and the high bits should + * be 0 in the non-64-bit case anyway. + */ +blk64_t ext2fs_file_acl_block(const struct ext2_inode *inode) +{ + return (inode->i_file_acl | + (__u64) inode->osd2.linux2.l_i_file_acl_high << 32); +} + +/* + * Set the acl block of a file + */ +void ext2fs_file_acl_block_set(struct ext2_inode *inode, blk64_t blk) +{ + inode->i_file_acl = blk; + inode->osd2.linux2.l_i_file_acl_high = (__u64) blk >> 32; +} + diff --git a/lib/libext2fs/orig/block.c b/lib/libext2fs/orig/block.c new file mode 100644 index 0000000..0e4ec77 --- /dev/null +++ b/lib/libext2fs/orig/block.c @@ -0,0 +1,623 @@ +/* + * block.c --- iterate over all blocks in an inode + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct block_context { + ext2_filsys fs; + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t bcount, + blk64_t ref_blk, + int ref_offset, + void *priv_data); + e2_blkcnt_t bcount; + int bsize; + int flags; + errcode_t errcode; + char *ind_buf; + char *dind_buf; + char *tind_buf; + void *priv_data; +}; + +#define check_for_ro_violation_return(ctx, ret) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + return ret; \ + } \ + } while (0) + +#define check_for_ro_violation_goto(ctx, ret, label) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + goto label; \ + } \ + } while (0) + +static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) { + blk64 = *ind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + *ind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*ind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit; + return ret; + } + if (*ind_block >= ext2fs_blocks_count(ctx->fs->super) || + *ind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_IND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->ind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + blk64 = *block_nr; + flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + *block_nr = blk64; + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + if (*block_nr == 0) + goto skip_sparse; + blk64 = *block_nr; + flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + *block_nr = blk64; + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + skip_sparse: + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *ind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + *ind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) { + blk64 = *dind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + *dind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*dind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit; + return ret; + } + if (*dind_block >= ext2fs_blocks_count(ctx->fs->super) || + *dind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->dind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit; + continue; + } + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *dind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + *dind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + blk64_t blk64; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) { + blk64 = *tind_block; + ret = (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + *tind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + if (!*tind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit*limit; + return ret; + } + if (*tind_block >= ext2fs_blocks_count(ctx->fs->super) || + *tind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->tind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit*limit; + continue; + } + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) { + blk64 = *tind_block; + ret |= (*ctx->func)(ctx->fs, &blk64, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + *tind_block = blk64; + } + check_for_ro_violation_return(ctx, ret); + return ret; +} + +errcode_t ext2fs_block_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + int i; + int r, ret = 0; + struct ext2_inode inode; + errcode_t retval; + struct block_context ctx; + int limit; + blk64_t blk64; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ctx.errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx.errcode) + return ctx.errcode; + + /* + * Check to see if we need to limit large files + */ + if (flags & BLOCK_FLAG_NO_LARGE) { + if (!LINUX_S_ISDIR(inode.i_mode) && + (inode.i_size_high != 0)) + return EXT2_ET_FILE_TOO_BIG; + } + + limit = fs->blocksize >> 2; + + ctx.fs = fs; + ctx.func = func; + ctx.priv_data = priv_data; + ctx.flags = flags; + ctx.bcount = 0; + if (block_buf) { + ctx.ind_buf = block_buf; + } else { + retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf); + if (retval) + return retval; + } + ctx.dind_buf = ctx.ind_buf + fs->blocksize; + ctx.tind_buf = ctx.dind_buf + fs->blocksize; + + /* + * Iterate over the HURD translator block (if present) + */ + if ((fs->super->s_creator_os == EXT2_OS_HURD) && + !(flags & BLOCK_FLAG_DATA_ONLY)) { + if (inode.osd1.hurd1.h_i_translator) { + blk64 = inode.osd1.hurd1.h_i_translator; + ret |= (*ctx.func)(fs, &blk64, + BLOCK_COUNT_TRANSLATOR, + 0, 0, priv_data); + inode.osd1.hurd1.h_i_translator = (blk_t) blk64; + if (ret & BLOCK_ABORT) + goto abort_exit; + check_for_ro_violation_goto(&ctx, ret, abort_exit); + } + } + + if (inode.i_flags & EXT4_EXTENTS_FL) { + ext2_extent_handle_t handle; + struct ext2fs_extent extent; + e2_blkcnt_t blockcnt = 0; + blk64_t blk, new_blk; + int op = EXT2_EXTENT_ROOT; + int uninit; + unsigned int j; + + ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle); + if (ctx.errcode) + goto abort_exit; + + while (1) { + ctx.errcode = ext2fs_extent_get(handle, op, &extent); + if (ctx.errcode) { + if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT) + break; + ctx.errcode = 0; + if (!(flags & BLOCK_FLAG_APPEND)) + break; + next_block_set: + blk = 0; + r = (*ctx.func)(fs, &blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt++, + (blk64_t) blk, 0); + if (ctx.errcode || (ret & BLOCK_ABORT)) + break; + if (blk) + goto next_block_set; + } + break; + } + + op = EXT2_EXTENT_NEXT; + blk = extent.e_pblk; + if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { + if (ctx.flags & BLOCK_FLAG_DATA_ONLY) + continue; + if ((!(extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) || + ((extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) { + ret |= (*ctx.func)(fs, &blk, + -1, 0, 0, priv_data); + if (ret & BLOCK_CHANGED) { + extent.e_pblk = blk; + ctx.errcode = + ext2fs_extent_replace(handle, 0, &extent); + if (ctx.errcode) + break; + } + } + continue; + } + uninit = 0; + if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + uninit = EXT2_EXTENT_SET_BMAP_UNINIT; + for (blockcnt = extent.e_lblk, j = 0; + j < extent.e_len; + blk++, blockcnt++, j++) { + new_blk = blk; + r = (*ctx.func)(fs, &new_blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt, + new_blk, uninit); + if (ctx.errcode) + goto extent_errout; + } + if (ret & BLOCK_ABORT) + break; + } + } + + extent_errout: + ext2fs_extent_free(handle); + ret |= BLOCK_ERROR | BLOCK_ABORT; + goto errout; + } + + /* + * Iterate over normal data blocks + */ + for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { + if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { + blk64 = inode.i_block[i]; + ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, + priv_data); + inode.i_block[i] = (blk_t) blk64; + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } + check_for_ro_violation_goto(&ctx, ret, abort_exit); + if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK], + 0, EXT2_IND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit; + if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK], + 0, EXT2_DIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit * limit; + if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK], + 0, EXT2_TIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + +abort_exit: + if (ret & BLOCK_CHANGED) { + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + ret |= BLOCK_ERROR; + ctx.errcode = retval; + } + } +errout: + if (!block_buf) + ext2fs_free_mem(&ctx.ind_buf); + + return (ret & BLOCK_ERROR) ? ctx.errcode : 0; +} + +/* + * Emulate the old ext2fs_block_iterate function! + */ + +struct xlate64 { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data); + void *real_private; +}; + +static int xlate64_func(ext2_filsys fs, blk64_t *blocknr, + e2_blkcnt_t blockcnt, blk64_t ref_blk, + int ref_offset, void *priv_data) +{ + struct xlate64 *xl = (struct xlate64 *) priv_data; + int ret; + blk_t block32 = *blocknr; + + ret = (*xl->func)(fs, &block32, blockcnt, (blk_t) ref_blk, ref_offset, + xl->real_private); + *blocknr = block32; + return ret; +} + +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + struct xlate64 xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_block_iterate3(fs, ino, flags, block_buf, + xlate64_func, &xl); +} + + +struct xlate { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int bcount, + void *priv_data); + void *real_private; +}; + +#ifdef __TURBOC__ + #pragma argsused +#endif +static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); +} + +errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, + block_buf, xlate_func, &xl); +} + diff --git a/lib/libext2fs/orig/bmap.c b/lib/libext2fs/orig/bmap.c new file mode 100644 index 0000000..fbcb375 --- /dev/null +++ b/lib/libext2fs/orig/bmap.c @@ -0,0 +1,335 @@ +/* + * bmap.c --- logical to physical block mapping + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) +#define _BMAP_INLINE_ __inline__ +#else +#define _BMAP_INLINE_ +#endif + +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); + +#define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) + +static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, + blk_t ind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + errcode_t retval; + blk_t b; + + if (!ind) { + if (flags & BMAP_SET) + return EXT2_ET_SET_BMAP_NO_IND; + *ret_blk = 0; + return 0; + } + retval = io_channel_read_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + if (flags & BMAP_SET) { + b = *ret_blk; +#ifdef WORDS_BIGENDIAN + b = ext2fs_swab32(b); +#endif + ((blk_t *) block_buf)[nr] = b; + return io_channel_write_blk(fs->io, ind, 1, block_buf); + } + + b = ((blk_t *) block_buf)[nr]; + +#ifdef WORDS_BIGENDIAN + b = ext2fs_swab32(b); +#endif + + if (!b && (flags & BMAP_ALLOC)) { + b = nr ? ((blk_t *) block_buf)[nr-1] : 0; + retval = ext2fs_alloc_block(fs, b, + block_buf + fs->blocksize, &b); + if (retval) + return retval; + +#ifdef WORDS_BIGENDIAN + ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); +#else + ((blk_t *) block_buf)[nr] = b; +#endif + + retval = io_channel_write_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + (*blocks_alloc)++; + } + + *ret_blk = b; + return 0; +} + +static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, + blk_t dind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, + blk_t tind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk64_t block, + int *ret_flags, blk64_t *phys_blk) +{ + struct ext2_inode inode_buf; + ext2_extent_handle_t handle = 0; + blk_t addr_per_block; + blk_t b, blk32; + char *buf = 0; + errcode_t retval = 0; + int blocks_alloc = 0, inode_dirty = 0; + + if (!(bmap_flags & BMAP_SET)) + *phys_blk = 0; + + if (ret_flags) + *ret_flags = 0; + + /* Read inode structure if necessary */ + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + addr_per_block = (blk_t) fs->blocksize >> 2; + + if (inode->i_flags & EXT4_EXTENTS_FL) { + struct ext2fs_extent extent; + unsigned int offset; + + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + goto done; + if (bmap_flags & BMAP_SET) { + retval = ext2fs_extent_set_bmap(handle, block, + *phys_blk, 0); + goto done; + } + retval = ext2fs_extent_goto(handle, block); + if (retval) { + /* If the extent is not found, return phys_blk = 0 */ + if (retval == EXT2_ET_EXTENT_NOT_FOUND) + goto got_block; + goto done; + } + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + offset = block - extent.e_lblk; + if (block >= extent.e_lblk && (offset <= extent.e_len)) { + *phys_blk = extent.e_pblk + offset; + if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + *ret_flags |= BMAP_RET_UNINIT; + } + got_block: + if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + retval = ext2fs_extent_set_bmap(handle, block, + (blk64_t) b, 0); + if (retval) + goto done; + /* Update inode after setting extent */ + retval = ext2fs_read_inode(fs, ino, inode); + if (retval) + return retval; + blocks_alloc++; + *phys_blk = b; + } + retval = 0; + goto done; + } + + if (!block_buf) { + retval = ext2fs_get_array(2, fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + if (block < EXT2_NDIR_BLOCKS) { + if (bmap_flags & BMAP_SET) { + b = *phys_blk; + inode_bmap(inode, block) = b; + inode_dirty++; + goto done; + } + + *phys_blk = inode_bmap(inode, block); + b = block ? inode_bmap(inode, block-1) : 0; + + if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, block) = b; + blocks_alloc++; + *phys_blk = b; + } + goto done; + } + + /* Indirect block */ + block -= EXT2_NDIR_BLOCKS; + blk32 = *phys_blk; + if (block < addr_per_block) { + b = inode_bmap(inode, EXT2_IND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK-1); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_IND_BLOCK) = b; + blocks_alloc++; + } + retval = block_ind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; + goto done; + } + + /* Doubly indirect block */ + block -= addr_per_block; + if (block < addr_per_block * addr_per_block) { + b = inode_bmap(inode, EXT2_DIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_DIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_dind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; + goto done; + } + + /* Triply indirect block */ + block -= addr_per_block * addr_per_block; + b = inode_bmap(inode, EXT2_TIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_DIND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_TIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_tind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, &blk32); + if (retval == 0) + *phys_blk = blk32; +done: + if (buf) + ext2fs_free_mem(&buf); + if (handle) + ext2fs_extent_free(handle); + if ((retval == 0) && (blocks_alloc || inode_dirty)) { + ext2fs_iblk_add_blocks(fs, inode, blocks_alloc); + retval = ext2fs_write_inode(fs, ino, inode); + } + return retval; +} + +errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk_t block, + blk_t *phys_blk) +{ + errcode_t ret; + blk64_t ret_blk = *phys_blk; + + ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block, + 0, &ret_blk); + if (ret) + return ret; + if (ret_blk >= ((long long) 1 << 32)) + return EOVERFLOW; + *phys_blk = ret_blk; + return 0; +} diff --git a/lib/libext2fs/orig/bmap64.h b/lib/libext2fs/orig/bmap64.h new file mode 100644 index 0000000..b0aa84c --- /dev/null +++ b/lib/libext2fs/orig/bmap64.h @@ -0,0 +1,61 @@ +/* + * bmap64.h --- 64-bit bitmap structure + * + * Copyright (C) 2007, 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct ext2fs_struct_generic_bitmap { + errcode_t magic; + ext2_filsys fs; + struct ext2_bitmap_ops *bitmap_ops; + int flags; + __u64 start, end; + __u64 real_end; + char *description; + void *private; + errcode_t base_error_code; +}; + +#define EXT2FS_IS_32_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP)) + +#define EXT2FS_IS_64_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP64)) + +struct ext2_bitmap_ops { + int type; + /* Generic bmap operators */ + errcode_t (*new_bmap)(ext2_filsys fs, ext2fs_generic_bitmap bmap); + void (*free_bmap)(ext2fs_generic_bitmap bitmap); + errcode_t (*copy_bmap)(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap dest); + errcode_t (*resize_bmap)(ext2fs_generic_bitmap bitmap, + __u64 new_end, + __u64 new_real_end); + /* bit set/test operators */ + int (*mark_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + int (*unmark_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + int (*test_bmap)(ext2fs_generic_bitmap bitmap, __u64 arg); + void (*mark_bmap_extent)(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num); + void (*unmark_bmap_extent)(ext2fs_generic_bitmap bitmap, __u64 arg, + unsigned int num); + int (*test_clear_bmap_extent)(ext2fs_generic_bitmap bitmap, + __u64 arg, unsigned int num); + errcode_t (*set_bmap_range)(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *in); + errcode_t (*get_bmap_range)(ext2fs_generic_bitmap bitmap, + __u64 start, size_t num, void *out); + void (*clear_bmap)(ext2fs_generic_bitmap bitmap); +}; + +extern struct ext2_bitmap_ops ext2fs_blkmap64_bitarray; diff --git a/lib/libext2fs/orig/bmove.c b/lib/libext2fs/orig/bmove.c new file mode 100644 index 0000000..deabf38 --- /dev/null +++ b/lib/libext2fs/orig/bmove.c @@ -0,0 +1,166 @@ +/* + * bmove.c --- Move blocks around to make way for a particular + * filesystem structure. + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_TIME_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +struct process_block_struct { + ext2_ino_t ino; + struct ext2_inode * inode; + ext2fs_block_bitmap reserve; + ext2fs_block_bitmap alloc_map; + errcode_t error; + char *buf; + int add_dir; + int flags; +}; + +static int process_block(ext2_filsys fs, blk64_t *block_nr, + e2_blkcnt_t blockcnt, blk64_t ref_block, + int ref_offset, void *priv_data) +{ + struct process_block_struct *pb; + errcode_t retval; + int ret; + blk64_t block, orig; + + pb = (struct process_block_struct *) priv_data; + block = orig = *block_nr; + ret = 0; + + /* + * Let's see if this is one which we need to relocate + */ + if (ext2fs_test_block_bitmap2(pb->reserve, block)) { + do { + if (++block >= ext2fs_blocks_count(fs->super)) + block = fs->super->s_first_data_block; + if (block == orig) { + pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; + return BLOCK_ABORT; + } + } while (ext2fs_test_block_bitmap2(pb->reserve, block) || + ext2fs_test_block_bitmap2(pb->alloc_map, block)); + + retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + retval = io_channel_write_blk64(fs->io, block, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + *block_nr = block; + ext2fs_mark_block_bitmap2(pb->alloc_map, block); + ret = BLOCK_CHANGED; + if (pb->flags & EXT2_BMOVE_DEBUG) + printf("ino=%u, blockcnt=%lld, %llu->%llu\n", + (unsigned) pb->ino, blockcnt, + (unsigned long long) orig, + (unsigned long long) block); + } + if (pb->add_dir) { + retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, + block, blockcnt); + if (retval) { + pb->error = retval; + ret |= BLOCK_ABORT; + } + } + return ret; +} + +errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags) +{ + ext2_ino_t ino; + struct ext2_inode inode; + errcode_t retval; + struct process_block_struct pb; + ext2_inode_scan scan; + char *block_buf; + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) + return retval; + + pb.reserve = reserve; + pb.error = 0; + pb.alloc_map = alloc_map ? alloc_map : fs->block_map; + pb.flags = flags; + + retval = ext2fs_get_array(4, fs->blocksize, &block_buf); + if (retval) + return retval; + pb.buf = block_buf + fs->blocksize * 3; + + /* + * If GET_DBLIST is set in the flags field, then we should + * gather directory block information while we're doing the + * block move. + */ + if (flags & EXT2_BMOVE_GET_DBLIST) { + if (fs->dblist) { + ext2fs_free_dblist(fs->dblist); + fs->dblist = NULL; + } + retval = ext2fs_init_dblist(fs, 0); + if (retval) + return retval; + } + + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) + return retval; + + while (ino) { + if ((inode.i_links_count == 0) || + !ext2fs_inode_has_valid_blocks(&inode)) + goto next; + + pb.ino = ino; + pb.inode = &inode; + + pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && + flags & EXT2_BMOVE_GET_DBLIST); + + retval = ext2fs_block_iterate3(fs, ino, 0, block_buf, + process_block, &pb); + if (retval) + return retval; + if (pb.error) + return pb.error; + + next: + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + goto next; + } + return 0; +} + diff --git a/lib/libext2fs/orig/brel.h b/lib/libext2fs/orig/brel.h new file mode 100644 index 0000000..a0dd5b9 --- /dev/null +++ b/lib/libext2fs/orig/brel.h @@ -0,0 +1,86 @@ +/* + * brel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +struct ext2_block_relocate_entry { + blk_t new; + __s16 offset; + __u16 flags; + union { + blk_t block_ref; + ext2_ino_t inode_ref; + } owner; +}; + +#define RELOCATE_TYPE_REF 0x0007 +#define RELOCATE_BLOCK_REF 0x0001 +#define RELOCATE_INODE_REF 0x0002 + +typedef struct ext2_block_relocation_table *ext2_brel; + +struct ext2_block_relocation_table { + __u32 magic; + char *name; + blk_t current; + void *priv_data; + + /* + * Add a block relocation entry. + */ + errcode_t (*put)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Get a block relocation entry. + */ + errcode_t (*get)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Initialize for iterating over the block relocation entries. + */ + errcode_t (*start_iter)(ext2_brel brel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); + + /* + * Move the inode relocation table from one block number to + * another. + */ + errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); + + /* + * Remove a block relocation entry. + */ + errcode_t (*delete)(ext2_brel brel, blk_t old); + + + /* + * Free the block relocation table. + */ + errcode_t (*free)(ext2_brel brel); +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *brel); + +#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) +#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) +#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) +#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) +#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) +#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) +#define ext2fs_brel_free(brel) ((brel)->free((brel))) + diff --git a/lib/libext2fs/orig/brel_ma.c b/lib/libext2fs/orig/brel_ma.c new file mode 100644 index 0000000..1a55702 --- /dev/null +++ b/lib/libext2fs/orig/brel_ma.c @@ -0,0 +1,198 @@ +/* + * brel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * TODO: rewrite to not use a direct array!!! (Fortunately this + * module isn't really used yet.) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "brel.h" + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_start_iter(ext2_brel brel); +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); +static errcode_t bma_delete(ext2_brel brel, blk_t old); +static errcode_t bma_free(ext2_brel brel); + +struct brel_ma { + __u32 magic; + blk_t max_block; + struct ext2_block_relocate_entry *entries; +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *new_brel) +{ + ext2_brel brel = 0; + errcode_t retval; + struct brel_ma *ma = 0; + size_t size; + + *new_brel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), + &brel); + if (retval) + goto errout; + memset(brel, 0, sizeof(struct ext2_block_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &brel->name); + if (retval) + goto errout; + strcpy(brel->name, name); + + retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct brel_ma)); + brel->priv_data = ma; + + size = (size_t) (sizeof(struct ext2_block_relocate_entry) * + (max_block+1)); + retval = ext2fs_get_array(max_block+1, + sizeof(struct ext2_block_relocate_entry), &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + ma->max_block = max_block; + + /* + * Fill in the brel data structure + */ + brel->put = bma_put; + brel->get = bma_get; + brel->start_iter = bma_start_iter; + brel->next = bma_next; + brel->move = bma_move; + brel->delete = bma_delete; + brel->free = bma_free; + + *new_brel = brel; + return 0; + +errout: + bma_free(brel); + return retval; +} + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + ma->entries[(unsigned)old] = *ent; + return 0; +} + +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + *ent = ma->entries[old]; + return 0; +} + +static errcode_t bma_start_iter(ext2_brel brel) +{ + brel->current = 0; + return 0; +} + +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + while (++brel->current < ma->max_block) { + if (ma->entries[(unsigned)brel->current].new == 0) + continue; + *old = brel->current; + *ent = ma->entries[(unsigned)brel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if ((old > ma->max_block) || (new > ma->max_block)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)new] = ma->entries[old]; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_delete(ext2_brel brel, blk_t old) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_free(ext2_brel brel) +{ + struct brel_ma *ma; + + if (!brel) + return 0; + + ma = brel->priv_data; + + if (ma) { + if (ma->entries) + ext2fs_free_mem(&ma->entries); + ext2fs_free_mem(&ma); + } + if (brel->name) + ext2fs_free_mem(&brel->name); + ext2fs_free_mem(&brel); + return 0; +} diff --git a/lib/libext2fs/orig/check_desc.c b/lib/libext2fs/orig/check_desc.c new file mode 100644 index 0000000..7929cd9 --- /dev/null +++ b/lib/libext2fs/orig/check_desc.c @@ -0,0 +1,103 @@ +/* + * check_desc.c --- Check the group descriptors of an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This routine sanity checks the group descriptors + */ +errcode_t ext2fs_check_desc(ext2_filsys fs) +{ + ext2fs_block_bitmap bmap; + errcode_t retval; + dgrp_t i; + blk64_t first_block = fs->super->s_first_data_block; + blk64_t last_block = ext2fs_blocks_count(fs->super)-1; + blk64_t blk, b; + int j; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_allocate_block_bitmap(fs, "check_desc map", &bmap); + if (retval) + return retval; + + for (i = 0; i < fs->group_desc_count; i++) + ext2fs_reserve_super_and_bgd(fs, i, bmap); + + for (i = 0; i < fs->group_desc_count; i++) { + if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_FLEX_BG)) { + first_block = ext2fs_group_first_block2(fs, i); + last_block = ext2fs_group_last_block2(fs, i); + if (i == (fs->group_desc_count - 1)) + last_block = ext2fs_blocks_count(fs->super)-1; + } + + /* + * Check to make sure the block bitmap for group is sane + */ + blk = ext2fs_block_bitmap_loc(fs, i); + if (blk < first_block || blk > last_block || + ext2fs_test_block_bitmap2(bmap, blk)) { + retval = EXT2_ET_GDESC_BAD_BLOCK_MAP; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, blk); + + /* + * Check to make sure the inode bitmap for group is sane + */ + blk = ext2fs_inode_bitmap_loc(fs, i); + if (blk < first_block || blk > last_block || + ext2fs_test_block_bitmap2(bmap, blk)) { + retval = EXT2_ET_GDESC_BAD_INODE_MAP; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, blk); + + /* + * Check to make sure the inode table for group is sane + */ + blk = ext2fs_inode_table_loc(fs, i); + if (blk < first_block || + ((blk + fs->inode_blocks_per_group - 1) > last_block)) { + retval = EXT2_ET_GDESC_BAD_INODE_TABLE; + goto errout; + } + for (j = 0, b = blk; j < fs->inode_blocks_per_group; + j++, b++) { + if (ext2fs_test_block_bitmap2(bmap, b)) { + retval = EXT2_ET_GDESC_BAD_INODE_TABLE; + goto errout; + } + ext2fs_mark_block_bitmap2(bmap, b); + } + } +errout: + ext2fs_free_block_bitmap(bmap); + return retval; +} diff --git a/lib/libext2fs/orig/closefs.c b/lib/libext2fs/orig/closefs.c new file mode 100644 index 0000000..8242622 --- /dev/null +++ b/lib/libext2fs/orig/closefs.c @@ -0,0 +1,461 @@ +/* + * closefs.c --- close an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2fs_bg_has_super(ext2_filsys fs, int group_block) +{ + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + return 1; + + if (test_root(group_block, 3) || (test_root(group_block, 5)) || + test_root(group_block, 7)) + return 1; + + return 0; +} + +/* + * ext2fs_super_and_bgd_loc2() + * @fs: ext2 fs pointer + * @group given block group + * @ret_super_blk: if !NULL, returns super block location + * @ret_old_desc_blk: if !NULL, returns location of the old block + * group descriptor + * @ret_new_desc_blk: if !NULL, returns location of meta_bg block + * group descriptor + * @ret_used_blks: if !NULL, returns number of blocks used by + * super block and group_descriptors. + * + * Returns errcode_t of 0 + */ +errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, + dgrp_t group, + blk64_t *ret_super_blk, + blk64_t *ret_old_desc_blk, + blk64_t *ret_new_desc_blk, + blk_t *ret_used_blks) +{ + blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; + unsigned int meta_bg, meta_bg_size; + blk_t numblocks = 0; + blk64_t old_desc_blocks; + int has_super; + + group_block = ext2fs_group_first_block2(fs, group); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + has_super = ext2fs_bg_has_super(fs, group); + + if (has_super) { + super_blk = group_block; + numblocks++; + } + meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); + meta_bg = group / meta_bg_size; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (meta_bg < fs->super->s_first_meta_bg)) { + if (has_super) { + old_desc_blk = group_block + 1; + numblocks += old_desc_blocks; + } + } else { + if (((group % meta_bg_size) == 0) || + ((group % meta_bg_size) == 1) || + ((group % meta_bg_size) == (meta_bg_size-1))) { + if (has_super) + has_super = 1; + new_desc_blk = group_block + has_super; + numblocks++; + } + } + + if (ret_super_blk) + *ret_super_blk = super_blk; + if (ret_old_desc_blk) + *ret_old_desc_blk = old_desc_blk; + if (ret_new_desc_blk) + *ret_new_desc_blk = new_desc_blk; + if (ret_used_blks) + *ret_used_blks = numblocks; + + return 0; +} + +/* + * This function returns the location of the superblock, block group + * descriptors for a given block group. It currently returns the + * number of free blocks assuming that inode table and allocation + * bitmaps will be in the group. This is not necessarily the case + * when the flex_bg feature is enabled, so callers should take care! + * It was only really intended for use by mke2fs, and even there it's + * not that useful. + * + * The ext2fs_super_and_bgd_loc2() function is 64-bit block number + * capable and returns the number of blocks used by super block and + * group descriptors. + */ +int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg) +{ + blk64_t ret_super_blk2; + blk64_t ret_old_desc_blk2; + blk64_t ret_new_desc_blk2; + blk_t ret_used_blks; + blk_t numblocks; + unsigned int meta_bg_size; + + ext2fs_super_and_bgd_loc2(fs, group, &ret_super_blk2, + &ret_old_desc_blk2, + &ret_new_desc_blk2, + &ret_used_blks); + + if (group == fs->group_desc_count-1) { + numblocks = (ext2fs_blocks_count(fs->super) - + (blk64_t) fs->super->s_first_data_block) % + (blk64_t) fs->super->s_blocks_per_group; + if (!numblocks) + numblocks = fs->super->s_blocks_per_group; + } else + numblocks = fs->super->s_blocks_per_group; + + if (ret_super_blk) + *ret_super_blk = (blk_t)ret_super_blk2; + if (ret_old_desc_blk) + *ret_old_desc_blk = (blk_t)ret_old_desc_blk2; + if (ret_new_desc_blk) + *ret_new_desc_blk = (blk_t)ret_new_desc_blk2; + if (ret_meta_bg) { + meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); + *ret_meta_bg = group / meta_bg_size; + } + + numblocks -= 2 + fs->inode_blocks_per_group + ret_used_blks; + + return numblocks; +} + +/* + * This function forces out the primary superblock. We need to only + * write out those fields which we have changed, since if the + * filesystem is mounted, it may have changed some of the other + * fields. + * + * It takes as input a superblock which has already been byte swapped + * (if necessary). + * + */ +static errcode_t write_primary_superblock(ext2_filsys fs, + struct ext2_super_block *super) +{ + __u16 *old_super, *new_super; + int check_idx, write_idx, size; + errcode_t retval; + + if (!fs->io->manager->write_byte || !fs->orig_super) { + fallback: + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + retval = io_channel_write_blk64(fs->io, 1, -SUPERBLOCK_SIZE, + super); + io_channel_set_blksize(fs->io, fs->blocksize); + return retval; + } + + old_super = (__u16 *) fs->orig_super; + new_super = (__u16 *) super; + + for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { + if (old_super[check_idx] == new_super[check_idx]) + continue; + write_idx = check_idx; + for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) + if (old_super[check_idx] == new_super[check_idx]) + break; + size = 2 * (check_idx - write_idx); +#if 0 + printf("Writing %d bytes starting at %d\n", + size, write_idx*2); +#endif + retval = io_channel_write_byte(fs->io, + SUPERBLOCK_OFFSET + (2 * write_idx), size, + new_super + write_idx); + if (retval == EXT2_ET_UNIMPLEMENTED) + goto fallback; + if (retval) + return retval; + } + memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); + return 0; +} + + +/* + * Updates the revision to EXT2_DYNAMIC_REV + */ +void ext2fs_update_dynamic_rev(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + + if (sb->s_rev_level > EXT2_GOOD_OLD_REV) + return; + + sb->s_rev_level = EXT2_DYNAMIC_REV; + sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + /* s_uuid is handled by e2fsck already */ + /* other fields should be left alone */ +} + +static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, + blk_t group_block, + struct ext2_super_block *super_shadow) +{ + dgrp_t sgrp = group; + + if (sgrp > ((1 << 16) - 1)) + sgrp = (1 << 16) - 1; +#ifdef WORDS_BIGENDIAN + super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); +#else + fs->super->s_block_group_nr = sgrp; +#endif + + return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, + super_shadow); +} + +errcode_t ext2fs_flush(ext2_filsys fs) +{ + dgrp_t i; + errcode_t retval; + unsigned long fs_state; + __u32 feature_incompat; + struct ext2_super_block *super_shadow = 0; + struct ext2_group_desc *group_shadow = 0; +#ifdef WORDS_BIGENDIAN + struct ext2_group_desc *gdp; + dgrp_t j; +#endif + char *group_ptr; + int old_desc_blocks; + struct ext2fs_numeric_progress_struct progress; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs_state = fs->super->s_state; + feature_incompat = fs->super->s_feature_incompat; + + fs->super->s_wtime = fs->now ? fs->now : time(NULL); + fs->super->s_block_group_nr = 0; +#ifdef WORDS_BIGENDIAN + retval = EXT2_ET_NO_MEMORY; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); + if (retval) + goto errout; + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &group_shadow); + if (retval) + goto errout; + memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize * + fs->desc_blocks); + + /* swap the group descriptors */ + for (j=0; j < fs->group_desc_count; j++) { + gdp = ext2fs_group_desc(fs, (struct opaque_ext2_group_desc *) group_shadow, j); + ext2fs_swap_group_desc2(fs, gdp); + } +#else + super_shadow = fs->super; + group_shadow = ext2fs_group_desc(fs, fs->group_desc, 0); +#endif + + /* + * Set the state of the FS to be non-valid. (The state has + * already been backed up earlier, and will be restored after + * we write out the backup superblocks.) + */ + fs->super->s_state &= ~EXT2_VALID_FS; + fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; +#ifdef WORDS_BIGENDIAN + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); +#endif + + /* + * If this is an external journal device, don't write out the + * block group descriptors or any of the backup superblocks + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + goto write_primary_superblock_only; + + /* + * Write out the master group descriptors, and the backup + * superblocks and group descriptors. + */ + group_ptr = (char *) group_shadow; + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = fs->desc_blocks; + + ext2fs_numeric_progress_init(fs, &progress, NULL, + fs->group_desc_count); + + + for (i = 0; i < fs->group_desc_count; i++) { + blk64_t super_blk, old_desc_blk, new_desc_blk; + + ext2fs_numeric_progress_update(fs, &progress, i); + ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, + &new_desc_blk, 0); + + if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { + retval = write_backup_super(fs, i, super_blk, + super_shadow); + if (retval) + goto errout; + } + if (fs->flags & EXT2_FLAG_SUPER_ONLY) + continue; + if ((old_desc_blk) && + (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { + retval = io_channel_write_blk64(fs->io, + old_desc_blk, old_desc_blocks, group_ptr); + if (retval) + goto errout; + } + if (new_desc_blk) { + int meta_bg = i / EXT2_DESC_PER_BLOCK(fs->super); + + retval = io_channel_write_blk64(fs->io, new_desc_blk, + 1, group_ptr + (meta_bg*fs->blocksize)); + if (retval) + goto errout; + } + } + + ext2fs_numeric_progress_close(fs, &progress, NULL); + + /* + * If the write_bitmaps() function is present, call it to + * flush the bitmaps. This is done this way so that a simple + * program that doesn't mess with the bitmaps doesn't need to + * drag in the bitmaps.c code. + */ + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + goto errout; + } + +write_primary_superblock_only: + /* + * Write out master superblock. This has to be done + * separately, since it is located at a fixed location + * (SUPERBLOCK_OFFSET). We flush all other pending changes + * out to disk first, just to avoid a race condition with an + * insy-tinsy window.... + */ + + fs->super->s_block_group_nr = 0; + fs->super->s_state = fs_state; + fs->super->s_feature_incompat = feature_incompat; +#ifdef WORDS_BIGENDIAN + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); +#endif + + retval = io_channel_flush(fs->io); + retval = write_primary_superblock(fs, super_shadow); + if (retval) + goto errout; + + fs->flags &= ~EXT2_FLAG_DIRTY; + + retval = io_channel_flush(fs->io); +errout: + fs->super->s_state = fs_state; +#ifdef WORDS_BIGENDIAN + if (super_shadow) + ext2fs_free_mem(&super_shadow); + if (group_shadow) + ext2fs_free_mem(&group_shadow); +#endif + return retval; +} + +errcode_t ext2fs_close(ext2_filsys fs) +{ + errcode_t retval; + int meta_blks; + io_stats stats = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + return retval; + } + if (fs->super->s_kbytes_written && + fs->io->manager->get_stats) + fs->io->manager->get_stats(fs->io, &stats); + if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { + fs->super->s_kbytes_written += stats->bytes_written >> 10; + meta_blks = fs->desc_blocks + 1; + if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) + fs->super->s_kbytes_written += meta_blks / + (fs->blocksize / 1024); + if ((fs->flags & EXT2_FLAG_DIRTY) == 0) + fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; + } + if (fs->flags & EXT2_FLAG_DIRTY) { + retval = ext2fs_flush(fs); + if (retval) + return retval; + } + ext2fs_free(fs); + return 0; +} + diff --git a/lib/libext2fs/orig/com_err.c b/lib/libext2fs/orig/com_err.c new file mode 100644 index 0000000..c27853e --- /dev/null +++ b/lib/libext2fs/orig/com_err.c @@ -0,0 +1,35 @@ +/* + * Copyright 1987, 1988 by MIT Student Information Processing Board. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose is hereby granted, provided that + * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. M.I.T. and the + * M.I.T. S.I.P.B. make no representations about the suitability of + * this software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" + +void com_err (const char *whoami, + errcode_t code, + const char *fmt, ...) +{ + if(whoami) + ext2_log_trace("%s: ", whoami); + + ext2_log_trace("error code: %i ", (int) code); + + if(fmt) + ext2_log_trace(fmt); + + ext2_log_trace("\n"); +} diff --git a/lib/libext2fs/orig/com_err.h b/lib/libext2fs/orig/com_err.h new file mode 100644 index 0000000..0c31a4e --- /dev/null +++ b/lib/libext2fs/orig/com_err.h @@ -0,0 +1,70 @@ +/* + * Header file for common error description library. + * + * Copyright 1988, Student Information Processing Board of the + * Massachusetts Institute of Technology. + * + * For copyright and distribution info, see the documentation supplied + * with this package. + */ + +#if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__) + +#ifdef __GNUC__ +#define COM_ERR_ATTR(x) __attribute__(x) +#else +#define COM_ERR_ATTR(x) +#endif + +#ifndef DEBUG_GEKKO +#define OMIT_COM_ERR +#endif + +#include +#include + +typedef long errcode_t; + +struct error_table { + char const * const * msgs; + long base; + int n_msgs; +}; +struct et_list; + +extern void com_err (const char *, long, const char *, ...) + COM_ERR_ATTR((format(printf, 3, 4))); + +extern void com_err_va (const char *whoami, errcode_t code, const char *fmt, + va_list args) + COM_ERR_ATTR((format(printf, 3, 0))); + +extern char const *error_message (long); +extern void (*com_err_hook) (const char *, long, const char *, va_list); +extern void (*set_com_err_hook (void (*) (const char *, long, + const char *, va_list))) + (const char *, long, const char *, va_list); +extern void (*reset_com_err_hook (void)) (const char *, long, + const char *, va_list); +extern int init_error_table(const char * const *msgs, long base, int count); + +extern errcode_t add_error_table(const struct error_table * et); +extern errcode_t remove_error_table(const struct error_table * et); +extern void add_to_error_table(struct et_list *new_table); + +/* Provided for Heimdall compatibility */ +extern const char *com_right(struct et_list *list, long code); +extern const char *com_right_r(struct et_list *list, long code, char *str, size_t len); +extern void initialize_error_table_r(struct et_list **list, + const char **messages, + int num_errors, + long base); +extern void free_error_table(struct et_list *et); + +/* Provided for compatibility with other com_err libraries */ +extern int et_list_lock(void); +extern int et_list_unlock(void); + +#define __COM_ERR_H +#define __COM_ERR_H__ +#endif /* !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)*/ diff --git a/lib/libext2fs/orig/config.h b/lib/libext2fs/orig/config.h new file mode 100644 index 0000000..be09663 --- /dev/null +++ b/lib/libext2fs/orig/config.h @@ -0,0 +1,10 @@ + +#define HAVE_UNISTD_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UTIME_H 1 +#define WORDS_BIGENDIAN 1 +#define HAVE_ERRNO_H 1 +#define EXT2_FLAT_INCLUDES 1 +#define HAVE_STRDUP 1 +#define HAVE_SYS_RESOURCE_H 1 diff --git a/lib/libext2fs/orig/crc16.c b/lib/libext2fs/orig/crc16.c new file mode 100644 index 0000000..02d81e4 --- /dev/null +++ b/lib/libext2fs/orig/crc16.c @@ -0,0 +1,73 @@ +/* + * crc16.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#if HAVE_SYS_TYPES_H +#include +#endif +#include "ext2_types.h" + +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ +static __u16 const crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * Compute the CRC-16 for the data buffer + * + * @param crc previous CRC value + * @param buffer data pointer + * @param len number of bytes in the buffer + * @return the updated CRC value + */ +crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len) +{ + const unsigned char *cp = buffer; + + while (len--) + /* + * for an unknown reason, PPC treats __u16 as signed + * and keeps doing sign extension on the value. + * Instead, use only the low 16 bits of an unsigned + * int for holding the CRC value to avoid this. + */ + crc = (((crc >> 8) & 0xffU) ^ + crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; + return crc; +} diff --git a/lib/libext2fs/orig/crc16.h b/lib/libext2fs/orig/crc16.h new file mode 100644 index 0000000..322e68d --- /dev/null +++ b/lib/libext2fs/orig/crc16.h @@ -0,0 +1,26 @@ +/* + * crc16.h - CRC-16 routine + * + * Implements the standard CRC-16: + * Width 16 + * Poly 0x8005 (x16 + x15 + x2 + 1) + * Init 0 + * + * Copyright (c) 2005 Ben Gardner + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#ifndef __CRC16_H +#define __CRC16_H + +/* for an unknown reason, PPC treats __u16 as signed and keeps doing sign + * extension on the value. Instead, use only the low 16 bits of an + * unsigned int for holding the CRC value to avoid this. + */ +typedef unsigned int crc16_t; + +extern crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len); + +#endif /* __CRC16_H */ diff --git a/lib/libext2fs/orig/csum.c b/lib/libext2fs/orig/csum.c new file mode 100644 index 0000000..58e2971 --- /dev/null +++ b/lib/libext2fs/orig/csum.c @@ -0,0 +1,288 @@ +/* + * csum.c --- checksumming of ext3 structures + * + * Copyright (C) 2006 Cluster File Systems, Inc. + * Copyright (C) 2006, 2007 by Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "crc16.h" +#include + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) +{ + __u16 crc = 0; + struct ext2_group_desc *desc; + size_t size; + + size = fs->super->s_desc_size; + if (size < EXT2_MIN_DESC_SIZE) + size = EXT2_MIN_DESC_SIZE; + if (size > sizeof(struct ext4_group_desc)) { + printf("%s: illegal s_desc_size(%zd)\n", __func__, size); + size = sizeof(struct ext4_group_desc); + } + + desc = ext2fs_group_desc(fs, fs->group_desc, group); + + if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { + int offset = offsetof(struct ext2_group_desc, bg_checksum); + +#ifdef WORDS_BIGENDIAN + struct ext4_group_desc swabdesc; + + /* Have to swab back to little-endian to do the checksum */ + memcpy(&swabdesc, desc, size); + ext2fs_swap_group_desc2(fs, + (struct ext2_group_desc *) &swabdesc); + desc = (struct ext2_group_desc *) &swabdesc; + + group = ext2fs_swab32(group); +#endif + crc = ext2fs_crc16(~0, fs->super->s_uuid, + sizeof(fs->super->s_uuid)); + crc = ext2fs_crc16(crc, &group, sizeof(group)); + crc = ext2fs_crc16(crc, desc, offset); + offset += sizeof(desc->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (offset < size) { + crc = ext2fs_crc16(crc, (char *)desc + offset, + size - offset); + } + } + + return crc; +} + +int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) +{ + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && + (ext2fs_bg_checksum(fs, group) != + ext2fs_group_desc_csum(fs, group))) + return 0; + + return 1; +} + +void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) +{ + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + return; + + /* ext2fs_bg_checksum_set() sets the actual checksum field but + * does not calculate the checksum itself. */ + ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); +} + +static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, + __u32 inodes_per_grp, dgrp_t grp_no) +{ + ext2_ino_t i, start_ino, end_ino; + + start_ino = grp_no * inodes_per_grp + 1; + end_ino = start_ino + inodes_per_grp - 1; + + for (i = end_ino; i >= start_ino; i--) { + if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) + return i - start_ino + 1; + } + return inodes_per_grp; +} + +/* update the bitmap flags, set the itable high watermark, and calculate + * checksums for the group descriptors */ +errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + int dirty = 0; + dgrp_t i; + + if (!fs->inode_map) + return EXT2_ET_NO_INODE_BITMAP; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) + return 0; + + for (i = 0; i < fs->group_desc_count; i++) { + unsigned int old_csum = ext2fs_bg_checksum(fs, i); + int old_unused = ext2fs_bg_itable_unused(fs, i); + unsigned int old_flags = ext2fs_bg_flags(fs, i); + int old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); + + if (old_free_inodes_count == sb->s_inodes_per_group) { + ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); + } else { + int unused = + sb->s_inodes_per_group - + find_last_inode_ingrp(fs->inode_map, + sb->s_inodes_per_group, i); + + ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); + ext2fs_bg_itable_unused_set(fs, i, unused); + } + + ext2fs_group_desc_csum_set(fs, i); + if (old_flags != ext2fs_bg_flags(fs, i)) + dirty = 1; + if (old_unused != ext2fs_bg_itable_unused(fs, i)) + dirty = 1; + if (old_csum != ext2fs_bg_checksum(fs, i)) + dirty = 1; + } + if (dirty) + ext2fs_mark_super_dirty(fs); + return 0; +} + +#ifdef DEBUG +#include "e2p/e2p.h" + +void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) +{ + __u16 crc1, crc2, crc3; + dgrp_t swabgroup; + struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, group); + size_t size; + struct ext2_super_block *sb = fs->super; + int offset = offsetof(struct ext2_group_desc, bg_checksum); +#ifdef WORDS_BIGENDIAN + struct ext4_group_desc swabdesc; +#endif + + size = fs->super->s_desc_size; + if (size < EXT2_MIN_DESC_SIZE) + size = EXT2_MIN_DESC_SIZE; + if (size > sizeof(struct ext4_group_desc)) + size = sizeof(struct ext4_group_desc); +#ifdef WORDS_BIGENDIAN + /* Have to swab back to little-endian to do the checksum */ + memcpy(&swabdesc, desc, size); + ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); + desc = (struct ext2_group_desc *) &swabdesc; + + swabgroup = ext2fs_swab32(group); +#else + swabgroup = group; +#endif + + crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); + crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); + crc3 = ext2fs_crc16(crc2, desc, offset); + offset += sizeof(desc->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (offset < size) + crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); + + printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n", + msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, + ext2fs_group_desc_csum(fs, group)); +} + +unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, + 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; + +int main(int argc, char **argv) +{ + struct ext2_super_block param; + errcode_t retval; + ext2_filsys fs; + int i; + __u16 csum1, csum2, csum_known = 0xd3a4; + + memset(¶m, 0, sizeof(param)); + ext2fs_blocks_count_set(¶m, 32768); + + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + test_io_manager, &fs); + if (retval) { + com_err("setup", retval, + "While initializing filesystem"); + exit(1); + } + memcpy(fs->super->s_uuid, sb_uuid, 16); + fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; + + for (i=0; i < fs->group_desc_count; i++) { + ext2fs_block_bitmap_loc_set(fs, i, 124); + ext2fs_inode_bitmap_loc_set(fs, i, 125); + ext2fs_inode_table_loc_set(fs, i, 126); + ext2fs_bg_free_blocks_count_set(fs, i, 31119); + ext2fs_bg_free_inodes_count_set(fs, i, 15701); + ext2fs_bg_used_dirs_count_set(fs, i, 2); + ext2fs_bg_flags_zap(fs, i); + }; + + csum1 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum0000", fs, 0); + + if (csum1 != csum_known) { + printf("checksum for group 0 should be %04x\n", csum_known); + exit(1); + } + csum2 = ext2fs_group_desc_csum(fs, 1); + print_csum("csum0001", fs, 1); + if (csum1 == csum2) { + printf("checksums for different groups shouldn't match\n"); + exit(1); + } + csum2 = ext2fs_group_desc_csum(fs, 2); + print_csum("csumffff", fs, 2); + if (csum1 == csum2) { + printf("checksums for different groups shouldn't match\n"); + exit(1); + } + ext2fs_bg_checksum_set(fs, 0, csum1); + csum2 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum_set", fs, 0); + if (csum1 != csum2) { + printf("checksums should not depend on checksum field\n"); + exit(1); + } + if (!ext2fs_group_desc_csum_verify(fs, 0)) { + printf("checksums should verify against gd_checksum\n"); + exit(1); + } + memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); + print_csum("new_uuid", fs, 0); + if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { + printf("checksums for different filesystems shouldn't match\n"); + exit(1); + } + csum1 = ext2fs_group_desc_csum(fs, 0); + ext2fs_bg_checksum_set(fs, 0, csum1); + print_csum("csum_new", fs, 0); + ext2fs_bg_free_blocks_count_set(fs, 0, 1); + csum2 = ext2fs_group_desc_csum(fs, 0); + print_csum("csum_blk", fs, 0); + if (csum1 == csum2) { + printf("checksums for different data shouldn't match\n"); + exit(1); + } + + return 0; +} +#endif diff --git a/lib/libext2fs/orig/dblist.c b/lib/libext2fs/orig/dblist.c new file mode 100644 index 0000000..8ee61b4 --- /dev/null +++ b/lib/libext2fs/orig/dblist.c @@ -0,0 +1,414 @@ +/* + * dblist.c -- directory block list functions + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b); +static EXT2_QSORT_TYPE dir_block_cmp2(const void *a, const void *b); +static EXT2_QSORT_TYPE (*sortfunc32)(const void *a, const void *b); + +/* + * Returns the number of directories in the filesystem as reported by + * the group descriptors. Of course, the group descriptors could be + * wrong! + */ +errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) +{ + dgrp_t i; + ext2_ino_t num_dirs, max_dirs; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + num_dirs = 0; + max_dirs = fs->super->s_inodes_per_group; + for (i = 0; i < fs->group_desc_count; i++) { + if (ext2fs_bg_used_dirs_count(fs, i) > max_dirs) + num_dirs += max_dirs / 8; + else + num_dirs += ext2fs_bg_used_dirs_count(fs, i); + } + if (num_dirs > fs->super->s_inodes_count) + num_dirs = fs->super->s_inodes_count; + + *ret_num_dirs = num_dirs; + + return 0; +} + +/* + * helper function for making a new directory block list (for + * initialize and copy). + */ +static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, + ext2_ino_t count, + struct ext2_db_entry2 *list, + ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + ext2_ino_t num_dirs; + size_t len; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if ((ret_dblist == 0) && fs->dblist && + (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) + return 0; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); + if (retval) + return retval; + memset(dblist, 0, sizeof(struct ext2_struct_dblist)); + + dblist->magic = EXT2_ET_MAGIC_DBLIST; + dblist->fs = fs; + if (size) + dblist->size = size; + else { + retval = ext2fs_get_num_dirs(fs, &num_dirs); + if (retval) + goto cleanup; + dblist->size = (num_dirs * 2) + 12; + } + len = (size_t) sizeof(struct ext2_db_entry2) * dblist->size; + dblist->count = count; + retval = ext2fs_get_array(dblist->size, sizeof(struct ext2_db_entry2), + &dblist->list); + if (retval) + goto cleanup; + + if (list) + memcpy(dblist->list, list, len); + else + memset(dblist->list, 0, len); + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + return 0; +cleanup: + if (dblist) + ext2fs_free_mem(&dblist); + return retval; +} + +/* + * Initialize a directory block list + */ +errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(fs, 0, 0, 0, &dblist); + if (retval) + return retval; + + dblist->sorted = 1; + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + + return 0; +} + +/* + * Copy a directory block list + */ +errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(src->fs, src->size, src->count, src->list, + &dblist); + if (retval) + return retval; + dblist->sorted = src->sorted; + *dest = dblist; + return 0; +} + +/* + * Close a directory block list + * + * (moved to closefs.c) + */ + + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt) +{ + struct ext2_db_entry2 *new_entry; + errcode_t retval; + unsigned long old_size; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count >= dblist->size) { + old_size = dblist->size * sizeof(struct ext2_db_entry2); + dblist->size += dblist->size > 200 ? dblist->size / 2 : 100; + retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * + sizeof(struct ext2_db_entry2), + &dblist->list); + if (retval) { + dblist->size -= 100; + return retval; + } + } + new_entry = dblist->list + ( dblist->count++); + new_entry->blk = blk; + new_entry->ino = ino; + new_entry->blockcnt = blockcnt; + + dblist->sorted = 0; + + return 0; +} + +/* + * Change the directory block to the directory block list + */ +errcode_t ext2fs_set_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt) +{ + dgrp_t i; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + for (i=0; i < dblist->count; i++) { + if ((dblist->list[i].ino != ino) || + (dblist->list[i].blockcnt != blockcnt)) + continue; + dblist->list[i].blk = blk; + dblist->sorted = 0; + return 0; + } + return EXT2_ET_DB_NOT_FOUND; +} + +void ext2fs_dblist_sort2(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)) +{ + if (!sortfunc) + sortfunc = dir_block_cmp2; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry2), sortfunc); + dblist->sorted = 1; +} + +/* + * This function iterates over the directory block list + */ +errcode_t ext2fs_dblist_iterate2(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry2 *db_info, + void *priv_data), + void *priv_data) +{ + unsigned long long i; + int ret; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (!dblist->sorted) + ext2fs_dblist_sort2(dblist, 0); + for (i=0; i < dblist->count; i++) { + ret = (*func)(dblist->fs, &dblist->list[i], priv_data); + if (ret & DBLIST_ABORT) + return 0; + } + return 0; +} + +static EXT2_QSORT_TYPE dir_block_cmp2(const void *a, const void *b) +{ + const struct ext2_db_entry2 *db_a = + (const struct ext2_db_entry2 *) a; + const struct ext2_db_entry2 *db_b = + (const struct ext2_db_entry2 *) b; + + if (db_a->blk != db_b->blk) + return (int) (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (int) (db_a->ino - db_b->ino); + + return (db_a->blockcnt - db_b->blockcnt); +} + +blk64_t ext2fs_dblist_count2(ext2_dblist dblist) +{ + return dblist->count; +} + +errcode_t ext2fs_dblist_get_last2(ext2_dblist dblist, + struct ext2_db_entry2 **entry) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + if (entry) + *entry = dblist->list + ( dblist->count-1); + return 0; +} + +errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + dblist->count--; + return 0; +} + +/* + * Legacy 32-bit versions + */ + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + return ext2fs_add_dir_block2(dblist, ino, blk, blockcnt); +} + +/* + * Change the directory block to the directory block list + */ +errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + return ext2fs_set_dir_block2(dblist, ino, blk, blockcnt); +} + +void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)) +{ + if (sortfunc) { + sortfunc32 = sortfunc; + sortfunc = dir_block_cmp; + } else + sortfunc = dir_block_cmp2; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry2), sortfunc); + dblist->sorted = 1; +} + +/* + * This function iterates over the directory block list + */ +struct iterate_passthrough { + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *priv_data); + void *priv_data; +}; + +static int passthrough_func(ext2_filsys fs, + struct ext2_db_entry2 *db_info, + void *priv_data) +{ + struct iterate_passthrough *p = priv_data; + struct ext2_db_entry db; + int ret; + + db.ino = db_info->ino; + db.blk = (blk_t) db_info->blk; + db.blockcnt = (int) db_info->blockcnt; + ret = (p->func)(fs, &db, p->priv_data); + db_info->ino = db.ino; + db_info->blk = db.blk; + db_info->blockcnt = db.blockcnt; + return ret; +} + +errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data) +{ + struct iterate_passthrough pass; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + pass.func = func; + pass.priv_data = priv_data; + + return ext2fs_dblist_iterate2(dblist, passthrough_func, &pass); +} + +static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry2 *db_a = + (const struct ext2_db_entry2 *) a; + const struct ext2_db_entry2 *db_b = + (const struct ext2_db_entry2 *) b; + + struct ext2_db_entry a32, b32; + + a32.ino = db_a->ino; a32.blk = db_a->blk; + a32.blockcnt = db_a->blockcnt; + + b32.ino = db_b->ino; b32.blk = db_b->blk; + b32.blockcnt = db_b->blockcnt; + + return sortfunc32(&a32, &b32); +} + +int ext2fs_dblist_count(ext2_dblist dblist) +{ + return dblist->count; +} + +errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, + struct ext2_db_entry **entry) +{ + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + static struct ext2_db_entry ret_entry; + struct ext2_db_entry2 *last; + + if (dblist->count == 0) + return EXT2_ET_DBLIST_EMPTY; + + if (!entry) + return 0; + + last = dblist->list + dblist->count -1; + + ret_entry.ino = last->ino; + ret_entry.blk = last->blk; + ret_entry.blockcnt = last->blockcnt; + *entry = &ret_entry; + + return 0; +} + diff --git a/lib/libext2fs/orig/dblist_dir.c b/lib/libext2fs/orig/dblist_dir.c new file mode 100644 index 0000000..07ed8af --- /dev/null +++ b/lib/libext2fs/orig/dblist_dir.c @@ -0,0 +1,79 @@ +/* + * dblist_dir.c --- iterate by directory entry + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data); + +errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + errcode_t retval; + struct dir_context ctx; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + ctx.dir = 0; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + + retval = ext2fs_dblist_iterate2(dblist, db_dir_proc, &ctx); + + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data) +{ + struct dir_context *ctx; + int ret; + + ctx = (struct dir_context *) priv_data; + ctx->dir = db_info->ino; + ctx->errcode = 0; + + ret = ext2fs_process_dir_block(fs, &db_info->blk, + db_info->blockcnt, 0, 0, priv_data); + if ((ret & BLOCK_ABORT) && !ctx->errcode) + return DBLIST_ABORT; + return 0; +} diff --git a/lib/libext2fs/orig/dir_iterate.c b/lib/libext2fs/orig/dir_iterate.c new file mode 100644 index 0000000..88f6b47 --- /dev/null +++ b/lib/libext2fs/orig/dir_iterate.c @@ -0,0 +1,270 @@ +/* + * dir_iterate.c --- ext2fs directory iteration operations + * + * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +#define EXT4_MAX_REC_LEN ((1<<16)-1) + +errcode_t ext2fs_get_rec_len(ext2_filsys fs, + struct ext2_dir_entry *dirent, + unsigned int *rec_len) +{ + unsigned int len = dirent->rec_len; + + if (fs->blocksize < 65536) + *rec_len = len; + else if (len == EXT4_MAX_REC_LEN || len == 0) + *rec_len = fs->blocksize; + else + *rec_len = (len & 65532) | ((len & 3) << 16); + return 0; +} + +errcode_t ext2fs_set_rec_len(ext2_filsys fs, + unsigned int len, + struct ext2_dir_entry *dirent) +{ + if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) + return EINVAL; + if (len < 65536) { + dirent->rec_len = len; + return 0; + } + if (len == fs->blocksize) { + if (fs->blocksize == 65536) + dirent->rec_len = EXT4_MAX_REC_LEN; + else + dirent->rec_len = 0; + } else + dirent->rec_len = (len & 65532) | ((len >> 16) & 3); + return 0; +} + +/* + * This function checks to see whether or not a potential deleted + * directory entry looks valid. What we do is check the deleted entry + * and each successive entry to make sure that they all look valid and + * that the last deleted entry ends at the beginning of the next + * undeleted entry. Returns 1 if the deleted entry looks valid, zero + * if not valid. + */ +static int ext2fs_validate_entry(ext2_filsys fs, char *buf, + unsigned int offset, + unsigned int final_offset) +{ + struct ext2_dir_entry *dirent; + unsigned int rec_len; +#define DIRENT_MIN_LENGTH 12 + + while ((offset < final_offset) && + (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { + dirent = (struct ext2_dir_entry *)(buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return 0; + offset += rec_len; + if ((rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) + return 0; + } + return (offset == final_offset); +} + +errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, + ext2fs_process_dir_block, &ctx); + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +struct xlate { + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *real_private; +}; + +static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); +} + +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_dir_iterate2(fs, dir, flags, block_buf, + xlate_func, &xl); +} + + +/* + * Helper function which is private to this module. Used by + * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() + */ +int ext2fs_process_dir_block(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + unsigned int rec_len, size; + int entry; + struct ext2_dir_entry *dirent; + + if (blockcnt < 0) + return 0; + + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0); + if (ctx->errcode) + return BLOCK_ABORT; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (((offset + rec_len) > fs->blocksize) || + (rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + fs->blocksize, ctx->buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (rec_len != size) { + unsigned int final_offset; + + final_offset = offset + rec_len; + offset += size; + while (offset < final_offset && + !ext2fs_validate_entry(fs, ctx->buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf, + 0); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} + diff --git a/lib/libext2fs/orig/dirblock.c b/lib/libext2fs/orig/dirblock.c new file mode 100644 index 0000000..73e1f0a --- /dev/null +++ b/lib/libext2fs/orig/dirblock.c @@ -0,0 +1,126 @@ +/* + * dirblock.c --- directory block routines. + * + * Copyright (C) 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + char *p, *end; + struct ext2_dir_entry *dirent; + unsigned int name_len, rec_len; + + + retval = io_channel_read_blk64(fs->io, block, 1, buf); + if (retval) + return retval; + + p = (char *) buf; + end = (char *) buf + fs->blocksize; + while (p < end-8) { + dirent = (struct ext2_dir_entry *) p; +#ifdef WORDS_BIGENDIAN + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + name_len = dirent->name_len; +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || (rec_len % 4)) { + rec_len = 8; + retval = EXT2_ET_DIR_CORRUPTED; + } else if (((name_len & 0xFF) + 8) > rec_len) + retval = EXT2_ET_DIR_CORRUPTED; + p += rec_len; + } + return retval; +} + +errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_read_dir_block3(fs, block, buf, flags); +} + +errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf) +{ + return ext2fs_read_dir_block3(fs, block, buf, 0); +} + + +errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ +#ifdef WORDS_BIGENDIAN + errcode_t retval; + char *p, *end; + char *buf = 0; + unsigned int rec_len; + struct ext2_dir_entry *dirent; + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memcpy(buf, inbuf, fs->blocksize); + p = buf; + end = buf + fs->blocksize; + while (p < end) { + dirent = (struct ext2_dir_entry *) p; + if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || + (rec_len % 4)) { + ext2fs_free_mem(&buf); + return (EXT2_ET_DIR_CORRUPTED); + } + p += rec_len; + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); + + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); + } + retval = io_channel_write_blk64(fs->io, block, 1, buf); + ext2fs_free_mem(&buf); + return retval; +#else + return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf); +#endif +} + +errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ + return ext2fs_write_dir_block3(fs, block, inbuf, flags); +} + +errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *inbuf) +{ + return ext2fs_write_dir_block3(fs, block, inbuf, 0); +} + diff --git a/lib/libext2fs/orig/dirhash.c b/lib/libext2fs/orig/dirhash.c new file mode 100644 index 0000000..a069706 --- /dev/null +++ b/lib/libext2fs/orig/dirhash.c @@ -0,0 +1,257 @@ +/* + * dirhash.c -- Calculate the hash of a directory entry + * + * Copyright (c) 2001 Daniel Phillips + * + * Copyright (c) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Keyed 32-bit hash function using TEA in a Davis-Meyer function + * H0 = Key + * Hi = E Mi(Hi-1) + Hi-1 + * + * (see Applied Cryptography, 2nd edition, p448). + * + * Jeremy Fitzhardinge 1998 + * + * This code is made available under the terms of the GPL + */ +#define DELTA 0x9E3779B9 + +static void TEA_transform(__u32 buf[4], __u32 const in[]) +{ + __u32 sum = 0; + __u32 b0 = buf[0], b1 = buf[1]; + __u32 a = in[0], b = in[1], c = in[2], d = in[3]; + int n = 16; + + do { + sum += DELTA; + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); + } while(--n); + + buf[0] += b0; + buf[1] += b1; +} + +/* F, G and H are basic MD4 functions: selection, majority, parity */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The generic round function. The application is so specific that + * we don't bother protecting all the arguments with parens, as is generally + * good macro practice, in favor of extra legibility. + * Rotation is separate from addition to prevent recomputation + */ +#define ROUND(f, a, b, c, d, x, s) \ + (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) +#define K1 0 +#define K2 013240474631UL +#define K3 015666365641UL + +/* + * Basic cut-down MD4 transform. Returns only 32 bits of result. + */ +static void halfMD4Transform (__u32 buf[4], __u32 const in[]) +{ + __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ + ROUND(F, a, b, c, d, in[0] + K1, 3); + ROUND(F, d, a, b, c, in[1] + K1, 7); + ROUND(F, c, d, a, b, in[2] + K1, 11); + ROUND(F, b, c, d, a, in[3] + K1, 19); + ROUND(F, a, b, c, d, in[4] + K1, 3); + ROUND(F, d, a, b, c, in[5] + K1, 7); + ROUND(F, c, d, a, b, in[6] + K1, 11); + ROUND(F, b, c, d, a, in[7] + K1, 19); + + /* Round 2 */ + ROUND(G, a, b, c, d, in[1] + K2, 3); + ROUND(G, d, a, b, c, in[3] + K2, 5); + ROUND(G, c, d, a, b, in[5] + K2, 9); + ROUND(G, b, c, d, a, in[7] + K2, 13); + ROUND(G, a, b, c, d, in[0] + K2, 3); + ROUND(G, d, a, b, c, in[2] + K2, 5); + ROUND(G, c, d, a, b, in[4] + K2, 9); + ROUND(G, b, c, d, a, in[6] + K2, 13); + + /* Round 3 */ + ROUND(H, a, b, c, d, in[3] + K3, 3); + ROUND(H, d, a, b, c, in[7] + K3, 9); + ROUND(H, c, d, a, b, in[2] + K3, 11); + ROUND(H, b, c, d, a, in[6] + K3, 15); + ROUND(H, a, b, c, d, in[1] + K3, 3); + ROUND(H, d, a, b, c, in[5] + K3, 9); + ROUND(H, c, d, a, b, in[0] + K3, 11); + ROUND(H, b, c, d, a, in[4] + K3, 15); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#undef ROUND +#undef F +#undef G +#undef H +#undef K1 +#undef K2 +#undef K3 + +/* The old legacy hash */ +static ext2_dirhash_t dx_hack_hash (const char *name, int len, + int unsigned_flag) +{ + __u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; + const unsigned char *ucp = (const unsigned char *) name; + const signed char *scp = (const signed char *) name; + int c; + + while (len--) { + if (unsigned_flag) + c = (int) *ucp++; + else + c = (int) *scp++; + hash = hash1 + (hash0 ^ (c * 7152373)); + + if (hash & 0x80000000) hash -= 0x7fffffff; + hash1 = hash0; + hash0 = hash; + } + return (hash0 << 1); +} + +static void str2hashbuf(const char *msg, int len, __u32 *buf, int num, + int unsigned_flag) +{ + __u32 pad, val; + int i, c; + const unsigned char *ucp = (const unsigned char *) msg; + const signed char *scp = (const signed char *) msg; + + pad = (__u32)len | ((__u32)len << 8); + pad |= pad << 16; + + val = pad; + if (len > num*4) + len = num * 4; + for (i=0; i < len; i++) { + if ((i % 4) == 0) + val = pad; + if (unsigned_flag) + c = (int) ucp[i]; + else + c = (int) scp[i]; + + val = c + (val << 8); + if ((i % 4) == 3) { + *buf++ = val; + val = pad; + num--; + } + } + if (--num >= 0) + *buf++ = val; + while (--num >= 0) + *buf++ = pad; +} + +/* + * Returns the hash of a filename. If len is 0 and name is NULL, then + * this function can be used to test whether or not a hash version is + * supported. + * + * The seed is an 4 longword (32 bits) "secret" which can be used to + * uniquify a hash. If the seed is all zero's, then some default seed + * may be used. + * + * A particular hash version specifies whether or not the seed is + * represented, and whether or not the returned hash is 32 bits or 64 + * bits. 32 bit hashes will return 0 for the minor hash. + */ +errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash) +{ + __u32 hash; + __u32 minor_hash = 0; + const char *p; + int i; + __u32 in[8], buf[4]; + int unsigned_flag = 0; + + /* Initialize the default seed for the hash checksum functions */ + buf[0] = 0x67452301; + buf[1] = 0xefcdab89; + buf[2] = 0x98badcfe; + buf[3] = 0x10325476; + + /* Check to see if the seed is all zero's */ + if (seed) { + for (i=0; i < 4; i++) { + if (seed[i]) + break; + } + if (i < 4) + memcpy(buf, seed, sizeof(buf)); + } + + switch (version) { + case EXT2_HASH_LEGACY_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_LEGACY: + hash = dx_hack_hash(name, len, unsigned_flag); + break; + case EXT2_HASH_HALF_MD4_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_HALF_MD4: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 8, unsigned_flag); + halfMD4Transform(buf, in); + len -= 32; + p += 32; + } + minor_hash = buf[2]; + hash = buf[1]; + break; + case EXT2_HASH_TEA_UNSIGNED: + unsigned_flag++; + case EXT2_HASH_TEA: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 4, unsigned_flag); + TEA_transform(buf, in); + len -= 16; + p += 16; + } + hash = buf[0]; + minor_hash = buf[1]; + break; + default: + *ret_hash = 0; + return EXT2_ET_DIRHASH_UNSUPP; + } + *ret_hash = hash & ~1; + if (ret_minor_hash) + *ret_minor_hash = minor_hash; + return 0; +} diff --git a/lib/libext2fs/orig/disc_cache.c b/lib/libext2fs/orig/disc_cache.c new file mode 100644 index 0000000..2a9ad05 --- /dev/null +++ b/lib/libext2fs/orig/disc_cache.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "disc_cache.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +CACHE* cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + CACHE* cache; + unsigned int i; + CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (CACHE*) mem_alloc (sizeof(CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (CACHE_ENTRY*) mem_alloc ( sizeof(CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + mem_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) mem_align (32, sectorsPerPage * cache->sectorSize); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void cache_destructor (CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + mem_free (cache->cacheEntries[i].cache); + } + mem_free (cache->cacheEntries); + mem_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static CACHE_ENTRY* cache_getPage(CACHE *cache,sec_t sector) +{ + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static CACHE_ENTRY* cache_findPage(CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool cache_flush (CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void cache_invalidate (CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/lib/libext2fs/orig/disc_cache.h b/lib/libext2fs/orig/disc_cache.h new file mode 100644 index 0000000..1d34c8c --- /dev/null +++ b/lib/libext2fs/orig/disc_cache.h @@ -0,0 +1,118 @@ +/* + CACHE.h + The CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + 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. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#ifndef _DISC_CACHE_H +#define _DISC_CACHE_H + +#include +#include +#include +#include +#include + +typedef struct +{ + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} CACHE_ENTRY; + +typedef struct +{ + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the CACHE +If the sector is not in the CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +/* +Write data to a sector in the CACHE +If the sector is not in the CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ + +/* +Write data to a sector in the CACHE, zeroing the sector first +If the sector is not in the CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ + +/* +Read several sectors from the CACHE +*/ +bool cache_readSectors (CACHE* DISC_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the CACHE +*/ +/* +Write a full sector to the CACHE +*/ +bool cache_writeSectors (CACHE* DISC_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the CACHE +*/ +bool cache_flush (CACHE* DISC_CACHE); + +/* +Clear out the contents of the CACHE without writing any dirty sectors first +*/ +void cache_invalidate (CACHE* DISC_CACHE); + +CACHE* cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void cache_destructor (CACHE* DISC_CACHE); + +#endif // _CACHE_H + diff --git a/lib/libext2fs/orig/dupfs.c b/lib/libext2fs/orig/dupfs.c new file mode 100644 index 0000000..a9e2a97 --- /dev/null +++ b/lib/libext2fs/orig/dupfs.c @@ -0,0 +1,96 @@ +/* + * dupfs.c --- duplicate a ext2 filesystem handle + * + * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) +{ + ext2_filsys fs; + errcode_t retval; + + EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + *fs = *src; + fs->device_name = 0; + fs->super = 0; + fs->orig_super = 0; + fs->group_desc = 0; + fs->inode_map = 0; + fs->block_map = 0; + fs->badblocks = 0; + fs->dblist = 0; + + io_channel_bumpcount(fs->io); + if (fs->icache) + fs->icache->refcount++; + + retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); + if (retval) + goto errout; + strcpy(fs->device_name, src->device_name); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); + if (retval) + goto errout; + memcpy(fs->super, src->super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto errout; + memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, + &fs->group_desc); + if (retval) + goto errout; + memcpy(fs->group_desc, src->group_desc, + (size_t) fs->desc_blocks * fs->blocksize); + + if (src->inode_map) { + retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); + if (retval) + goto errout; + } + if (src->block_map) { + retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); + if (retval) + goto errout; + } + if (src->badblocks) { + retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); + if (retval) + goto errout; + } + if (src->dblist) { + retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); + if (retval) + goto errout; + } + *dest = fs; + return 0; +errout: + ext2fs_free(fs); + return retval; + +} + diff --git a/lib/libext2fs/orig/e2image.h b/lib/libext2fs/orig/e2image.h new file mode 100644 index 0000000..4de2c8d --- /dev/null +++ b/lib/libext2fs/orig/e2image.h @@ -0,0 +1,51 @@ +/* + * e2image.h --- header file describing the ext2 image format + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +struct ext2_image_hdr { + __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ + char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ + char fs_hostname[64];/* Hostname of machine of image */ + char fs_netaddr[32]; /* Network address */ + __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ + __u32 fs_device; /* Device number of image */ + char fs_device_name[64]; /* Device name */ + char fs_uuid[16]; /* UUID of filesystem */ + __u32 fs_blocksize; /* Block size of the filesystem */ + __u32 fs_reserved[8]; + + __u32 image_device; /* Device number of image file */ + __u32 image_inode; /* Inode number of image file */ + __u32 image_time; /* Time of image creation */ + __u32 image_reserved[8]; + + __u32 offset_super; /* Byte offset of the sb and descriptors */ + __u32 offset_inode; /* Byte offset of the inode table */ + __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ + __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ + __u32 offset_reserved[8]; +}; + + + + + + + + + + + + + diff --git a/lib/libext2fs/orig/e2p/e2p.h b/lib/libext2fs/orig/e2p/e2p.h new file mode 100644 index 0000000..319c9bc --- /dev/null +++ b/lib/libext2fs/orig/e2p/e2p.h @@ -0,0 +1,74 @@ +/* + * e2p.h --- header file for the e2p library + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include /* Needed by dirent.h on netbsd */ +#include +#include + +#include "../ext2_fs.h" + +#define E2P_FEATURE_COMPAT 0 +#define E2P_FEATURE_INCOMPAT 1 +#define E2P_FEATURE_RO_INCOMPAT 2 +#define E2P_FEATURE_TYPE_MASK 0x03 + +#define E2P_FEATURE_NEGATE_FLAG 0x80 + +#define E2P_FS_FEATURE 0 +#define E2P_JOURNAL_FEATURE 1 + +/* `options' for print_flags() */ + +#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ + + +int fgetflags (const char * name, unsigned long * flags); +int fgetversion (const char * name, unsigned long * version); +int fsetflags (const char * name, unsigned long flags); +int fsetversion (const char * name, unsigned long version); +int getflags (int fd, unsigned long * flags); +int getversion (int fd, unsigned long * version); +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private); +void list_super(struct ext2_super_block * s); +void list_super2(struct ext2_super_block * s, FILE *f); +void print_fs_errors (FILE * f, unsigned short errors); +void print_flags (FILE * f, unsigned long flags, unsigned options); +void print_fs_state (FILE * f, unsigned short state); +int setflags (int fd, unsigned long flags); +int setversion (int fd, unsigned long version); + +const char *e2p_feature2string(int compat, unsigned int mask); +const char *e2p_jrnl_feature2string(int compat, unsigned int mask); +int e2p_string2feature(char *string, int *compat, unsigned int *mask); +int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask); +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array); +int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, + __u32 *clear_ok_array, int *type_err, + unsigned int *mask_err); + +int e2p_is_null_uuid(void *uu); +void e2p_uuid_to_str(void *uu, char *out); +const char *e2p_uuid2str(void *uu); + +const char *e2p_hash2string(int num); +int e2p_string2hash(char *string); + +const char *e2p_mntopt2string(unsigned int mask); +int e2p_string2mntopt(char *string, unsigned int *mask); +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok); + +unsigned long parse_num_blocks(const char *arg, int log_block_size); +unsigned long long parse_num_blocks2(const char *arg, int log_block_size); + +char *e2p_os2string(int os_type); +int e2p_string2os(char *str); + +unsigned int e2p_percent(int percent, unsigned int base); diff --git a/lib/libext2fs/orig/e2p/feature.c b/lib/libext2fs/orig/e2p/feature.c new file mode 100644 index 0000000..4806be5 --- /dev/null +++ b/lib/libext2fs/orig/e2p/feature.c @@ -0,0 +1,385 @@ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" +#include +#include + +struct feature { + int compat; + unsigned int mask; + const char *string; +}; + +static struct feature feature_list[] = { + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC, + "dir_prealloc" }, + { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL, + "has_journal" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, + "imagic_inodes" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, + "ext_attr" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, + "dir_index" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE, + "resize_inode" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG, + "lazy_bg" }, + + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, + "sparse_super" }, + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE, + "large_file" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HUGE_FILE, + "huge_file" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, + "uninit_bg" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, + "uninit_groups" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_DIR_NLINK, + "dir_nlink" }, + { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE, + "extra_isize" }, + + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, + "compression" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE, + "filetype" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER, + "needs_recovery" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV, + "journal_dev" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, + "extent" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, + "extents" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG, + "meta_bg" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_64BIT, + "64bit" }, + { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG, + "flex_bg"}, + { 0, 0, 0 }, +}; + +static struct feature jrnl_feature_list[] = { + { E2P_FEATURE_COMPAT, JFS_FEATURE_COMPAT_CHECKSUM, + "journal_checksum" }, + + { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE, + "journal_incompat_revoke" }, + { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT, + "journal_async_commit" }, + { 0, 0, 0 }, +}; + +const char *e2p_feature2string(int compat, unsigned int mask) +{ + struct feature *f; + static char buf[20]; + char fchar; + int fnum; + + for (f = feature_list; f->string; f++) { + if ((compat == f->compat) && + (mask == f->mask)) + return f->string; + } + switch (compat) { + case E2P_FEATURE_COMPAT: + fchar = 'C'; + break; + case E2P_FEATURE_INCOMPAT: + fchar = 'I'; + break; + case E2P_FEATURE_RO_INCOMPAT: + fchar = 'R'; + break; + default: + fchar = '?'; + break; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "FEATURE_%c%d", fchar, fnum); + return buf; +} + +int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) +{ + struct feature *f; + char *eptr; + int num; + + for (f = feature_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *compat_type = f->compat; + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "FEATURE_", 8)) + return 1; + + switch (string[8]) { + case 'c': + case 'C': + *compat_type = E2P_FEATURE_COMPAT; + break; + case 'i': + case 'I': + *compat_type = E2P_FEATURE_INCOMPAT; + break; + case 'r': + case 'R': + *compat_type = E2P_FEATURE_RO_INCOMPAT; + break; + default: + return 1; + } + if (string[9] == 0) + return 1; + num = strtol(string+9, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +const char *e2p_jrnl_feature2string(int compat, unsigned int mask) +{ + struct feature *f; + static char buf[20]; + char fchar; + int fnum; + + for (f = jrnl_feature_list; f->string; f++) { + if ((compat == f->compat) && + (mask == f->mask)) + return f->string; + } + switch (compat) { + case E2P_FEATURE_COMPAT: + fchar = 'C'; + break; + case E2P_FEATURE_INCOMPAT: + fchar = 'I'; + break; + case E2P_FEATURE_RO_INCOMPAT: + fchar = 'R'; + break; + default: + fchar = '?'; + break; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "FEATURE_%c%d", fchar, fnum); + return buf; +} + +int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask) +{ + struct feature *f; + char *eptr; + int num; + + for (f = jrnl_feature_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *compat_type = f->compat; + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "FEATURE_", 8)) + return 1; + + switch (string[8]) { + case 'c': + case 'C': + *compat_type = E2P_FEATURE_COMPAT; + break; + case 'i': + case 'I': + *compat_type = E2P_FEATURE_INCOMPAT; + break; + case 'r': + case 'R': + *compat_type = E2P_FEATURE_RO_INCOMPAT; + break; + default: + return 1; + } + if (string[9] == 0) + return 1; + num = strtol(string+9, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} +static char *skip_over_blanks(char *cp) +{ + while (*cp && isspace((int)*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace((int)*cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a feature set array as requested by the user. The ok_array, + * if set, allows the application to limit what features the user is + * allowed to set or clear using this function. If clear_ok_array is set, + * then use it tell whether or not it is OK to clear a filesystem feature. + */ +int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, + __u32 *clear_ok_array, int *type_err, + unsigned int *mask_err) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + int compat_type; + int rc = 0; + + if (!clear_ok_array) + clear_ok_array = ok_array; + + if (type_err) + *type_err = 0; + if (mask_err) + *mask_err = 0; + + buf = malloc(strlen(str)+1); + if (!buf) + return 1; + strcpy(buf, str); + for (cp = buf; cp && *cp; cp = next ? next+1 : 0) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + + if (*next == 0) + next = 0; + else + *next = 0; + + if ((strcasecmp(cp, "none") == 0) || + (strcasecmp(cp, "clear") == 0)) { + compat_array[0] = 0; + compat_array[1] = 0; + compat_array[2] = 0; + continue; + } + + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2feature(cp, &compat_type, &mask)) { + rc = 1; + break; + } + if (neg) { + if (clear_ok_array && + !(clear_ok_array[compat_type] & mask)) { + rc = 1; + if (type_err) + *type_err = (compat_type | + E2P_FEATURE_NEGATE_FLAG); + if (mask_err) + *mask_err = mask; + break; + } + compat_array[compat_type] &= ~mask; + } else { + if (ok_array && !(ok_array[compat_type] & mask)) { + rc = 1; + if (type_err) + *type_err = compat_type; + if (mask_err) + *mask_err = mask; + break; + } + compat_array[compat_type] |= mask; + } + } + free(buf); + return rc; +} + +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) +{ + return e2p_edit_feature2(str, compat_array, ok_array, 0, 0, 0); +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + int compat, compat2, i; + unsigned int mask, mask2; + const char *str; + struct feature *f; + + for (i = 0; i < 2; i++) { + if (i == 0) { + f = feature_list; + printf("Feature list:\n"); + } else { + printf("\nJournal feature list:\n"); + f = jrnl_feature_list; + } + for (; f->string; f++) { + if (i == 0) { + e2p_string2feature((char *)f->string, &compat, + &mask); + str = e2p_feature2string(compat, mask); + } else { + e2p_jrnl_string2feature((char *)f->string, + &compat, &mask); + str = e2p_jrnl_feature2string(compat, mask); + } + + printf("\tCompat = %d, Mask = %u, %s\n", + compat, mask, f->string); + if (strcmp(f->string, str)) { + if (e2p_string2feature((char *) str, &compat2, + &mask2) || + (compat2 != compat) || + (mask2 != mask)) { + fprintf(stderr, "Failure!\n"); + exit(1); + } + } + } + } + exit(0); +} +#endif diff --git a/lib/libext2fs/orig/e2p/fgetflags.c b/lib/libext2fs/orig/e2p/fgetflags.c new file mode 100644 index 0000000..282be32 --- /dev/null +++ b/lib/libext2fs/orig/e2p/fgetflags.c @@ -0,0 +1,95 @@ +/* + * fgetflags.c - Get a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fgetflags (const char * name, unsigned long * flags) +{ +#if HAVE_STAT_FLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) + struct stat buf; + if (stat (name, &buf) == -1) + return -1; + + *flags = 0; +#ifdef UF_IMMUTABLE + if (buf.st_flags & UF_IMMUTABLE) + *flags |= EXT2_IMMUTABLE_FL; +#endif +#ifdef UF_APPEND + if (buf.st_flags & UF_APPEND) + *flags |= EXT2_APPEND_FL; +#endif +#ifdef UF_NODUMP + if (buf.st_flags & UF_NODUMP) + *flags |= EXT2_NODUMP_FL; +#endif + + return 0; +#else +#if HAVE_EXT2_IOCTLS + int fd, r, f, save_errno = 0; + + if (!lstat(name, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + goto notsupp; + } +#if !APPLE_DARWIN + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + if (r == -1) + save_errno = errno; + *flags = f; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + f = -1; + save_errno = syscall(SYS_fsctl, name, EXT2_IOC_GETFLAGS, &f, 0); + *flags = f; + return (save_errno); +#endif +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/orig/e2p/fgetversion.c b/lib/libext2fs/orig/e2p/fgetversion.c new file mode 100644 index 0000000..97519d7 --- /dev/null +++ b/lib/libext2fs/orig/e2p/fgetversion.c @@ -0,0 +1,69 @@ +/* + * fgetversion.c - Get a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fgetversion (const char * name, unsigned long * version) +{ +#if HAVE_EXT2_IOCTLS +#if !APPLE_DARWIN + int fd, r, ver, save_errno = 0; + + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); + if (r == -1) + save_errno = errno; + *version = ver; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + int ver=-1, err; + err = syscall(SYS_fsctl, name, EXT2_IOC_GETVERSION, &ver, 0); + *version = ver; + return(err); +#endif +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/orig/e2p/fsetflags.c b/lib/libext2fs/orig/e2p/fsetflags.c new file mode 100644 index 0000000..0f39ee9 --- /dev/null +++ b/lib/libext2fs/orig/e2p/fsetflags.c @@ -0,0 +1,100 @@ +/* + * fsetflags.c - Set a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +/* + * Deal with lame glibc's that define this function without actually + * implementing it. Can you say "attractive nuisance", boys and girls? + * I knew you could! + */ +#ifdef __linux__ +#undef HAVE_CHFLAGS +#endif + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fsetflags (const char * name, unsigned long flags) +{ +#if HAVE_CHFLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS) + struct stat buf; + unsigned long bsd_flags = 0; + +#ifdef UF_IMMUTABLE + if (flags & EXT2_IMMUTABLE_FL) + bsd_flags |= UF_IMMUTABLE; +#endif +#ifdef UF_APPEND + if (flags & EXT2_APPEND_FL) + bsd_flags |= UF_APPEND; +#endif +#ifdef UF_NODUMP + if (flags & EXT2_NODUMP_FL) + bsd_flags |= UF_NODUMP; +#endif + + return chflags (name, bsd_flags); +#else +#if HAVE_EXT2_IOCTLS + int fd, r, f, save_errno = 0; + + if (!lstat(name, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + goto notsupp; + } +#if !APPLE_DARWIN + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + f = (int) flags; + r = ioctl (fd, EXT2_IOC_SETFLAGS, &f); + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; +#else + f = (int) flags; + return syscall(SYS_fsctl, name, EXT2_IOC_SETFLAGS, &f, 0); +#endif + return r; +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/orig/e2p/fsetversion.c b/lib/libext2fs/orig/e2p/fsetversion.c new file mode 100644 index 0000000..ee26f91 --- /dev/null +++ b/lib/libext2fs/orig/e2p/fsetversion.c @@ -0,0 +1,67 @@ +/* + * fsetversion.c - Set a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fsetversion (const char * name, unsigned long version) +{ +#if HAVE_EXT2_IOCTLS +#if !APPLE_DARWIN + int fd, r, ver, save_errno = 0; + + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + ver = (int) version; + r = ioctl (fd, EXT2_IOC_SETVERSION, &ver); + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else + int ver = (int)version; + return syscall(SYS_fsctl, name, EXT2_IOC_SETVERSION, &ver, 0); +#endif +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/orig/e2p/getflags.c b/lib/libext2fs/orig/e2p/getflags.c new file mode 100644 index 0000000..0cc768a --- /dev/null +++ b/lib/libext2fs/orig/e2p/getflags.c @@ -0,0 +1,66 @@ +/* + * getflags.c - Get a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int getflags (int fd, unsigned long * flags) +{ +#if HAVE_STAT_FLAGS + struct stat buf; + if (fstat (fd, &buf) == -1) + return -1; + + *flags = 0; +#ifdef UF_IMMUTABLE + if (buf.st_flags & UF_IMMUTABLE) + *flags |= EXT2_IMMUTABLE_FL; +#endif +#ifdef UF_APPEND + if (buf.st_flags & UF_APPEND) + *flags |= EXT2_APPEND_FL; +#endif +#ifdef UF_NODUMP + if (buf.st_flags & UF_NODUMP) + *flags |= EXT2_NODUMP_FL; +#endif + + return 0; +#else +#if HAVE_EXT2_IOCTLS + int r, f; + + if (!fstat(fd, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) + goto notsupp; + r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + *flags = f; + return r; +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/orig/e2p/getversion.c b/lib/libext2fs/orig/e2p/getversion.c new file mode 100644 index 0000000..06adf6d --- /dev/null +++ b/lib/libext2fs/orig/e2p/getversion.c @@ -0,0 +1,41 @@ +/* + * getversion.c - Get a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int getversion (int fd, unsigned long * version) +{ +#if HAVE_EXT2_IOCTLS + int r, ver; + + r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); + *version = ver; + return 0; +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/orig/e2p/hashstr.c b/lib/libext2fs/orig/e2p/hashstr.c new file mode 100644 index 0000000..5ee6237 --- /dev/null +++ b/lib/libext2fs/orig/e2p/hashstr.c @@ -0,0 +1,71 @@ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct hash { + int num; + const char *string; +}; + +static struct hash hash_list[] = { + { EXT2_HASH_LEGACY, "legacy" }, + { EXT2_HASH_HALF_MD4, "half_md4" }, + { EXT2_HASH_TEA, "tea" }, + { 0, 0 }, +}; + +const char *e2p_hash2string(int num) +{ + struct hash *p; + static char buf[20]; + + for (p = hash_list; p->string; p++) { + if (num == p->num) + return p->string; + } + sprintf(buf, "HASHALG_%d", num); + return buf; +} + +/* + * Returns the hash algorithm, or -1 on error + */ +int e2p_string2hash(char *string) +{ + struct hash *p; + char *eptr; + int num; + + for (p = hash_list; p->string; p++) { + if (!strcasecmp(string, p->string)) { + return p->num; + } + } + if (strncasecmp(string, "HASHALG_", 8)) + return -1; + + if (string[8] == 0) + return -1; + num = strtol(string+8, &eptr, 10); + if (num > 255 || num < 0) + return -1; + if (*eptr) + return -1; + return num; +} + diff --git a/lib/libext2fs/orig/e2p/iod.c b/lib/libext2fs/orig/e2p/iod.c new file mode 100644 index 0000000..c53aafe --- /dev/null +++ b/lib/libext2fs/orig/e2p/iod.c @@ -0,0 +1,75 @@ +/* + * iod.c - Iterate a function on each entry of a directory + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include "e2p.h" +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private) +{ + DIR * dir; + struct dirent *de, *dep; + int max_len = -1, len, ret = 0; + +#if HAVE_PATHCONF && defined(_PC_NAME_MAX) + max_len = pathconf(dir_name, _PC_NAME_MAX); +#endif + if (max_len == -1) { +#ifdef _POSIX_NAME_MAX + max_len = _POSIX_NAME_MAX; +#else +#ifdef NAME_MAX + max_len = NAME_MAX; +#else + max_len = 256; +#endif /* NAME_MAX */ +#endif /* _POSIX_NAME_MAX */ + } + max_len += sizeof(struct dirent); + + de = malloc(max_len+1); + if (!de) + return -1; + memset(de, 0, max_len+1); + + dir = opendir (dir_name); + if (dir == NULL) { + free(de); + return -1; + } + while ((dep = readdir (dir))) { +#ifdef HAVE_RECLEN_DIRENT + len = dep->d_reclen; + if (len > max_len) + len = max_len; +#else + len = sizeof(struct dirent); +#endif + memcpy(de, dep, len); + if ((*func)(dir_name, de, private)) + ret++; + } + free(de); + closedir(dir); + return ret; +} diff --git a/lib/libext2fs/orig/e2p/ls.c b/lib/libext2fs/orig/e2p/ls.c new file mode 100644 index 0000000..20908a6 --- /dev/null +++ b/lib/libext2fs/orig/e2p/ls.c @@ -0,0 +1,403 @@ +/* + * ls.c - List the contents of an ext2fs superblock + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Copyright (C) 1995, 1996, 1997 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "e2p.h" + +static void print_user (unsigned short uid, FILE *f) +{ + struct passwd *pw; + + fprintf(f, "%u ", uid); + pw = getpwuid (uid); + if (pw == NULL) + fprintf(f, "(user unknown)\n"); + else + fprintf(f, "(user %s)\n", pw->pw_name); +} + +static void print_group (unsigned short gid, FILE *f) +{ + struct group *gr; + + fprintf(f, "%u ", gid); + gr = getgrgid (gid); + if (gr == NULL) + fprintf(f, "(group unknown)\n"); + else + fprintf(f, "(group %s)\n", gr->gr_name); +} + +#define MONTH_INT (86400 * 30) +#define WEEK_INT (86400 * 7) +#define DAY_INT (86400) +#define HOUR_INT (60 * 60) +#define MINUTE_INT (60) + +static const char *interval_string(unsigned int secs) +{ + static char buf[256], tmp[80]; + int hr, min, num; + + buf[0] = 0; + + if (secs == 0) + return ""; + + if (secs >= MONTH_INT) { + num = secs / MONTH_INT; + secs -= num*MONTH_INT; + sprintf(buf, "%d month%s", num, (num>1) ? "s" : ""); + } + if (secs >= WEEK_INT) { + num = secs / WEEK_INT; + secs -= num*WEEK_INT; + sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs >= DAY_INT) { + num = secs / DAY_INT; + secs -= num*DAY_INT; + sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs > 0) { + hr = secs / HOUR_INT; + secs -= hr*HOUR_INT; + min = secs / MINUTE_INT; + secs -= min*MINUTE_INT; + sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "", + hr, min, secs); + strcat(buf, tmp); + } + return buf; +} + +static void print_features(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, j, printed=0; + __u32 *mask = &s->s_feature_compat, m; + + fprintf(f, "Filesystem features: "); + for (i=0; i <3; i++,mask++) { + for (j=0,m=1; j < 32; j++, m<<=1) { + if (*mask & m) { + fprintf(f, " %s", e2p_feature2string(i, m)); + printed++; + } + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + +static void print_mntopts(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, printed=0; + __u32 mask = s->s_default_mount_opts, m; + + fprintf(f, "Default mount options: "); + if (mask & EXT3_DEFM_JMODE) { + fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE)); + printed++; + } + for (i=0,m=1; i < 32; i++, m<<=1) { + if (m & EXT3_DEFM_JMODE) + continue; + if (mask & m) { + fprintf(f, " %s", e2p_mntopt2string(m)); + printed++; + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + +static void print_super_flags(struct ext2_super_block * s, FILE *f) +{ + int flags_found = 0; + + if (s->s_flags == 0) + return; + + fputs("Filesystem flags: ", f); + if (s->s_flags & EXT2_FLAGS_SIGNED_HASH) { + fputs("signed_directory_hash ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_UNSIGNED_HASH) { + fputs("unsigned_directory_hash ", f); + flags_found++; + } + if (s->s_flags & EXT2_FLAGS_TEST_FILESYS) { + fputs("test_filesystem ", f); + flags_found++; + } + if (flags_found) + fputs("\n", f); + else + fputs("(none)\n", f); +} + +static __u64 e2p_blocks_count(struct ext2_super_block *super) +{ + return super->s_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_blocks_count_hi << 32 : 0); +} + +static __u64 e2p_r_blocks_count(struct ext2_super_block *super) +{ + return super->s_r_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_r_blocks_count_hi << 32 : 0); +} + +static __u64 e2p_free_blocks_count(struct ext2_super_block *super) +{ + return super->s_free_blocks_count | + (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? + (__u64) super->s_free_blocks_hi << 32 : 0); +} + +#ifndef EXT2_INODE_SIZE +#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode) +#endif + +#ifndef EXT2_GOOD_OLD_REV +#define EXT2_GOOD_OLD_REV 0 +#endif + +void list_super2(struct ext2_super_block * sb, FILE *f) +{ + int inode_blocks_per_group; + char buf[80], *str; + time_t tm; + + inode_blocks_per_group = (((sb->s_inodes_per_group * + EXT2_INODE_SIZE(sb)) + + EXT2_BLOCK_SIZE(sb) - 1) / + EXT2_BLOCK_SIZE(sb)); + if (sb->s_volume_name[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); + } else + strcpy(buf, ""); + fprintf(f, "Filesystem volume name: %s\n", buf); + if (sb->s_last_mounted[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); + } else + strcpy(buf, ""); + fprintf(f, "Last mounted on: %s\n", buf); + fprintf(f, "Filesystem UUID: %s\n", e2p_uuid2str(sb->s_uuid)); + fprintf(f, "Filesystem magic number: 0x%04X\n", sb->s_magic); + fprintf(f, "Filesystem revision #: %d", sb->s_rev_level); + if (sb->s_rev_level == EXT2_GOOD_OLD_REV) { + fprintf(f, " (original)\n"); +#ifdef EXT2_DYNAMIC_REV + } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) { + fprintf(f, " (dynamic)\n"); +#endif + } else + fprintf(f, " (unknown)\n"); + print_features(sb, f); + print_super_flags(sb, f); + print_mntopts(sb, f); + if (sb->s_mount_opts[0]) + fprintf(f, "Mount options: %s\n", sb->s_mount_opts); + fprintf(f, "Filesystem state: "); + print_fs_state (f, sb->s_state); + fprintf(f, "\n"); + fprintf(f, "Errors behavior: "); + print_fs_errors(f, sb->s_errors); + fprintf(f, "\n"); + str = e2p_os2string(sb->s_creator_os); + fprintf(f, "Filesystem OS type: %s\n", str); + free(str); + fprintf(f, "Inode count: %u\n", sb->s_inodes_count); + fprintf(f, "Block count: %llu\n", e2p_blocks_count(sb)); + fprintf(f, "Reserved block count: %llu\n", e2p_r_blocks_count(sb)); + fprintf(f, "Free blocks: %llu\n", e2p_free_blocks_count(sb)); + fprintf(f, "Free inodes: %u\n", sb->s_free_inodes_count); + fprintf(f, "First block: %u\n", sb->s_first_data_block); + fprintf(f, "Block size: %u\n", EXT2_BLOCK_SIZE(sb)); + fprintf(f, "Fragment size: %u\n", EXT2_FRAG_SIZE(sb)); + if (sb->s_reserved_gdt_blocks) + fprintf(f, "Reserved GDT blocks: %u\n", + sb->s_reserved_gdt_blocks); + fprintf(f, "Blocks per group: %u\n", sb->s_blocks_per_group); + fprintf(f, "Fragments per group: %u\n", sb->s_frags_per_group); + fprintf(f, "Inodes per group: %u\n", sb->s_inodes_per_group); + fprintf(f, "Inode blocks per group: %u\n", inode_blocks_per_group); + if (sb->s_raid_stride) + fprintf(f, "RAID stride: %u\n", + sb->s_raid_stride); + if (sb->s_raid_stripe_width) + fprintf(f, "RAID stripe width: %u\n", + sb->s_raid_stripe_width); + if (sb->s_first_meta_bg) + fprintf(f, "First meta block group: %u\n", + sb->s_first_meta_bg); + if (sb->s_log_groups_per_flex) + fprintf(f, "Flex block group size: %u\n", + 1 << sb->s_log_groups_per_flex); + if (sb->s_mkfs_time) { + tm = sb->s_mkfs_time; + fprintf(f, "Filesystem created: %s", ctime(&tm)); + } + tm = sb->s_mtime; + fprintf(f, "Last mount time: %s", + sb->s_mtime ? ctime(&tm) : "n/a\n"); + tm = sb->s_wtime; + fprintf(f, "Last write time: %s", ctime(&tm)); + fprintf(f, "Mount count: %u\n", sb->s_mnt_count); + fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count); + tm = sb->s_lastcheck; + fprintf(f, "Last checked: %s", ctime(&tm)); + fprintf(f, "Check interval: %u (%s)\n", sb->s_checkinterval, + interval_string(sb->s_checkinterval)); + if (sb->s_checkinterval) + { + time_t next; + + next = sb->s_lastcheck + sb->s_checkinterval; + fprintf(f, "Next check after: %s", ctime(&next)); + } +#define POW2(x) ((__u64) 1 << (x)) + if (sb->s_kbytes_written) { + fprintf(f, "Lifetime writes: "); + if (sb->s_kbytes_written < POW2(13)) + fprintf(f, "%llu kB\n", sb->s_kbytes_written); + else if (sb->s_kbytes_written < POW2(23)) + fprintf(f, "%llu MB\n", + (sb->s_kbytes_written + POW2(9)) >> 10); + else if (sb->s_kbytes_written < POW2(33)) + fprintf(f, "%llu GB\n", + (sb->s_kbytes_written + POW2(19)) >> 20); + else if (sb->s_kbytes_written < POW2(43)) + fprintf(f, "%llu TB\n", + (sb->s_kbytes_written + POW2(29)) >> 30); + else + fprintf(f, "%llu PB\n", + (sb->s_kbytes_written + POW2(39)) >> 40); + } + fprintf(f, "Reserved blocks uid: "); + print_user(sb->s_def_resuid, f); + fprintf(f, "Reserved blocks gid: "); + print_group(sb->s_def_resgid, f); + if (sb->s_rev_level >= EXT2_DYNAMIC_REV) { + fprintf(f, "First inode: %d\n", sb->s_first_ino); + fprintf(f, "Inode size: %d\n", sb->s_inode_size); + if (sb->s_min_extra_isize) + fprintf(f, "Required extra isize: %d\n", + sb->s_min_extra_isize); + if (sb->s_want_extra_isize) + fprintf(f, "Desired extra isize: %d\n", + sb->s_want_extra_isize); + } + if (!e2p_is_null_uuid(sb->s_journal_uuid)) + fprintf(f, "Journal UUID: %s\n", + e2p_uuid2str(sb->s_journal_uuid)); + if (sb->s_journal_inum) + fprintf(f, "Journal inode: %u\n", + sb->s_journal_inum); + if (sb->s_journal_dev) + fprintf(f, "Journal device: 0x%04x\n", + sb->s_journal_dev); + if (sb->s_last_orphan) + fprintf(f, "First orphan inode: %u\n", + sb->s_last_orphan); + if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || + sb->s_def_hash_version) + fprintf(f, "Default directory hash: %s\n", + e2p_hash2string(sb->s_def_hash_version)); + if (!e2p_is_null_uuid(sb->s_hash_seed)) + fprintf(f, "Directory Hash Seed: %s\n", + e2p_uuid2str(sb->s_hash_seed)); + if (sb->s_jnl_backup_type) { + fprintf(f, "Journal backup: "); + switch (sb->s_jnl_backup_type) { + case 1: + fprintf(f, "inode blocks\n"); + break; + default: + fprintf(f, "type %u\n", sb->s_jnl_backup_type); + } + } + if (sb->s_snapshot_inum) { + fprintf(f, "Snapshot inode: %u\n", + sb->s_snapshot_inum); + fprintf(f, "Snapshot ID: %u\n", + sb->s_snapshot_id); + fprintf(f, "Snapshot reserved blocks: %llu\n", + sb->s_snapshot_r_blocks_count); + } + if (sb->s_snapshot_list) + fprintf(f, "Snapshot list head: %u\n", + sb->s_snapshot_list); + if (sb->s_error_count) + fprintf(f, "FS Error count: %u\n", + sb->s_error_count); + if (sb->s_first_error_time) { + tm = sb->s_first_error_time; + fprintf(f, "First error time: %s", ctime(&tm)); + memset(buf, 0, sizeof(buf)); + strncpy(buf, (char *)sb->s_first_error_func, + sizeof(sb->s_first_error_func)); + fprintf(f, "First error function: %s\n", buf); + fprintf(f, "First error line #: %u\n", + sb->s_first_error_line); + fprintf(f, "First error inode #: %u\n", + sb->s_first_error_ino); + fprintf(f, "First error block #: %llu\n", + sb->s_first_error_block); + } + if (sb->s_last_error_time) { + tm = sb->s_last_error_time; + fprintf(f, "Last error time: %s", ctime(&tm)); + memset(buf, 0, sizeof(buf)); + strncpy(buf, (char *) sb->s_last_error_func, + sizeof(sb->s_last_error_func)); + fprintf(f, "Last error function: %s\n", buf); + fprintf(f, "Last error line #: %u\n", + sb->s_last_error_line); + fprintf(f, "Last error inode #: %u\n", + sb->s_last_error_ino); + fprintf(f, "Last error block #: %llu\n", + sb->s_last_error_block); + } +} + +void list_super (struct ext2_super_block * s) +{ + list_super2(s, stdout); +} + diff --git a/lib/libext2fs/orig/e2p/mntopts.c b/lib/libext2fs/orig/e2p/mntopts.c new file mode 100644 index 0000000..0903ad5 --- /dev/null +++ b/lib/libext2fs/orig/e2p/mntopts.c @@ -0,0 +1,147 @@ +/* + * mountopts.c --- convert between default mount options and strings + * + * Copyright (C) 2002 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct mntopt { + unsigned int mask; + const char *string; +}; + +static struct mntopt mntopt_list[] = { + { EXT2_DEFM_DEBUG, "debug" }, + { EXT2_DEFM_BSDGROUPS, "bsdgroups" }, + { EXT2_DEFM_XATTR_USER, "user_xattr" }, + { EXT2_DEFM_ACL, "acl" }, + { EXT2_DEFM_UID16, "uid16" }, + { EXT3_DEFM_JMODE_DATA, "journal_data" }, + { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" }, + { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" }, + { EXT4_DEFM_NOBARRIER, "nobarrier" }, + { EXT4_DEFM_BLOCK_VALIDITY, "block_validity" }, + { EXT4_DEFM_DISCARD, "discard"}, + { EXT4_DEFM_NODELALLOC, "nodelalloc"}, + { 0, 0 }, +}; + +const char *e2p_mntopt2string(unsigned int mask) +{ + struct mntopt *f; + static char buf[20]; + int fnum; + + for (f = mntopt_list; f->string; f++) { + if (mask == f->mask) + return f->string; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "MNTOPT_%d", fnum); + return buf; +} + +int e2p_string2mntopt(char *string, unsigned int *mask) +{ + struct mntopt *f; + char *eptr; + int num; + + for (f = mntopt_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "MNTOPT_", 8)) + return 1; + + if (string[8] == 0) + return 1; + num = strtol(string+8, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +static char *skip_over_blanks(char *cp) +{ + while (*cp && isspace((int) *cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace((int) *cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a mntopt set array as requested by the user. The ok + * parameter, if non-zero, allows the application to limit what + * mntopts the user is allowed to set or clear using this function. + */ +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + int rc = 0; + + buf = malloc(strlen(str)+1); + if (!buf) + return 1; + strcpy(buf, str); + cp = buf; + while (cp && *cp) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + if (*next == 0) + next = 0; + else + *next = 0; + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2mntopt(cp, &mask)) { + rc = 1; + break; + } + if (ok && !(ok & mask)) { + rc = 1; + break; + } + if (mask & EXT3_DEFM_JMODE) + *mntopts &= ~EXT3_DEFM_JMODE; + if (neg) + *mntopts &= ~mask; + else + *mntopts |= mask; + cp = next ? next+1 : 0; + } + free(buf); + return rc; +} diff --git a/lib/libext2fs/orig/e2p/ostype.c b/lib/libext2fs/orig/e2p/ostype.c new file mode 100644 index 0000000..978315b --- /dev/null +++ b/lib/libext2fs/orig/e2p/ostype.c @@ -0,0 +1,77 @@ +/* + * getostype.c - Get the Filesystem OS type + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" +#include +#include + +static const char *os_tab[] = + { "Linux", + "Hurd", + "Masix", + "FreeBSD", + "Lites", + 0 }; + +/* + * Convert an os_type to a string + */ +char *e2p_os2string(int os_type) +{ + const char *os; + char *ret; + + if (os_type <= EXT2_OS_LITES) + os = os_tab[os_type]; + else + os = "(unknown os)"; + + ret = malloc(strlen(os)+1); + if (ret) + strcpy(ret, os); + return ret; +} + +/* + * Convert an os_type to a string + */ +int e2p_string2os(char *str) +{ + const char **cpp; + int i = 0; + + for (cpp = os_tab; *cpp; cpp++, i++) { + if (!strcasecmp(str, *cpp)) + return i; + } + return -1; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + char *s; + int i, os; + + for (i=0; i <= EXT2_OS_LITES; i++) { + s = e2p_os2string(i); + os = e2p_string2os(s); + printf("%d: %s (%d)\n", i, s, os); + if (i != os) { + fprintf(stderr, "Failure!\n"); + exit(1); + } + } + exit(0); +} +#endif + + diff --git a/lib/libext2fs/orig/e2p/parse_num.c b/lib/libext2fs/orig/e2p/parse_num.c new file mode 100644 index 0000000..83a329a --- /dev/null +++ b/lib/libext2fs/orig/e2p/parse_num.c @@ -0,0 +1,71 @@ +/* + * parse_num.c - Parse the number of blocks + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" + +#include + +unsigned long long parse_num_blocks2(const char *arg, int log_block_size) +{ + char *p; + unsigned long long num; + + num = strtoull(arg, &p, 0); + + if (p[0] && p[1]) + return 0; + + switch (*p) { /* Using fall-through logic */ + case 'T': case 't': + num <<= 10; + case 'G': case 'g': + num <<= 10; + case 'M': case 'm': + num <<= 10; + case 'K': case 'k': + num >>= log_block_size; + break; + case 's': + num >>= (1+log_block_size); + break; + case '\0': + break; + default: + return 0; + } + return num; +} + +unsigned long parse_num_blocks(const char *arg, int log_block_size) +{ + return parse_num_blocks2(arg, log_block_size); +} + +#ifdef DEBUG +#include +#include + +main(int argc, char **argv) +{ + unsigned long num; + int log_block_size = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s arg\n", argv[0]); + exit(1); + } + + num = parse_num_blocks(argv[1], log_block_size); + + printf("Parsed number: %lu\n", num); + exit(0); +} +#endif diff --git a/lib/libext2fs/orig/e2p/pe.c b/lib/libext2fs/orig/e2p/pe.c new file mode 100644 index 0000000..78c8099 --- /dev/null +++ b/lib/libext2fs/orig/e2p/pe.c @@ -0,0 +1,39 @@ +/* + * pe.c - Print a second extended filesystem errors behavior + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 94/01/09 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_errors (FILE * f, unsigned short errors) +{ + switch (errors) + { + case EXT2_ERRORS_CONTINUE: + fprintf (f, "Continue"); + break; + case EXT2_ERRORS_RO: + fprintf (f, "Remount read-only"); + break; + case EXT2_ERRORS_PANIC: + fprintf (f, "Panic"); + break; + default: + fprintf (f, "Unknown (continue)"); + } +} diff --git a/lib/libext2fs/orig/e2p/percent.c b/lib/libext2fs/orig/e2p/percent.c new file mode 100644 index 0000000..cfa7046 --- /dev/null +++ b/lib/libext2fs/orig/e2p/percent.c @@ -0,0 +1,66 @@ +/* + * percent.c - Take percentage of a number + * + * Copyright (C) 2006 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "e2p.h" + +#include + +/* + * We work really hard to calculate this accurately, while avoiding + * an overflow. "Is there a hyphen in anal-retentive?" :-) + */ +unsigned int e2p_percent(int percent, unsigned int base) +{ + unsigned int mask = ~((1 << (sizeof(unsigned int) - 1) * 8) - 1); + + if (!percent) + return 0; + if (100 % percent == 0) + return base / (100 / percent); + if (mask & base) + return (base / 100) * percent; + return base * percent / 100; +} + +#ifdef DEBUG +#include +#include + +main(int argc, char **argv) +{ + unsigned int base; + int percent; + char *p; + int log_block_size = 0; + + if (argc != 3) { + fprintf(stderr, "Usage: %s percent base\n", argv[0]); + exit(1); + } + + percent = strtoul(argv[1], &p, 0); + if (p[0] && p[1]) { + fprintf(stderr, "Bad percent: %s\n", argv[1]); + exit(1); + } + + base = strtoul(argv[2], &p, 0); + if (p[0] && p[1]) { + fprintf(stderr, "Bad base: %s\n", argv[2]); + exit(1); + } + + printf("%d percent of %u is %u.\n", percent, base, + e2p_percent(percent, base)); + + exit(0); +} +#endif diff --git a/lib/libext2fs/orig/e2p/pf.c b/lib/libext2fs/orig/e2p/pf.c new file mode 100644 index 0000000..f34a5cc --- /dev/null +++ b/lib/libext2fs/orig/e2p/pf.c @@ -0,0 +1,78 @@ +/* + * pf.c - Print file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include + +#include "e2p.h" + +struct flags_name { + unsigned long flag; + const char *short_name; + const char *long_name; +}; + +static struct flags_name flags_array[] = { + { EXT2_SECRM_FL, "s", "Secure_Deletion" }, + { EXT2_UNRM_FL, "u" , "Undelete" }, + { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, + { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, + { EXT2_IMMUTABLE_FL, "i", "Immutable" }, + { EXT2_APPEND_FL, "a", "Append_Only" }, + { EXT2_NODUMP_FL, "d", "No_Dump" }, + { EXT2_NOATIME_FL, "A", "No_Atime" }, + { EXT2_COMPR_FL, "c", "Compression_Requested" }, +#ifdef ENABLE_COMPRESSION + { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, + { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, + { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, + { EXT2_ECOMPR_FL, "E", "Compression_Error" }, +#endif + { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, + { EXT2_INDEX_FL, "I", "Indexed_directory" }, + { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, + { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, + { EXT4_EXTENTS_FL, "e", "Extents" }, + { EXT4_HUGE_FILE_FL, "h", "Huge_file" }, + { 0, NULL, NULL } +}; + +void print_flags (FILE * f, unsigned long flags, unsigned options) +{ + int long_opt = (options & PFOPT_LONG); + struct flags_name *fp; + int first = 1; + + for (fp = flags_array; fp->flag != 0; fp++) { + if (flags & fp->flag) { + if (long_opt) { + if (first) + first = 0; + else + fputs(", ", f); + fputs(fp->long_name, f); + } else + fputs(fp->short_name, f); + } else { + if (!long_opt) + fputs("-", f); + } + } + if (long_opt && first) + fputs("---", f); +} + diff --git a/lib/libext2fs/orig/e2p/ps.c b/lib/libext2fs/orig/e2p/ps.c new file mode 100644 index 0000000..e6ad60a --- /dev/null +++ b/lib/libext2fs/orig/e2p/ps.c @@ -0,0 +1,31 @@ +/* + * ps.c - Print filesystem state + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/12/22 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_state (FILE * f, unsigned short state) +{ + if (state & EXT2_VALID_FS) + fprintf (f, " clean"); + else + fprintf (f, " not clean"); + if (state & EXT2_ERROR_FS) + fprintf (f, " with errors"); +} diff --git a/lib/libext2fs/orig/e2p/setflags.c b/lib/libext2fs/orig/e2p/setflags.c new file mode 100644 index 0000000..776300d --- /dev/null +++ b/lib/libext2fs/orig/e2p/setflags.c @@ -0,0 +1,74 @@ +/* + * setflags.c - Set a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +/* + * Deal with lame glibc's that define this function without actually + * implementing it. Can you say "attractive nuisance", boys and girls? + * I knew you could! + */ +#ifdef __linux__ +#undef HAVE_CHFLAGS +#endif + +int setflags (int fd, unsigned long flags) +{ +#if HAVE_CHFLAGS + struct stat buf; + unsigned long bsd_flags = 0; + +#ifdef UF_IMMUTABLE + if (flags & EXT2_IMMUTABLE_FL) + bsd_flags |= UF_IMMUTABLE; +#endif +#ifdef UF_APPEND + if (flags & EXT2_APPEND_FL) + bsd_flags |= UF_APPEND; +#endif +#ifdef UF_NODUMP + if (flags & EXT2_NODUMP_FL) + bsd_flags |= UF_NODUMP; +#endif + + return fchflags (fd, bsd_flags); +#else +#if HAVE_EXT2_IOCTLS + int f; + + if (!fstat(fd, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + errno = EOPNOTSUPP; + return -1; + } + f = (int) flags; + return ioctl (fd, EXT2_IOC_SETFLAGS, &f); +#endif /* HAVE_EXT2_IOCTLS */ +#endif + errno = EOPNOTSUPP; + return -1; +} diff --git a/lib/libext2fs/orig/e2p/setversion.c b/lib/libext2fs/orig/e2p/setversion.c new file mode 100644 index 0000000..d7d4128 --- /dev/null +++ b/lib/libext2fs/orig/e2p/setversion.c @@ -0,0 +1,40 @@ +/* + * setversion.c - Set a file version on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_EXT2_IOCTLS +#include +#endif + +#include "e2p.h" + +int setversion (int fd, unsigned long version) +{ +#if HAVE_EXT2_IOCTLS + int ver; + + ver = (int) version; + return ioctl (fd, EXT2_IOC_SETVERSION, &ver); +#else /* ! HAVE_EXT2_IOCTLS */ + extern int errno; + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/lib/libext2fs/orig/e2p/uuid.c b/lib/libext2fs/orig/e2p/uuid.c new file mode 100644 index 0000000..310b01d --- /dev/null +++ b/lib/libext2fs/orig/e2p/uuid.c @@ -0,0 +1,84 @@ +/* + * uuid.c -- utility routines for manipulating UUID's. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include + +#include "e2p.h" + +struct uuid { + __u32 time_low; + __u16 time_mid; + __u16 time_hi_and_version; + __u16 clock_seq; + __u8 node[6]; +}; + +/* Returns 1 if the uuid is the NULL uuid */ +int e2p_is_null_uuid(void *uu) +{ + __u8 *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp++) + return 0; + return 1; +} + +static void e2p_unpack_uuid(void *in, struct uuid *uu) +{ + __u8 *ptr = in; + __u32 tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +void e2p_uuid_to_str(void *uu, char *out) +{ + struct uuid uuid; + + e2p_unpack_uuid(uu, &uuid); + sprintf(out, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +const char *e2p_uuid2str(void *uu) +{ + static char buf[80]; + + if (e2p_is_null_uuid(uu)) + return ""; + e2p_uuid_to_str(uu, buf); + return buf; +} + diff --git a/lib/libext2fs/orig/expanddir.c b/lib/libext2fs/orig/expanddir.c new file mode 100644 index 0000000..7673a3b --- /dev/null +++ b/lib/libext2fs/orig/expanddir.c @@ -0,0 +1,126 @@ +/* + * expand.c --- expand an ext2fs directory + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct expand_dir_struct { + int done; + int newblocks; + errcode_t err; +}; + +static int expand_dir_proc(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; + blk64_t new_blk; + static blk64_t last_blk = 0; + char *block; + errcode_t retval; + + if (*blocknr) { + last_blk = *blocknr; + return 0; + } + retval = ext2fs_new_block2(fs, last_blk, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt > 0) { + retval = ext2fs_new_dir_block(fs, 0, 0, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + es->done = 1; + retval = ext2fs_write_dir_block(fs, new_blk, block); + } else { + retval = ext2fs_get_mem(fs->blocksize, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + memset(block, 0, fs->blocksize); + retval = io_channel_write_blk64(fs->io, new_blk, 1, block); + } + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + ext2fs_free_mem(&block); + *blocknr = new_blk; + ext2fs_block_alloc_stats2(fs, new_blk, +1); + es->newblocks++; + + if (es->done) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; +} + +errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) +{ + errcode_t retval; + struct expand_dir_struct es; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + es.done = 0; + es.err = 0; + es.newblocks = 0; + + retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND, + 0, expand_dir_proc, &es); + + if (es.err) + return es.err; + if (!es.done) + return EXT2_ET_EXPAND_DIR_ERR; + + /* + * Update the size and block count fields in the inode. + */ + retval = ext2fs_read_inode(fs, dir, &inode); + if (retval) + return retval; + + inode.i_size += fs->blocksize; + ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); + + retval = ext2fs_write_inode(fs, dir, &inode); + if (retval) + return retval; + + return 0; +} diff --git a/lib/libext2fs/orig/ext2.c b/lib/libext2fs/orig/ext2.c new file mode 100644 index 0000000..7cff7f3 --- /dev/null +++ b/lib/libext2fs/orig/ext2.c @@ -0,0 +1,420 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" +#include "gekko_io.h" +#include "mem_allocate.h" +#include "partitions.h" + +bool ext2Mount(const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) +{ + errcode_t retval = -1; + ext2_filsys fs = NULL; + io_channel io_chan = NULL; + gekko_fd *fd = NULL; + ext2_vd * vd = NULL; + + // Sanity check + if (!name || !interface) + { + errno = EINVAL; + return false; + } + + // Allocate the device driver descriptor + fd = (gekko_fd*) mem_alloc(sizeof(gekko_fd)); + if (!fd) + goto cleanup; + + memset(fd, 0, sizeof(gekko_fd)); + + // Setup the device driver descriptor + fd->interface = interface; + fd->startSector = startSector; + fd->sectorSize = 0; + fd->sectorCount = 0; + fd->cachePageCount = cachePageCount; + fd->cachePageSize = cachePageSize; + + fs = mem_alloc(sizeof(struct struct_ext2_filsys)); + if (!fs) + { + ext2_log_trace("no memory for fs\n"); + errno = ENOMEM; + goto cleanup; + } + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + + io_chan = mem_alloc(sizeof(struct struct_io_channel)); + if (!io_chan) + { + ext2_log_trace("no memory for io_chan\n"); + errno = ENOMEM; + goto cleanup; + } + + memset(io_chan, 0, sizeof(struct struct_io_channel)); + + io_chan->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io_chan->manager = gekko_io_manager; + io_chan->name = strdup(name); + if(!io_chan->name) goto cleanup; + io_chan->block_size = 1024; + io_chan->read_error = 0; + io_chan->write_error = 0; + io_chan->refcount = 1; + io_chan->private_data = fd; + io_chan->flags = flags; + + retval = ext2fs_open2(io_chan->name, 0, io_chan->flags, 0, 0, &io_chan, &fs); + if(retval) + { + ext2_log_trace("error mounting %i\n", (int) retval); + goto cleanup; + } + + vd = mem_alloc(sizeof(ext2_vd)); + if(!vd) + { + ext2_log_trace("no memory for vd\n"); + goto cleanup; + } + + // Initialise the volume descriptor + ext2InitVolume(vd); + vd->fs = fs; + vd->io = io_chan; + vd->root = EXT2_ROOT_INO; + + // Add the device to the devoptab table + if (ext2AddDevice(name, vd)) { + ext2DeinitVolume(vd); + goto cleanup; + } + + return true; + +cleanup: + if(fd) + mem_free(fd); + if(io_chan) + mem_free(io_chan); + if(vd) + mem_free(vd); + if(fs) + { + ext2fs_close(fs); + ext2fs_free(fs); + } + + return false; +} + +void ext2Unmount(const char *name) +{ + ext2_vd *vd = NULL; + + // Get the devices volume descriptor + vd = ext2GetVolume(name); + if (!vd) + return; + + // Remove the device from the devoptab table + ext2RemoveDevice(name); + + // Deinitialise the volume descriptor + ext2DeinitVolume(vd); + + // Unmount the volume + ext2fs_close(vd->fs); + ext2fs_free(vd->fs); + + //Free the io manager + mem_free(vd->io->private_data); + mem_free(vd->io); + + // Free the volume descriptor + mem_free(vd); + + return; +} + + +const char *ext2GetVolumeName (const char *name) +{ + if (!name) { + errno = EINVAL; + return NULL; + } + + // Get the devices volume descriptor + ext2_vd *vd = ext2GetVolume(name); + if (!vd) { + errno = ENODEV; + return NULL; + } + + return vd->fs->super->s_volume_name; +} + +bool ext2SetVolumeName (const char *name, const char *volumeName) +{ + // Sanity check + if (!name || !volumeName) { + errno = EINVAL; + return false; + } + + // Get the devices volume descriptor + ext2_vd *vd = ext2GetVolume(name); + if (!vd) { + errno = ENODEV; + return false; + } + + // Lock + ext2Lock(vd); + int i; + for(i = 0; i < 15 && *volumeName != 0; ++i, volumeName++) + vd->fs->super->s_volume_name[i] = *volumeName; + + vd->fs->super->s_volume_name[i] = '\0'; + + ext2fs_mark_super_dirty(vd->fs); + + ext2Sync(vd, NULL); + + // Unlock + ext2Unlock(vd); + + return true; +} + +int ext2FindPartitions (const DISC_INTERFACE *interface, sec_t **out_partitions) +{ + MASTER_BOOT_RECORD mbr; + PARTITION_RECORD *partition = NULL; + int partition_count = 0, ret = -1; + sec_t part_lba = 0; + sec_t * partitions = NULL; + int i; + + union { + u8 buffer[512]; + MASTER_BOOT_RECORD mbr; + EXTENDED_BOOT_RECORD ebr; + } sector; + + // Sanity check + if (!interface) { + errno = EINVAL; + return -1; + } + + if(!out_partitions) { + errno = EINVAL; + return -1; + } + + // Start the device and check that it is inserted + if (!interface->startup()) { + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + errno = EIO; + return 0; + } + + struct ext2_super_block * super = (struct ext2_super_block *) malloc(SUPERBLOCK_SIZE); //1024 bytes + if(!super) + { + ext2_log_trace("no memory for superblock"); + errno = ENOMEM; + return -1; + } + + partitions = (sec_t *) malloc(sizeof(sec_t)); + if(!partitions) + { + ext2_log_trace("no memory for partitions"); + errno = ENOMEM; + mem_free(super); + return -1; + } + // Read the first sector on the device + if (!interface->readSectors(0, 1, §or.buffer)) { + errno = EIO; + mem_free(partitions); + mem_free(super); + return -1; + } + + // If this is the devices master boot record + if (sector.mbr.signature == MBR_SIGNATURE) + { + memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); + + // Search the partition table for all EXT2/3/4 partitions (max. 4 primary partitions) + for (i = 0; i < 4; i++) + { + partition = &mbr.partitions[i]; + part_lba = ext2fs_le32_to_cpu(mbr.partitions[i].lba_start); + + // Figure out what type of partition this is + switch (partition->type) + { + // Ignore empty partitions + case PARTITION_TYPE_EMPTY: + continue; + + // EXT2/3/4 partition + case PARTITION_TYPE_LINUX: + + // Read and validate the EXT partition + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + + break; + + // DOS 3.3+ or Windows 95 extended partition + case PARTITION_TYPE_DOS33_EXTENDED: + case PARTITION_TYPE_WIN95_EXTENDED: + { + ext2_log_trace("Partition %i: Claims to be Extended\n", i + 1); + + // Walk the extended partition chain, finding all EXT partitions within it + sec_t ebr_lba = part_lba; + sec_t next_erb_lba = 0; + do { + // Read and validate the extended boot record + if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) + { + if (sector.ebr.signature == EBR_SIGNATURE) + { + ext2_log_trace("Logical Partition @ %d: %s type 0x%x\n", ebr_lba + next_erb_lba, + sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", + sector.ebr.partition.type); + + // Get the start sector of the current partition + // and the next extended boot record in the chain + part_lba = ebr_lba + next_erb_lba + ext2fs_le32_to_cpu(sector.ebr.partition.lba_start); + next_erb_lba = ext2fs_le32_to_cpu(sector.ebr.next_ebr.lba_start); + + // Check if this partition has a valid EXT boot record + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + } + else + next_erb_lba = 0; + } + + } while (next_erb_lba); + + break; + + } + + // Unknown or unsupported partition type + default: + { + // Check if this partition has a valid EXT boot record anyway, + // it might be misrepresented due to a lazy partition editor + if (interface->readSectors(part_lba+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = part_lba; + } + } + break; + } + } + } + + // Else it is assumed this device has no master boot record + } + else + { + ext2_log_trace("No Master Boot Record was found!\n"); + + // As a last-ditched effort, search the first 64 sectors of the device for stray EXT partitions + for (i = 1; i < 64; i++) + { + if (interface->readSectors(i+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + if (ext2fs_le16_to_cpu(super->s_magic) == EXT2_SUPER_MAGIC) + { + partition_count++; + sec_t * tmp = (sec_t *) realloc(partitions, partition_count*sizeof(sec_t)); + if(!tmp) goto cleanup; + partitions = tmp; + partitions[partition_count-1] = i; + } + } + } + + } + + // Return the found partitions (if any) + if (partition_count > 0) + { + *out_partitions = partitions; + ret = partition_count; + } + +cleanup: + + if(partitions && partition_count == 0) + mem_free(partitions); + if(super) + mem_free(super); + + return ret; +} + diff --git a/lib/libext2fs/orig/ext2_err.h b/lib/libext2fs/orig/ext2_err.h new file mode 100644 index 0000000..4f12744 --- /dev/null +++ b/lib/libext2fs/orig/ext2_err.h @@ -0,0 +1,152 @@ +// +// Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. +// +// This file may be redistributed under the terms of the GNU Public +// License. +#ifndef EXT2_ERR_H_ +#define EXT2_ERR_H_ + +#define EXT2_ET_OK 0 +#define EXT2_ET_BASE -1 +#define EXT2_ET_MAGIC_EXT2FS_FILSYS -2 +#define EXT2_ET_MAGIC_BADBLOCKS_LIST -3 +#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE -4 +#define EXT2_ET_MAGIC_INODE_SCAN -5 +#define EXT2_ET_MAGIC_IO_CHANNEL -6 +#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL -7 +#define EXT2_ET_MAGIC_IO_MANAGER -8 +#define EXT2_ET_MAGIC_BLOCK_BITMAP -9 +#define EXT2_ET_MAGIC_INODE_BITMAP -10 +#define EXT2_ET_MAGIC_GENERIC_BITMAP -11 +#define EXT2_ET_MAGIC_TEST_IO_CHANNEL -12 +#define EXT2_ET_MAGIC_DBLIST -13 +#define EXT2_ET_MAGIC_ICOUNT -14 +#define EXT2_ET_MAGIC_PQ_IO_CHANNEL -15 +#define EXT2_ET_MAGIC_EXT2_FILE -16 +#define EXT2_ET_MAGIC_E2IMAGE -17 +#define EXT2_ET_MAGIC_INODE_IO_CHANNEL -18 +#define EXT2_ET_MAGIC_EXTENT_HANDLE -19 +#define EXT2_ET_BAD_MAGIC -20 +#define EXT2_ET_REV_TOO_HIGH -21 +#define EXT2_ET_RO_FILSYS -22 +#define EXT2_ET_GDESC_READ -23 +#define EXT2_ET_GDESC_WRITE -24 +#define EXT2_ET_GDESC_BAD_BLOCK_MAP -25 +#define EXT2_ET_GDESC_BAD_INODE_MAP -26 +#define EXT2_ET_GDESC_BAD_INODE_TABLE -27 +#define EXT2_ET_INODE_BITMAP_WRITE -28 +#define EXT2_ET_INODE_BITMAP_READ -29 +#define EXT2_ET_BLOCK_BITMAP_WRITE -30 +#define EXT2_ET_BLOCK_BITMAP_READ -31 +#define EXT2_ET_INODE_TABLE_WRITE -32 +#define EXT2_ET_INODE_TABLE_READ -33 +#define EXT2_ET_NEXT_INODE_READ -34 +#define EXT2_ET_UNEXPECTED_BLOCK_SIZE -35 +#define EXT2_ET_DIR_CORRUPTED -36 +#define EXT2_ET_SHORT_READ -37 +#define EXT2_ET_SHORT_WRITE -38 +#define EXT2_ET_DIR_NO_SPACE -39 +#define EXT2_ET_NO_INODE_BITMAP -40 +#define EXT2_ET_NO_BLOCK_BITMAP -41 +#define EXT2_ET_BAD_INODE_NUM -42 +#define EXT2_ET_BAD_BLOCK_NUM -45 +#define EXT2_ET_EXPAND_DIR_ERR -46 +#define EXT2_ET_TOOSMALL -47 +#define EXT2_ET_BAD_BLOCK_MARK -48 +#define EXT2_ET_BAD_BLOCK_UNMARK -49 +#define EXT2_ET_BAD_BLOCK_TEST -50 +#define EXT2_ET_BAD_INODE_MARK -51 +#define EXT2_ET_BAD_INODE_UNMARK -52 +#define EXT2_ET_BAD_INODE_TEST -53 +#define EXT2_ET_FUDGE_BLOCK_BITMAP_END -54 +#define EXT2_ET_FUDGE_INODE_BITMAP_END -55 +#define EXT2_ET_BAD_IND_BLOCK -56 +#define EXT2_ET_BAD_DIND_BLOCK -57 +#define EXT2_ET_BAD_TIND_BLOCK -58 +#define EXT2_ET_NEQ_BLOCK_BITMAP -59 +#define EXT2_ET_NEQ_INODE_BITMAP -60 +#define EXT2_ET_BAD_DEVICE_NAME -61 +#define EXT2_ET_MISSING_INODE_TABLE -62 +#define EXT2_ET_CORRUPT_SUPERBLOCK -63 +#define EXT2_ET_BAD_GENERIC_MARK -64 +#define EXT2_ET_BAD_GENERIC_UNMARK -65 +#define EXT2_ET_BAD_GENERIC_TEST -66 +#define EXT2_ET_SYMLINK_LOOP -67 +#define EXT2_ET_CALLBACK_NOTHANDLED -68 +#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE -69 +#define EXT2_ET_UNSUPP_FEATURE -70 +#define EXT2_ET_RO_UNSUPP_FEATURE -71 +#define EXT2_ET_LLSEEK_FAILED -72 +#define EXT2_ET_NO_MEMORY -73 +#define EXT2_ET_INVALID_ARGUMENT -74 +#define EXT2_ET_BLOCK_ALLOC_FAIL -75 +#define EXT2_ET_INODE_ALLOC_FAIL -76 +#define EXT2_ET_NO_DIRECTORY -77 +#define EXT2_ET_TOO_MANY_REFS -78 +#define EXT2_ET_FILE_NOT_FOUND -79 +#define EXT2_ET_FILE_RO -80 +#define EXT2_ET_DB_NOT_FOUND -81 +#define EXT2_ET_DIR_EXISTS -82 +#define EXT2_ET_UNIMPLEMENTED -83 +#define EXT2_ET_CANCEL_REQUESTED -84 +#define EXT2_ET_FILE_TOO_BIG -85 +#define EXT2_ET_JOURNAL_NOT_BLOCK -86 +#define EXT2_ET_NO_JOURNAL_SB -87 +#define EXT2_ET_JOURNAL_TOO_SMALL -88 +#define EXT2_ET_JOURNAL_UNSUPP_VERSION -89 +#define EXT2_ET_LOAD_EXT_JOURNAL -90 +#define EXT2_ET_NO_JOURNAL -91 +#define EXT2_ET_DIRHASH_UNSUPP -92 +#define EXT2_ET_BAD_EA_BLOCK_NUM -93 +#define EXT2_ET_TOO_MANY_INODES -94 +#define EXT2_ET_NOT_IMAGE_FILE -95 +#define EXT2_ET_RES_GDT_BLOCKS -96 +#define EXT2_ET_RESIZE_INODE_CORRUPT -97 +#define EXT2_ET_SET_BMAP_NO_IND -98 +#define EXT2_ET_TDB_SUCCESS -99 +#define EXT2_ET_TDB_ERR_CORRUPT -100 +#define EXT2_ET_TDB_ERR_IO -101 +#define EXT2_ET_TDB_ERR_LOCK -102 +#define EXT2_ET_TDB_ERR_OOM -103 +#define EXT2_ET_TDB_ERR_EXISTS -104 +#define EXT2_ET_TDB_ERR_NOLOCK -105 +#define EXT2_ET_TDB_ERR_EINVAL -106 +#define EXT2_ET_TDB_ERR_NOEXIST -107 +#define EXT2_ET_TDB_ERR_RDONLY -108 +#define EXT2_ET_DBLIST_EMPTY -109 +#define EXT2_ET_RO_BLOCK_ITERATE -110 +#define EXT2_ET_MAGIC_EXTENT_PATH -111 +#define EXT2_ET_MAGIC_RESERVED_10 -112 +#define EXT2_ET_MAGIC_RESERVED_11 -113 +#define EXT2_ET_MAGIC_RESERVED_12 -114 +#define EXT2_ET_MAGIC_RESERVED_13 -115 +#define EXT2_ET_MAGIC_RESERVED_14 -116 +#define EXT2_ET_MAGIC_RESERVED_15 -117 +#define EXT2_ET_MAGIC_RESERVED_16 -118 +#define EXT2_ET_MAGIC_RESERVED_17 -119 +#define EXT2_ET_MAGIC_RESERVED_18 -120 +#define EXT2_ET_MAGIC_RESERVED_19 -121 +#define EXT2_ET_EXTENT_HEADER_BAD -122 +#define EXT2_ET_EXTENT_INDEX_BAD -123 +#define EXT2_ET_EXTENT_LEAF_BAD -124 +#define EXT2_ET_EXTENT_NO_SPACE -125 +#define EXT2_ET_INODE_NOT_EXTENT -126 +#define EXT2_ET_EXTENT_NO_NEXT -127 +#define EXT2_ET_EXTENT_NO_PREV -128 +#define EXT2_ET_EXTENT_NO_UP -129 +#define EXT2_ET_EXTENT_NO_DOWN -130 +#define EXT2_ET_NO_CURRENT_NODE -131 +#define EXT2_ET_OP_NOT_SUPPORTED -132 +#define EXT2_ET_CANT_INSERT_EXTENT -133 +#define EXT2_ET_CANT_SPLIT_EXTENT -134 +#define EXT2_ET_EXTENT_NOT_FOUND -135 +#define EXT2_ET_EXTENT_NOT_SUPPORTED -136 +#define EXT2_ET_EXTENT_INVALID_LENGTH -137 +#define EXT2_ET_IO_CHANNEL_NO_SUPPORT_64 -138 +#define EXT2_NO_MTAB_FILE -139 +#define EXT2_ET_MAGIC_GENERIC_BITMAP64 -140 +#define EXT2_ET_MAGIC_BLOCK_BITMAP64 -141 +#define EXT2_ET_MAGIC_INODE_BITMAP64 -142 +#define EXT2_ET_CANT_USE_LEGACY_BITMAPS -143 + +#endif diff --git a/lib/libext2fs/orig/ext2_ext_attr.h b/lib/libext2fs/orig/ext2_ext_attr.h new file mode 100644 index 0000000..ed548d1 --- /dev/null +++ b/lib/libext2fs/orig/ext2_ext_attr.h @@ -0,0 +1,71 @@ +/* + File: linux/ext2_ext_attr.h + + On-disk format of extended attributes for the ext2 filesystem. + + (C) 2000 Andreas Gruenbacher, +*/ + +#ifndef _EXT2_EXT_ATTR_H +#define _EXT2_EXT_ATTR_H +/* Magic value in attribute blocks */ +#define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 +#define EXT2_EXT_ATTR_MAGIC 0xEA020000 + +/* Maximum number of references to one attribute block */ +#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 + +struct ext2_ext_attr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext2_ext_attr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* attribute name index */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ +#if 0 + char e_name[0]; /* attribute name */ +#endif +}; + +#define EXT2_EXT_ATTR_PAD_BITS 2 +#define EXT2_EXT_ATTR_PAD ((unsigned) 1<e_name_len)) ) +#define EXT2_EXT_ATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) +#define EXT2_EXT_ATTR_NAME(entry) \ + (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) +#define EXT2_XATTR_LEN(name_len) \ + (((name_len) + EXT2_EXT_ATTR_ROUND + \ + sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_XATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT2_FS_EXT_ATTR +extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); +extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int); +extern void ext2_ext_attr_free_inode(struct inode *inode); +extern void ext2_ext_attr_put_super(struct super_block *sb); +extern int ext2_ext_attr_init(void); +extern void ext2_ext_attr_done(void); +# else +# define ext2_get_ext_attr NULL +# define ext2_set_ext_attr NULL +# endif +#endif /* __KERNEL__ */ +#endif /* _EXT2_EXT_ATTR_H */ diff --git a/lib/libext2fs/orig/ext2_fs.h b/lib/libext2fs/orig/ext2_fs.h new file mode 100644 index 0000000..8a58dd1 --- /dev/null +++ b/lib/libext2fs/orig/ext2_fs.h @@ -0,0 +1,799 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _LINUX_EXT2_FS_H +#define _LINUX_EXT2_FS_H + +#include "ext2_types.h" /* Changed from linux/types.h */ + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ +#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 65000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#ifdef __KERNEL__ +#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) +#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) +#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) +#else +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#endif +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) + +/* + * Macro-instructions used to manage allocation clusters + */ +#define EXT2_MIN_CLUSTER_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_MAX_CLUSTER_LOG_SIZE 29 /* 512MB */ +#define EXT2_MIN_CLUSTER_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_CLUSTER_SIZE (1 << EXT2_MAX_CLUSTER_LOG_SIZE) +#define EXT2_CLUSTER_SIZE(s) (EXT2_MIN_BLOCK_SIZE << \ + (s)->s_log_cluster_size) +#define EXT2_CLUSTER_SIZE_BITS(s) ((s)->s_log_cluster_size + 10) + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; + __u32 bg_reserved[2]; + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __u32 bg_reserved[2]; /* Likely block/inode bitmap checksum */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __u32 bg_inode_table_hi; /* Inodes table block MSB */ + __u16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __u16 bg_used_dirs_count_hi; /* Directories count MSB */ + __u16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __u32 bg_reserved2[3]; +}; + +#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ +#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ +#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + __u32 reserved_zero; + __u8 hash_version; /* 0 now, 1 at release */ + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 +#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ +#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ +#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + __u32 hash; + __u32 block; +}; + +struct ext2_dx_countlimit { + __u16 limit; + __u16 count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_MIN_DESC_SIZE 32 +#define EXT2_MIN_DESC_SIZE_64BIT 64 +#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_DESC_SIZE(s) \ + ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \ + (s)->s_desc_size : EXT2_MIN_DESC_SIZE) + +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) +#ifdef __KERNEL__ +#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) +#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) +#else +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ +#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ +#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ + +/* + * ioctl commands + */ + +/* Used for online resize */ +struct ext2_new_group_input { + __u32 group; /* Group number for this data */ + __u32 block_bitmap; /* Absolute block number of block bitmap */ + __u32 inode_bitmap; /* Absolute block number of inode bitmap */ + __u32 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; /* Number of reserved GDT blocks in group */ +}; + +struct ext4_new_group_input { + __u32 group; /* Group number for this data */ + __u64 block_bitmap; /* Absolute block number of block bitmap */ + __u64 inode_bitmap; /* Absolute block number of inode bitmap */ + __u64 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +#ifdef __GNU__ /* Needed for the Hurd */ +#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) +#endif + +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) +#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) +#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) +#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) +#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode Change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ + __u16 i_extra_isize; + __u16 i_pad1; + __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ + __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __u32 i_crtime; /* File creation time */ + __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ + __u32 i_version_hi; /* high 32 bits for 64-bit version */ +}; + +#define i_dir_acl i_size_high + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#define i_reserved2 osd2.linux2.l_i_reserved2 +#else +#if defined(__GNU__) + +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author + +#endif /* __GNU__ */ +#endif /* defined(__KERNEL__) || defined(__linux__) */ + +#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) +#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) +#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x)) +#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x)) + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ +#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ + +/* + * Misc. filesystem flags + */ +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ +#define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ +#define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ +#define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +#if (__GNUC__ >= 4) +#define ext4_offsetof(TYPE,MEMBER) __builtin_offsetof(TYPE,MEMBER) +#else +#define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __u32 s_log_cluster_size; /* Allocation cluster size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_clusters_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ + __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ + __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ + __u32 s_blocks_count_hi; /* Blocks count high 32bits */ + __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ + __u32 s_free_blocks_hi; /* Free blocks count */ + __u16 s_min_extra_isize; /* All inodes have at least # bytes */ + __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __u32 s_flags; /* Miscellaneous flags */ + __u16 s_raid_stride; /* RAID stride */ + __u16 s_mmp_interval; /* # seconds to wait in MMP checking */ + __u64 s_mmp_block; /* Block for multi-mount protection */ + __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_reserved_char_pad; + __u16 s_reserved_pad; /* Padding to next 32bits */ + __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __u32 s_snapshot_inum; /* Inode number of active snapshot */ + __u32 s_snapshot_id; /* sequential ID of active snapshot */ + __u64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __u32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ +#define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count) + __u32 s_error_count; /* number of fs errors */ + __u32 s_first_error_time; /* first time an error happened */ + __u32 s_first_error_ino; /* inode involved in first error */ + __u64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __u32 s_first_error_line; /* line number where error happened */ + __u32 s_last_error_time; /* most recent time of an error */ + __u32 s_last_error_ino; /* inode involved in last error */ + __u32 s_last_error_line; /* line number where error happened */ + __u64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __u32 s_usr_quota_inum; /* inode number of user quota file */ + __u32 s_grp_quota_inum; /* inode number of group quota file */ + __u32 s_overhead_blocks; /* overhead blocks/clusters in fs */ + __u32 s_reserved[109]; /* Padding to the end of the block */ +}; + +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OBSO_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 +#define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +/* + * This structure will be used for multiple mount protection. It will be + * written into the block number saved in the s_mmp_block field in the + * superblock. + */ +#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */ +#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */ +#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */ + +struct mmp_struct { + __u32 mmp_magic; + __u32 mmp_seq; + __u64 mmp_time; + char mmp_nodename[64]; + char mmp_bdevname[32]; + __u16 mmp_interval; + __u16 mmp_pad1; + __u32 mmp_pad2; +}; + +/* + * Interval in number of seconds to update the MMP sequence number. + */ +#define EXT2_MMP_DEF_INTERVAL 5 + +#endif /* _LINUX_EXT2_FS_H */ diff --git a/lib/libext2fs/orig/ext2_internal.c b/lib/libext2fs/orig/ext2_internal.c new file mode 100644 index 0000000..d75133a --- /dev/null +++ b/lib/libext2fs/orig/ext2_internal.c @@ -0,0 +1,1076 @@ +/** + * ext2_internal.c - Internal support routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "ext2_internal.h" +#include "ext2dir.h" +#include "ext2file.h" +#include "gekko_io.h" + +// EXT2 device driver devoptab +static const devoptab_t devops_ext2 = +{ + NULL, /* Device name */ + sizeof(ext2_file_state), + ext2_open_r, + ext2_close_r, + ext2_write_r, + ext2_read_r, + ext2_seek_r, + ext2_fstat_r, + ext2_stat_r, + ext2_link_r, + ext2_unlink_r, + ext2_chdir_r, + ext2_rename_r, + ext2_mkdir_r, + sizeof(ext2_dir_state), + ext2_diropen_r, + ext2_dirreset_r, + ext2_dirnext_r, + ext2_dirclose_r, + ext2_statvfs_r, + ext2_ftruncate_r, + ext2_fsync_r, + NULL /* Device data */ +}; + + +const devoptab_t *ext2GetDevOpTab() +{ + return &devops_ext2; +} + +int ext2AddDevice (const char *name, void *deviceData) +{ + const devoptab_t *devoptab_ext2 = ext2GetDevOpTab(); + devoptab_t *dev = NULL; + char *devname = NULL; + int i; + + // Sanity check + if (!name || !deviceData || !devoptab_ext2) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) mem_alloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return -1; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // Setup the devoptab + memcpy(dev, devoptab_ext2, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = deviceData; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 0; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0] && i != 0) { + devoptab_list[i] = dev; + return 0; + } + } + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +void ext2RemoveDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + mem_free((devoptab_t*)devoptab); + break; + } + } + } + + return; +} + +const devoptab_t *ext2GetDevice (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes + for (i = 0; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return devoptab; + } + } + } + + return NULL; +} + +ext2_vd *ext2GetVolume (const char *path) +{ + // Get the volume descriptor from the paths associated devoptab (if found) + const devoptab_t *devoptab_ext2 = ext2GetDevOpTab(); + const devoptab_t *devoptab = ext2GetDevice(path); + if (devoptab && devoptab_ext2 && (devoptab->open_r == devoptab_ext2->open_r)) + return (ext2_vd*)devoptab->deviceData; + + return NULL; +} + +int ext2InitVolume (ext2_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + // Reset the volumes data + memset(vd, 0, sizeof(ext2_vd)); + + // Initialise the volume lock + LWP_MutexInit(&vd->lock, false); + + return 0; +} + +void ext2DeinitVolume (ext2_vd *vd) +{ + // Sanity check + if (!vd) { + errno = ENODEV; + return; + } + + // Lock + ext2Lock(vd); + + // Close any directories which are still open (lazy programmers!) + ext2_dir_state *nextDir = vd->firstOpenDir; + while (nextDir) { + ext2CloseDir(nextDir); + nextDir = nextDir->nextOpenDir; + } + + // Close any files which are still open (lazy programmers!) + ext2_file_state *nextFile = vd->firstOpenFile; + while (nextFile) { + ext2CloseFile(nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Reset open directory and file stats + vd->openDirCount = 0; + vd->openFileCount = 0; + vd->firstOpenDir = NULL; + vd->firstOpenFile = NULL; + + // Force the underlying device to sync + ext2Sync(vd, NULL); + + // Unlock + ext2Unlock(vd); + + // Deinitialise the volume lock + LWP_MutexDestroy(vd->lock); +} + +static ext2_ino_t ext2PathToInode(ext2_vd *vd, const char * path) +{ + //Sanity check + if(!vd || !path) + return 0; + + char filename[EXT2_NAME_LEN]; + errcode_t errorcode = 0; + ext2_ino_t ino = 0, parent = vd->cwd_ni && *path != '/' && *path != '\0' ? vd->cwd_ni->ino : vd->root; + const char * ptr = path; + int i; + + while(*ptr == '/') ++ptr; + + if(*ptr == '\0') + return parent; + + while(*ptr != '\0') + { + for(i = 0; *ptr != '\0' && *ptr != '/' && (i < EXT2_NAME_LEN-1); ++ptr, ++i) + filename[i] = *ptr; + + filename[i] = '\0'; + + errorcode = ext2fs_namei(vd->fs, vd->root, parent, filename, &ino); + if(errorcode != EXT2_ET_OK) + return 0; + + parent = ino; + + while(*ptr == '/') ++ptr; + + } + + + return ino; +} + +ext2_inode_t *ext2OpenEntry (ext2_vd *vd, const char *path) +{ + errcode_t errorcode = 0; + ext2_inode_t * ni = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return NULL; + } + + // Get the actual path of the entry + path = ext2RealPath(path); + if (!path) + { + errno = EINVAL; + return NULL; + } + + ni = mem_alloc(sizeof(ext2_inode_t)); + if(!ni) + { + errno = ENOMEM; + return NULL; + } + + memset(ni, 0, sizeof(ext2_inode_t)); + + // Find the entry, taking into account our current directory (if any) + ni->ino = ext2PathToInode(vd, path); + if(ni->ino == 0) + { + mem_free(ni); + return NULL; + } + + errorcode = ext2fs_read_inode(vd->fs, ni->ino, &ni->ni); + if(errorcode) + { + mem_free(ni); + return NULL; + } + + return ni; +} + +void ext2CloseEntry(ext2_vd *vd, ext2_inode_t * ni) +{ + // Sanity check + if (!vd || !ni) { + errno = ENODEV; + return; + } + + // Lock + ext2Lock(vd); + + // Sync the entry (if it is dirty) + if(ni && ni->dirty) + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + + // Close the entry + if(ni) + mem_free(ni); + + // Unlock + ext2Unlock(vd); + + return; +} + +static ext2_ino_t ext2CreateSymlink(ext2_vd *vd, const char *path, const char * targetdir, const char * name, mode_t type) +{ + ext2_inode_t *target_ni = NULL; + ext2_ino_t newentry = 0; + ext2_ino_t ino = 0; + + // Check if it does exist + target_ni = ext2OpenEntry(vd, targetdir); + if (!target_ni) + goto cleanup; + + int err = ext2fs_new_inode(vd->fs, target_ni->ino, type, 0, &ino); + if (err) + goto cleanup; + + do + { + err = ext2fs_link(vd->fs, target_ni->ino, name, ino, EXT2_FT_SYMLINK); + if (err == EXT2_ET_DIR_NO_SPACE) + { + err = ext2fs_expand_dir(vd->fs, target_ni->ino); + if (err) + goto cleanup; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + ext2fs_inode_alloc_stats2(vd->fs, ino, +1, 0); + + struct ext2_inode inode; + memset(&inode, 0, sizeof(inode)); + inode.i_mode = type; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); + inode.i_links_count = 1; + inode.i_size = strlen(path); //initial size of file + inode.i_uid = target_ni->ni.i_uid; + inode.i_gid = target_ni->ni.i_gid; + + if (strlen(path) <= sizeof(inode.i_block)) + { + /* fast symlink */ + strncpy((char *)&(inode.i_block[0]),path,sizeof(inode.i_blocks)); + } + else + { + /* slow symlink */ + char * buffer = mem_alloc(vd->fs->blocksize); + if (buffer) + { + blk_t blk; + strncpy(buffer, path, vd->fs->blocksize); + err = ext2fs_new_block(vd->fs, 0, 0, &blk); + if (!err) + { + inode.i_block[0] = blk; + inode.i_blocks = vd->fs->blocksize / BYTES_PER_SECTOR; + vd->fs->io->manager->write_blk(vd->fs->io, blk, 1, buffer); + ext2fs_block_alloc_stats(vd->fs, blk, +1); + } + mem_free(buffer); + } + } + + if(ext2fs_write_new_inode(vd->fs, ino, &inode) != 0) + newentry = ino; + +cleanup: + + if(target_ni) + ext2CloseEntry(vd, target_ni); + + return newentry; +} + +static ext2_ino_t ext2CreateMkDir(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name) +{ + ext2_ino_t newentry = 0; + ext2_ino_t existing; + + if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0) + return 0; + + errcode_t err = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newentry); + if(err != EXT2_ET_OK) + return 0; + + do + { + err = ext2fs_mkdir(vd->fs, parent->ino, newentry, name); + if(err == EXT2_ET_DIR_NO_SPACE) + { + if(ext2fs_expand_dir(vd->fs, parent->ino) != 0) + return 0; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + if(err != EXT2_ET_OK) + return 0; + + struct ext2_inode inode; + if(ext2fs_read_inode(vd->fs, newentry, &inode) == EXT2_ET_OK) + { + inode.i_mode = type; + inode.i_uid = parent->ni.i_uid; + inode.i_gid = parent->ni.i_gid; + ext2fs_write_new_inode(vd->fs, newentry, &inode); + } + + return newentry; +} + + +static ext2_ino_t ext2CreateFile(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name) +{ + errcode_t retval = -1; + ext2_ino_t newfile = 0; + ext2_ino_t existing; + + if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0) + return 0; + + retval = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newfile); + if (retval) + return 0; + + do + { + retval = ext2fs_link(vd->fs, parent->ino, name, newfile, EXT2_FT_REG_FILE); + if (retval == EXT2_ET_DIR_NO_SPACE) + { + if (ext2fs_expand_dir(vd->fs, parent->ino) != 0) + return 0; + } + } + while(retval == EXT2_ET_DIR_NO_SPACE); + + if (retval) + return 0; + + ext2fs_inode_alloc_stats2(vd->fs, newfile, +1, 0); + + struct ext2_inode inode; + memset(&inode, 0, sizeof(inode)); + inode.i_mode = type; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(0); + inode.i_links_count = 1; + inode.i_size = 0; + inode.i_uid = parent->ni.i_uid; + inode.i_gid = parent->ni.i_gid; + + if (ext2fs_write_new_inode(vd->fs, newfile, &inode) != 0) + return 0; + + return newfile; +} + +ext2_inode_t *ext2Create(ext2_vd *vd, const char *path, mode_t type, const char *target) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *targetdir = NULL; + char *name = NULL; + ext2_ino_t newentry = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return NULL; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return NULL; + + // You cannot link between devices + if(target) { + if(vd != ext2GetVolume(target)) { + errno = EXDEV; + return NULL; + } + // Check if existing + dir_ni = ext2OpenEntry(vd, target); + if (dir_ni) { + goto cleanup; + } + ext2CloseEntry(vd, dir_ni); + dir_ni = NULL; + targetdir = strdup(target); + if (!targetdir) { + errno = EINVAL; + goto cleanup; + } + } + + // Get the actual paths of the entry + path = ext2RealPath(path); + target = ext2RealPath(target); + if (!path) { + errno = EINVAL; + return NULL; + } + + // Lock + ext2Lock(vd); + + // Clean me + // NOTE: this looks horrible right now and need a cleanup + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + + char * tmp_path = (targetdir && (type == S_IFLNK)) ? targetdir : dir; + if (strrchr(tmp_path, '/') != NULL) + { + char * ptr = strrchr(tmp_path, '/'); + name = strdup(ptr+1); + *ptr = '\0'; + } + else + name = strdup(tmp_path); + + // Open the entries parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + goto cleanup; + } + + // If not yet read, read the inode and block bitmap + if(!vd->fs->inode_map || !vd->fs->block_map) + ext2fs_read_bitmaps(vd->fs); + + // Symbolic link + if(type == S_IFLNK) + { + if (!target) { + errno = EINVAL; + goto cleanup; + } + + newentry = ext2CreateSymlink(vd, path, targetdir, name, type); + } + // Directory + else if(type == S_IFDIR) + { + newentry = ext2CreateMkDir(vd, dir_ni, LINUX_S_IFDIR | (0755 & ~vd->fs->umask), name); + } + // File + else if(type == S_IFREG) + { + newentry = ext2CreateFile(vd, dir_ni, LINUX_S_IFREG | (0755 & ~vd->fs->umask), name); + } + + // If the entry was created + if (newentry != 0) + { + // Sync the entry to disc + ext2Sync(vd, NULL); + + ni = ext2OpenEntry(vd, target ? target : path); + } + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(name) + mem_free(name); + + if(dir) + mem_free(dir); + + if(targetdir) + mem_free(targetdir); + + // Unlock + ext2Unlock(vd); + + return ni; +} + +/* + * Given a mode, return the ext2 file type + */ +static int ext2_file_type(unsigned int mode) +{ + if (LINUX_S_ISREG(mode)) + return EXT2_FT_REG_FILE; + + if (LINUX_S_ISDIR(mode)) + return EXT2_FT_DIR; + + if (LINUX_S_ISCHR(mode)) + return EXT2_FT_CHRDEV; + + if (LINUX_S_ISBLK(mode)) + return EXT2_FT_BLKDEV; + + if (LINUX_S_ISLNK(mode)) + return EXT2_FT_SYMLINK; + + if (LINUX_S_ISFIFO(mode)) + return EXT2_FT_FIFO; + + if (LINUX_S_ISSOCK(mode)) + return EXT2_FT_SOCK; + + return 0; +} + +int ext2Link(ext2_vd *vd, const char *old_path, const char *new_path) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + errcode_t err = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // You cannot link between devices + if(vd != ext2GetVolume(new_path)) { + errno = EXDEV; + return -1; + } + + // Get the actual paths of the entry + old_path = ext2RealPath(old_path); + new_path = ext2RealPath(new_path); + if (!old_path || !new_path) { + errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(vd); + + //check for existing in new path + ni = ext2OpenEntry(vd, new_path); + if (ni) { + ext2CloseEntry(vd, ni); + ni = NULL; + errno = EINVAL; + return -1; + } + + dir = strdup(new_path); + if (!dir) { + errno = EINVAL; + err = -1; + goto cleanup; + } + char * ptr = strrchr(dir, '/'); + if (ptr) + { + name = strdup(ptr+1); + *ptr = 0; + } + else + name = strdup(dir); + + // Find the entry + ni = ext2OpenEntry(vd, old_path); + if (!ni) { + errno = ENOENT; + err = -1; + goto cleanup; + } + + // Open the entries new parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + err = -1; + goto cleanup; + } + + do + { + // Link the entry to its new parent + err = ext2fs_link(vd->fs, dir_ni->ino, name, ni->ino, ext2_file_type(ni->ni.i_mode)); + if (err == EXT2_ET_DIR_NO_SPACE) + { + if (ext2fs_expand_dir(vd->fs, dir_ni->ino) != 0) + goto cleanup; + } + else if(err != 0) + { + errno = ENOMEM; + goto cleanup; + } + } + while(err == EXT2_ET_DIR_NO_SPACE); + + ni->ni.i_links_count++; + + // Update entry times + ext2UpdateTimes(vd, ni, EXT2_UPDATE_MCTIME); + + // Sync the entry to disc + ext2Sync(vd, ni); + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(ni) + ext2CloseEntry(vd, ni); + + if(dir) + mem_free(dir); + + if(name) + mem_free(name); + + // Unlock + ext2Unlock(vd); + + return err; +} + +typedef struct _rd_struct +{ + ext2_ino_t parent; + int empty; +} rd_struct; + +static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused))) +{ + blk_t block; + + block = *blocknr; + ext2fs_block_alloc_stats(fs, block, -1); + *blocknr = 0; + return 0; +} + +static int unlink_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), + void *private_data) +{ + rd_struct *rds = (rd_struct *) private_data; + + if (dirent->inode == 0) + return 0; + if (((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.')) + return 0; + if (((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') && + (dirent->name[1] == '.')) { + rds->parent = dirent->inode; + return 0; + } + + rds->empty = 0; + return 0; +} + +int ext2Unlink (ext2_vd *vd, const char *path) +{ + ext2_inode_t *dir_ni = NULL, *ni = NULL; + char *dir = NULL; + char *name = NULL; + errcode_t err = -1; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // Get the actual path of the entry + path = ext2RealPath(path); + if (!path) { + errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(vd); + + dir = strdup(path); + if (!dir) { + errno = EINVAL; + goto cleanup; + } + char * ptr = strrchr(dir, '/'); + if (ptr) + { + name = strdup(ptr+1); + *ptr = 0; + } + else + name = dir; + + // Find the entry + ni = ext2OpenEntry(vd, path); + if (!ni) { + errno = ENOENT; + goto cleanup; + } + + // Open the entries parent directory + dir_ni = ext2OpenEntry(vd, dir); + if (!dir_ni) { + errno = ENOENT; + goto cleanup; + } + + // Directory + if(LINUX_S_ISDIR(ni->ni.i_mode)) + { + rd_struct rds; + rds.parent = 0; + rds.empty = 1; + + if (ext2fs_dir_iterate2(vd->fs, ni->ino, 0, 0, unlink_proc, &rds) != 0) + goto cleanup; + + if(!rds.empty) + goto cleanup; + + if (rds.parent) + { + struct ext2_inode inode; + if (ext2fs_read_inode(vd->fs, rds.parent, &inode) == 0) + { + if(inode.i_links_count > 1) + inode.i_links_count--; + ext2fs_write_inode(vd->fs, rds.parent, &inode); + } + } + + // set link count 0 + ni->ni.i_links_count = 0; + } + // File + else + { + ni->ni.i_links_count--; + } + + if(ni->ni.i_links_count <= 0) + { + ni->ni.i_size = 0; + ni->ni.i_size_high = 0; + ni->ni.i_links_count = 0; + ni->ni.i_dtime = (u32) time(0); + } + + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + + // Unlink the entry from its parent + if(ext2fs_unlink(vd->fs, dir_ni->ino, name, 0, 0) != 0) + goto cleanup; + + if (ext2fs_inode_has_valid_blocks(&ni->ni)) + { + ext2fs_block_iterate(vd->fs, ni->ino, 0, NULL, release_blocks_proc, NULL); + ext2fs_inode_alloc_stats2(vd->fs, ni->ino, -1, LINUX_S_ISDIR(ni->ni.i_mode)); + } + + if(ni->ni.i_links_count == 0) + { + // It's odd that i have to do this on my own and the lib is not doing that for me + blk64_t truncate_block = ((vd->fs->blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(vd->fs->super)) + 1; + ext2fs_punch(vd->fs, ni->ino, &ni->ni, 0, truncate_block, ~0ULL); + } + + // Sync the entry to disc + ext2Sync(vd, NULL); + + err = 0; + +cleanup: + + if(dir_ni) + ext2CloseEntry(vd, dir_ni); + + if(ni) + ext2CloseEntry(vd, ni); + + if(name) + mem_free(name); + + if(dir) + mem_free(dir); + + // Unlock + ext2Unlock(vd); + + return err; +} + + +int ext2Sync(ext2_vd *vd, ext2_inode_t *ni) +{ + errcode_t res = 0; + + // Sanity check + if (!vd || !vd->fs) { + errno = ENODEV; + return -1; + } + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return -1; + + // Lock + ext2Lock(vd); + + if(ni && ni->dirty) + { + ext2fs_write_inode(vd->fs, ni->ino, &ni->ni); + ni->dirty = false; + } + + // Sync the entry + res = ext2fs_flush(vd->fs); + + // Force the underlying device to sync + vd->io->manager->flush(vd->io); + + // Unlock + ext2Unlock(vd); + + return res; + +} + +int ext2Stat (ext2_vd *vd, ext2_inode_t *ni_main, struct stat *st) +{ + int res = 0; + + // Sanity check + if (!vd) { + errno = ENODEV; + return -1; + } + + struct ext2_inode * ni = ni_main ? &ni_main->ni : 0; + + // Sanity check + if (!ni) { + errno = ENOENT; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Lock + ext2Lock(vd); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + if(LINUX_S_ISDIR(ni->i_mode)) + { + st->st_nlink = 1; + st->st_size = ni->i_size; + } + else + { + st->st_nlink = ni->i_links_count; + st->st_size = EXT2_I_SIZE(ni); + } + + st->st_mode = ni->i_mode; + st->st_blocks = ni->i_blocks; + st->st_blksize = vd->fs->blocksize; + + // Fill in the generic entry stats + st->st_dev = (dev_t) ((long) vd->fs); + st->st_uid = ni->i_uid | (((u32) ni->osd2.linux2.l_i_uid_high) << 16); + st->st_gid = ni->i_gid | (((u32) ni->osd2.linux2.l_i_gid_high) << 16); + st->st_ino = ni_main->ino; + st->st_atime = ni->i_atime; + st->st_ctime = ni->i_ctime; + st->st_mtime = ni->i_mtime; + + // Update entry times + ext2UpdateTimes(vd, ni_main, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(vd); + + return res; +} + +void ext2UpdateTimes(ext2_vd *vd, ext2_inode_t *ni, ext2_time_update_flags mask) +{ + // Sanity check + if(!ni || !mask) + return; + + if(!(vd->fs->flags & EXT2_FLAG_RW)) + return; + + u32 now = (u32) time(0); + + if(mask & EXT2_UPDATE_ATIME) + ni->ni.i_atime = now; + if(mask & EXT2_UPDATE_MTIME) + ni->ni.i_mtime = now; + if(mask & EXT2_UPDATE_CTIME) + ni->ni.i_ctime = now; + + ni->dirty = true; +} + +const char *ext2RealPath (const char *path) +{ + // Sanity check + if (!path) + return NULL; + + // Move the path pointer to the start of the actual path + if (strchr(path, ':') != NULL) { + path = strchr(path, ':')+1; + } + if (strchr(path, ':') != NULL) { + return NULL; + } + + return path; +} diff --git a/lib/libext2fs/orig/ext2_internal.h b/lib/libext2fs/orig/ext2_internal.h new file mode 100644 index 0000000..24dd4a5 --- /dev/null +++ b/lib/libext2fs/orig/ext2_internal.h @@ -0,0 +1,102 @@ +/** + * ext2_internal.h + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef EXT2_INTERNAL_H_ +#define EXT2_INTERNAL_H_ + +#include +#include +#include +#include "ext2fs.h" +#include "ext2_fs.h" +#include "mem_allocate.h" + +#ifdef DEBUG_GEKKO +#define ext2_log_trace printf +#else +#define ext2_log_trace(...) +#endif + +typedef struct _ext2_inode_t +{ + struct ext2_inode ni; + ext2_ino_t ino; + bool dirty; +} ext2_inode_t; + +/** + * ext2_vd - EXT2 volume descriptor + */ +typedef struct _ext2_vd +{ + io_channel io; /* EXT device handle */ + ext2_filsys fs; /* EXT volume handle */ + mutex_t lock; /* Volume lock mutex */ + ext2_inode_t *cwd_ni; /* Current directory */ + struct _ext2_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */ + struct _ext2_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */ + u16 openDirCount; /* The total number of directories currently open in this volume */ + u16 openFileCount; /* The total number of files currently open in this volume */ + ext2_ino_t root; /* Root node */ +} ext2_vd; + +typedef enum { + EXT2_UPDATE_ATIME = 0x01, + EXT2_UPDATE_MTIME = 0x02, + EXT2_UPDATE_CTIME = 0x04, + EXT2_UPDATE_AMTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_MTIME, + EXT2_UPDATE_ACTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_CTIME, + EXT2_UPDATE_MCTIME = EXT2_UPDATE_MTIME | EXT2_UPDATE_CTIME, + EXT2_UPDATE_AMCTIME = EXT2_UPDATE_ATIME | EXT2_UPDATE_MTIME | EXT2_UPDATE_CTIME, +} ext2_time_update_flags; + +/* Lock volume */ +static inline void ext2Lock (ext2_vd *vd) +{ + LWP_MutexLock(vd->lock); +} + +/* Unlock volume */ +static inline void ext2Unlock (ext2_vd *vd) +{ + LWP_MutexUnlock(vd->lock); +} + +const char *ext2RealPath (const char *path); +int ext2InitVolume (ext2_vd *vd); +void ext2DeinitVolume (ext2_vd *vd); +ext2_vd *ext2GetVolume (const char *path); + +int ext2AddDevice (const char *name, void *deviceData); +void ext2RemoveDevice (const char *path); +const devoptab_t *ext2GetDevice (const char *path); + +ext2_inode_t *ext2OpenEntry (ext2_vd *vd, const char *path); +void ext2CloseEntry (ext2_vd *vd, ext2_inode_t * ni); +int ext2Stat (ext2_vd *vd, ext2_inode_t * ni, struct stat *st); +int ext2Sync (ext2_vd *vd, ext2_inode_t * ni); + +ext2_inode_t *ext2Create (ext2_vd *vd, const char *path, mode_t type, const char *target); +int ext2Link (ext2_vd *vd, const char *old_path, const char *new_path); +int ext2Unlink (ext2_vd *vd, const char *path); + +void ext2UpdateTimes(ext2_vd *vd, ext2_inode_t *ni, ext2_time_update_flags mask); + +#endif diff --git a/lib/libext2fs/orig/ext2_io.h b/lib/libext2fs/orig/ext2_io.h new file mode 100644 index 0000000..9f84d99 --- /dev/null +++ b/lib/libext2fs/orig/ext2_io.h @@ -0,0 +1,144 @@ +/* + * io.h --- the I/O manager abstraction + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _EXT2FS_EXT2_IO_H +#define _EXT2FS_EXT2_IO_H + +#include "ext2fs.h" + +/* + * ext2_loff_t is defined here since unix_io.c needs it. + */ +typedef long long ext2_loff_t; + +/* llseek.c */ +ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); + +typedef struct struct_io_manager *io_manager; +typedef struct struct_io_channel *io_channel; +typedef struct struct_io_stats *io_stats; + +#define CHANNEL_FLAGS_WRITETHROUGH 0x01 +#define CHANNEL_FLAGS_DISCARD_ZEROES 0x02 + +#define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES) + +struct struct_io_channel { + errcode_t magic; + io_manager manager; + char *name; + int block_size; + errcode_t (*read_error)(io_channel channel, + unsigned long block, + int count, + void *data, + size_t size, + int actual_bytes_read, + errcode_t error); + errcode_t (*write_error)(io_channel channel, + unsigned long block, + int count, + const void *data, + size_t size, + int actual_bytes_written, + errcode_t error); + int refcount; + int flags; + long reserved[14]; + void *private_data; + void *app_data; +}; + +struct struct_io_stats { + int num_fields; + int reserved; + unsigned long long bytes_read; + unsigned long long bytes_written; +}; + +struct struct_io_manager { + errcode_t magic; + const char *name; + errcode_t (*open)(const char *name, int flags, io_channel *channel); + errcode_t (*close)(io_channel channel); + errcode_t (*set_blksize)(io_channel channel, int blksize); + errcode_t (*read_blk)(io_channel channel, unsigned long block, + int count, void *data); + errcode_t (*write_blk)(io_channel channel, unsigned long block, + int count, const void *data); + errcode_t (*flush)(io_channel channel); + errcode_t (*write_byte)(io_channel channel, unsigned long offset, + int count, const void *data); + errcode_t (*set_option)(io_channel channel, const char *option, + const char *arg); + errcode_t (*get_stats)(io_channel channel, io_stats *io_stats); + errcode_t (*read_blk64)(io_channel channel, unsigned long long block, + int count, void *data); + errcode_t (*write_blk64)(io_channel channel, unsigned long long block, + int count, const void *data); + errcode_t (*discard)(io_channel channel, unsigned long long block, + unsigned long long count); + long reserved[16]; +}; + +#define IO_FLAG_RW 0x0001 +#define IO_FLAG_EXCLUSIVE 0x0002 +#define IO_FLAG_DIRECT_IO 0x0004 + +/* + * Convenience functions.... + */ +#define io_channel_close(c) ((c)->manager->close((c))) +#define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) +#define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) +#define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) +#define io_channel_flush(c) ((c)->manager->flush((c))) +#define io_channel_bumpcount(c) ((c)->refcount++) + +/* io_manager.c */ +extern errcode_t io_channel_set_options(io_channel channel, + const char *options); +extern errcode_t io_channel_write_byte(io_channel channel, + unsigned long offset, + int count, const void *data); +extern errcode_t io_channel_read_blk64(io_channel channel, + unsigned long long block, + int count, void *data); +extern errcode_t io_channel_write_blk64(io_channel channel, + unsigned long long block, + int count, const void *data); +extern errcode_t io_channel_discard(io_channel channel, + unsigned long long block, + unsigned long long count); + +/* unix_io.c */ +extern io_manager unix_io_manager; + +/* undo_io.c */ +extern io_manager undo_io_manager; +extern errcode_t set_undo_io_backing_manager(io_manager manager); +extern errcode_t set_undo_io_backup_file(char *file_name); + +/* test_io.c */ +extern io_manager test_io_manager, test_io_backing_manager; +extern void (*test_io_cb_read_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_write_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_read_blk64) + (unsigned long long block, int count, errcode_t err); +extern void (*test_io_cb_write_blk64) + (unsigned long long block, int count, errcode_t err); +extern void (*test_io_cb_set_blksize) + (int blksize, errcode_t err); + +#endif /* _EXT2FS_EXT2_IO_H */ + diff --git a/lib/libext2fs/orig/ext2_types.h b/lib/libext2fs/orig/ext2_types.h new file mode 100644 index 0000000..5f5f7a5 --- /dev/null +++ b/lib/libext2fs/orig/ext2_types.h @@ -0,0 +1,18 @@ +/* + * If linux/types.h is already been included, assume it has defined + * everything we need. (cross fingers) Other header files may have + * also defined the types that we need. + */ +#ifndef _EXT2_TYPES_H +#define _EXT2_TYPES_H + +typedef unsigned char __u8; +typedef signed char __s8; +typedef unsigned short __u16; +typedef short __s16; +typedef unsigned int __u32; +typedef int __s32; +typedef unsigned long long __u64; +typedef signed long long __s64; + +#endif /* _EXT2_TYPES_H */ diff --git a/lib/libext2fs/orig/ext2dir.c b/lib/libext2fs/orig/ext2dir.c new file mode 100644 index 0000000..ee7ce3c --- /dev/null +++ b/lib/libext2fs/orig/ext2dir.c @@ -0,0 +1,657 @@ +/** + * ext2_dir.c - devoptab directory routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "ext2_internal.h" +#include "ext2dir.h" +#include + +#define STATE(x) ((ext2_dir_state*)(x)->dirStruct) + +void ext2CloseDir (ext2_dir_state *dir) +{ + // Sanity check + if (!dir || !dir->vd) + return; + + // Free the directory entries (if any) + while (dir->first) { + ext2_dir_entry *next = dir->first->next; + mem_free(dir->first->name); + mem_free(dir->first); + dir->first = next; + } + + // Close the directory (if open) + if (dir->ni) + ext2CloseEntry(dir->vd, dir->ni); + + // Reset the directory state + dir->ni = NULL; + dir->first = NULL; + dir->current = NULL; + + return; +} + +int ext2_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + // Short circuit cases were we don't actually have to do anything + if (!st || !path) + return 0; + + ext2_log_trace("path %s, st %p\n", path, st); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFDIR; + return 0; + } + + // Lock + ext2Lock(vd); + + // Find the entry + ni = ext2OpenEntry(vd, path); + if (!ni) { + r->_errno = errno; + ext2Unlock(vd); + return -1; + } + + // Get the entry stats + int ret = ext2Stat(vd, ni, st); + if (ret) + r->_errno = errno; + + // Close the entry + ext2CloseEntry(vd, ni); + + ext2Unlock(vd); + + return 0; +} + +int ext2_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + ext2_log_trace("existing %s, newLink %s\n", existing, newLink); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(existing); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Create a symbolic link between the two paths + ni = ext2Create(vd, existing, S_IFLNK, newLink); + if (!ni) { + ext2Unlock(vd); + r->_errno = errno; + return -1; + } + + // Close the symbolic link + ext2CloseEntry(vd, ni); + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_unlink_r (struct _reent *r, const char *name) +{ + ext2_log_trace("name %s\n", name); + + ext2_vd *vd = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Unlink the entry + int ret = ext2Unlink(vd, name); + if (ret) + r->_errno = errno; + + return ret; +} + +int ext2_chdir_r (struct _reent *r, const char *name) +{ + ext2_log_trace("name %s\n", name); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(name); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Find the directory + ni = ext2OpenEntry(vd, name); + if (!ni) { + ext2Unlock(vd); + r->_errno = ENOENT; + return -1; + } + + // Ensure that this directory is indeed a directory + if (!LINUX_S_ISDIR(ni->ni.i_mode)) { + ext2CloseEntry(vd, ni); + ext2Unlock(vd); + r->_errno = ENOTDIR; + return -1; + } + + // Close the old current directory (if any) + if (vd->cwd_ni) + ext2CloseEntry(vd, vd->cwd_ni); + + // Set the new current directory + vd->cwd_ni = ni; + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + ext2_log_trace("oldName %s, newName %s\n", oldName, newName); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(oldName); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // You cannot rename between devices + if(vd != ext2GetVolume(newName)) { + ext2Unlock(vd); + r->_errno = EXDEV; + return -1; + } + + // Check that there is no existing entry with the new name + ni = ext2OpenEntry(vd, newName); + if (ni) { + ext2CloseEntry(vd, ni); + ext2Unlock(vd); + r->_errno = EEXIST; + return -1; + } + + // Link the old entry with the new one + if (ext2Link(vd, oldName, newName)) { + ext2Unlock(vd); + return -1; + } + + // Unlink the old entry + if (ext2Unlink(vd, oldName)) { + if (ext2Unlink(vd, newName)) { + ext2Unlock(vd); + return -1; + } + ext2Unlock(vd); + return -1; + } + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_mkdir_r (struct _reent *r, const char *path, int mode) +{ + ext2_log_trace("path %s, mode %i\n", path, mode); + + ext2_vd *vd = NULL; + ext2_inode_t *ni = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(vd); + + // Create the directory + ni = ext2Create(vd, path, S_IFDIR, NULL); + if (!ni) { + ext2Unlock(vd); + r->_errno = errno; + return -1; + } + + // Close the directory + ext2CloseEntry(vd, ni); + + // Unlock + ext2Unlock(vd); + + return 0; +} + +int ext2_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + ext2_log_trace("path %s, buf %p\n", path, buf); + + ext2_vd *vd = NULL; + + // Get the volume descriptor for this path + vd = ext2GetVolume(path); + if (!vd) { + r->_errno = ENODEV; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!buf) + return 0; + + // Lock + ext2Lock(vd); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + // File system block size + switch(vd->fs->super->s_log_block_size) + { + case 1: + buf->f_bsize = 2048; + break; + case 2: + buf->f_bsize = 4096; + break; + case 3: + buf->f_bsize = 8192; + break; + default: + case 0: + buf->f_bsize = 1024; + break; + } + + // Fundamental file system block size + buf->f_frsize = buf->f_bsize; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = vd->fs->super->s_blocks_count | (((u64) vd->fs->super->s_blocks_count_hi) << 32); + + // Free blocks available for all and for non-privileged processes + buf->f_bfree = vd->fs->super->s_free_blocks_count | (((u64) vd->fs->super->s_free_blocks_hi) << 32); + + // Number of inodes at this point in time + buf->f_files = vd->fs->super->s_inodes_count; + + // Free inodes available for all and for non-privileged processes + buf->f_ffree = vd->fs->super->s_free_inodes_count; + + // File system id + buf->f_fsid = vd->fs->super->s_magic; + + // Bit mask of f_flag values. + buf->f_flag = vd->fs->super->s_flags; + + // Maximum length of filenames + buf->f_namemax = EXT2_NAME_LEN; + + // Unlock + ext2Unlock(vd); + + return 0; +} + +/** + * PRIVATE: Callback for directory walking + */ +static int DirIterateCallback(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *dirState) +{ + // Sanity check + if(!dirent) + { + errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(((DIR_ITER *) dirState)); + + // Sanity check + if (!dir || !dir->vd) { + errno = EINVAL; + return -1; + } + + //skip ".." on root directory + if(dir->ni->ino == dir->vd->root && strcmp(dirent->name, "..") == 0) + { + return EXT2_ET_OK; + } + + // Allocate a new directory entry + ext2_dir_entry *entry = (ext2_dir_entry *) mem_alloc(sizeof(ext2_dir_entry)); + if (!entry) + { + errno = ENOMEM; + return -1; + } + + memset(entry, 0, sizeof(ext2_dir_entry)); + + int stringlen = dirent->name_len & 0xFF; + + entry->name = mem_alloc(stringlen+1); + if(!entry->name) + { + mem_free(entry); + errno = ENOMEM; + return -1; + } + + // The null termination is not necessarily there in the fs, we gotta do it + int i; + for(i = 0; i < stringlen; ++i) + entry->name[i] = dirent->name[i]; + entry->name[i] = '\0'; + + // Link the entry to the directory + if (!dir->first) { + dir->first = entry; + dir->length = dirent->rec_len; + } else { + ext2_dir_entry *last = dir->first; + while (last->next) last = last->next; + last->next = entry; + } + + return EXT2_ET_OK; +} + +DIR_ITER *ext2_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + ext2_log_trace("dirState %p, path %s\n", dirState, path); + + if(!dirState) + { + r->_errno = EINVAL; + return NULL; + } + + ext2_dir_state* dir = STATE(dirState); + + if(!dir) + { + r->_errno = EINVAL; + return NULL; + } + + // Get the volume descriptor for this path + dir->vd = ext2GetVolume(path); + if (!dir->vd) { + r->_errno = ENODEV; + return NULL; + } + + // Lock + ext2Lock(dir->vd); + + // Find the directory + dir->ni = ext2OpenEntry(dir->vd, path); + if (!dir->ni) { + ext2Unlock(dir->vd); + r->_errno = ENOENT; + return NULL; + } + + // Ensure that this directory is indeed a directory + if (!LINUX_S_ISDIR(dir->ni->ni.i_mode)) { + ext2CloseEntry(dir->vd, dir->ni); + ext2Unlock(dir->vd); + r->_errno = ENOTDIR; + return NULL; + } + + // Read the directory + dir->first = dir->current = NULL; + if (ext2fs_dir_iterate(dir->vd->fs, dir->ni->ino, 0, 0, DirIterateCallback, dirState) != EXT2_ET_OK) { + ext2CloseDir(dir); + ext2Unlock(dir->vd); + r->_errno = errno; + return NULL; + } + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Insert the directory into the double-linked FILO list of open directories + if (dir->vd->firstOpenDir) { + dir->nextOpenDir = dir->vd->firstOpenDir; + dir->vd->firstOpenDir->prevOpenDir = dir; + } else { + dir->nextOpenDir = NULL; + } + dir->prevOpenDir = NULL; + dir->vd->cwd_ni = dir->ni; + dir->vd->firstOpenDir = dir; + dir->vd->openDirCount++; + + // Unlock + ext2Unlock(dir->vd); + + return dirState; +} + +int ext2_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + ext2_log_trace("dirState %p\n", dirState); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Move to the first entry in the directory + dir->current = dir->first; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} + +int ext2_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) +{ + ext2_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + ext2_inode_t *ni = NULL; + + // Sanity check + if (!dir || !dir->vd || !dir->ni) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Check that there is a entry waiting to be fetched + if (!dir->current) { + ext2Unlock(dir->vd); + r->_errno = ENOENT; + return -1; + } + + // Fetch the current entry + strcpy(filename, dir->current->name); + if(filestat != NULL) + { + if(strcmp(dir->current->name, ".") == 0 || strcmp(dir->current->name, "..") == 0) + { + memset(filestat, 0, sizeof(struct stat)); + filestat->st_mode = S_IFDIR; + } + else + { + ni = ext2OpenEntry(dir->vd, dir->current->name); + if (ni) { + ext2Stat(dir->vd, ni, filestat); + ext2CloseEntry(dir->vd, ni); + } + } + } + + // Move to the next entry in the directory + dir->current = dir->current->next; + + // Update directory times + ext2UpdateTimes(dir->vd, dir->ni, EXT2_UPDATE_ATIME); + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} + +int ext2_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + ext2_log_trace("dirState %p\n", dirState); + + if(!dirState) + { + r->_errno = EINVAL; + return -1; + } + + ext2_dir_state* dir = STATE(dirState); + + // Sanity check + if (!dir || !dir->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(dir->vd); + + // Close the directory + ext2CloseDir(dir); + + // Remove the directory from the double-linked FILO list of open directories + dir->vd->openDirCount--; + if (dir->nextOpenDir) + dir->nextOpenDir->prevOpenDir = dir->prevOpenDir; + if (dir->prevOpenDir) + dir->prevOpenDir->nextOpenDir = dir->nextOpenDir; + else + dir->vd->firstOpenDir = dir->nextOpenDir; + + // Unlock + ext2Unlock(dir->vd); + + return 0; +} diff --git a/lib/libext2fs/orig/ext2dir.h b/lib/libext2fs/orig/ext2dir.h new file mode 100644 index 0000000..26081bb --- /dev/null +++ b/lib/libext2fs/orig/ext2dir.h @@ -0,0 +1,69 @@ +/** + * ext2_dir.h - devoptab directory routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _EXT2DIR_H +#define _EXT2DIR_H + +#include +#include "ext2_internal.h" + +/** + * ext2_dir_entry - Directory entry + */ +typedef struct _ext2_dir_entry { + char *name; + struct _ext2_dir_entry *next; +} ext2_dir_entry; + +/** + * ext2_dir_state - Directory state + */ +typedef struct _ext2_dir_state { + ext2_vd *vd; /* Volume this directory belongs to */ + ext2_inode_t *ni; /* Directory descriptor */ + unsigned short length; /* Directory length */ + ext2_dir_entry *first; /* The first entry in the directory */ + ext2_dir_entry *current; /* The current entry in the directory */ + struct _ext2_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */ + struct _ext2_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */ +} ext2_dir_state; + +/* Directory state routines */ +void ext2CloseDir (ext2_dir_state *file); + +/* Gekko devoptab directory routines for EXT2-based devices */ +extern int ext2_stat_r (struct _reent *r, const char *path, struct stat *st); +extern int ext2_link_r (struct _reent *r, const char *existing, const char *newLink); +extern int ext2_unlink_r (struct _reent *r, const char *name); +extern int ext2_chdir_r (struct _reent *r, const char *name); +extern int ext2_rename_r (struct _reent *r, const char *oldName, const char *newName); +extern int ext2_mkdir_r (struct _reent *r, const char *path, int mode); +extern int ext2_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* Gekko devoptab directory walking routines for EXT2-based devices */ +extern DIR_ITER *ext2_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path); +extern int ext2_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int ext2_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int ext2_dirclose_r (struct _reent *r, DIR_ITER *dirState); + +#endif /* _EXT2DIR_H */ + diff --git a/lib/libext2fs/orig/ext2file.c b/lib/libext2fs/orig/ext2file.c new file mode 100644 index 0000000..6647466 --- /dev/null +++ b/lib/libext2fs/orig/ext2file.c @@ -0,0 +1,413 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "ext2_internal.h" +#include "ext2file.h" + +#define STATE(x) ((ext2_file_state*)x) + +void ext2CloseFile (ext2_file_state *file) +{ + // Sanity check + if (!file || !file->vd) + return; + + ext2fs_file_close(file->fd); + + // Sync the file (and its attributes) to disc + if(file->write) + { + // Read in node changes before writing them + ext2fs_read_inode(file->vd->fs, file->ni->ino, &file->ni->ni); + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ACTIME); + } + + if (file->read) + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ATIME); + + ext2Sync(file->vd, file->ni); + + // Close the file (if open) + if (file->ni) + ext2CloseEntry(file->vd, file->ni); + + // Reset the file state + file->ni = NULL; + file->fd = NULL; + file->flags = 0; + file->read = false; + file->write = false; + file->append = false; + + return; +} + +int ext2_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + ext2_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode); + + ext2_file_state* file = STATE(fileStruct); + + // Get the volume descriptor for this path + file->vd = ext2GetVolume(path); + if (!file->vd) { + r->_errno = ENODEV; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Determine which mode the file is opened for + file->flags = flags; + if ((flags & 0x03) == O_RDONLY) { + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = false; + file->write = true; + file->append = (flags & O_APPEND); + } else if ((flags & 0x03) == O_RDWR) { + file->read = true; + file->write = true; + file->append = (flags & O_APPEND); + } else { + r->_errno = EACCES; + ext2Unlock(file->vd); + return -1; + } + + // Try and find the file and (if found) ensure that it is not a directory + file->ni = ext2OpenEntry(file->vd, path); + if (file->ni && LINUX_S_ISDIR(file->ni->ni.i_mode)) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = EISDIR; + return -1; + } + + // Are we creating this file? + if ((flags & O_CREAT) && !file->ni) + // Create the file + file->ni = ext2Create(file->vd, path, S_IFREG, NULL); + + // Sanity check, the file should be open by now + if (!file->ni) { + ext2Unlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + // Make sure we aren't trying to write to a read-only file + if (!(file->vd->fs->flags & EXT2_FLAG_RW) && file->write) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = EROFS; + return -1; + } + + errcode_t err = ext2fs_file_open2(file->vd->fs, file->ni->ino, &file->ni->ni, + file->write ? EXT2_FLAG_RW : 0, &file->fd); + if(err != 0) + { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = ENOENT; + return -1; + } + + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write) { + if (ext2fs_file_set_size2(file->fd, 0) != 0) { + ext2CloseEntry(file->vd, file->ni); + ext2Unlock(file->vd); + r->_errno = errno; + return -1; + } + file->ni->ni.i_size = file->ni->ni.i_size_high = 0; + } + + // Set the files current position + ext2fs_file_llseek(file->fd, file->append ? EXT2_I_SIZE(&file->ni->ni) : 0, SEEK_SET, 0); + + ext2_log_trace("file->len %lld\n", EXT2_I_SIZE(&file->ni->ni)); + + // Update file times + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_ATIME); + + // Insert the file into the double-linked FILO list of open files + if (file->vd->firstOpenFile) { + file->nextOpenFile = file->vd->firstOpenFile; + file->vd->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + file->vd->firstOpenFile = file; + file->vd->openFileCount++; + + // Sync access time + ext2Sync(file->vd, file->ni); + + // Unlock + ext2Unlock(file->vd); + + return (int)fileStruct; +} + +int ext2_close_r (struct _reent *r, int fd) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd) { + r->_errno = EBADF; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Close the file + ext2CloseFile(file); + + // Remove the file from the double-linked FILO list of open files + file->vd->openFileCount--; + if (file->nextOpenFile) + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + if (file->prevOpenFile) + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + else + file->vd->firstOpenFile = file->nextOpenFile; + + // Unlock + ext2Unlock(file->vd); + + return 0; +} + +ssize_t ext2_write_r (struct _reent *r, int fd, const char *ptr, size_t len) +{ + ext2_log_trace("fd %p, ptr %p, len %i\n", (void *) fd, ptr, len); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Check that we are allowed to write to this file + if (!file->write) { + r->_errno = EACCES; + return -1; + } + + // Lock + ext2Lock(file->vd); + + u32 writen = 0; + + // Write to the files data atrribute + errcode_t err = ext2fs_file_write(file->fd, ptr, len, &writen); + if (writen <= 0 || err) { + ext2Unlock(file->vd); + r->_errno = errno; + return (err ? err : -1); + } + + // Unlock + ext2Unlock(file->vd); + + return (writen == 0 ? -1 : writen); +} + +ssize_t ext2_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + ext2_log_trace("fd %p, ptr %p, len %i\n", (void *) fd, ptr, len); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->vd || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases where we don't actually have to do anything + if (!ptr || len <= 0) { + return 0; + } + + // Lock + ext2Lock(file->vd); + + // Check that we are allowed to read from this file + if (!file->read) { + ext2Unlock(file->vd); + r->_errno = EACCES; + return -1; + } + + u32 read = 0; + errcode_t err = 0; + + // Read from the files data attribute + err = ext2fs_file_read(file->fd, ptr, len, &read); + if (err || read <= 0 || read > len) { + ext2Unlock(file->vd); + r->_errno = errno; + return err ? err : -1; + } + + // Unlock + ext2Unlock(file->vd); + + return (read == 0) ? -1 : read; +} + +off_t ext2_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + ext2_log_trace("fd %p, pos %lli, dir %i\n", (void *) fd, pos, dir); + + ext2_file_state* file = STATE(fd); + + // Sanity check + if (!file || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + u64 pos_loaded = 0; + + ext2fs_file_llseek(file->fd, pos, dir, &pos_loaded); + + return (off_t) pos_loaded; +} + +int ext2_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Short circuit cases were we don't actually have to do anything + if (!st) + return 0; + + // Get the file stats + ret = ext2Stat(file->vd, file->ni, st); + if (ret) + r->_errno = errno; + + return ret; +} + +int ext2_ftruncate_r (struct _reent *r, int fd, off_t len) +{ + ext2_log_trace("fd %p, len %Li\n", (void *) fd, len); + + ext2_file_state* file = STATE(fd); + errcode_t err = 0; + + // Sanity check + if (!file || !file->vd || !file->ni || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Check that we are allowed to write to this file + if (!file->write) { + ext2Unlock(file->vd); + r->_errno = EACCES; + return -1; + } + + err = ext2fs_file_set_size2(file->fd, len); + + // Sync the file (and its attributes) to disc + if(!err) + ext2Sync(file->vd, file->ni); + + // update times + ext2UpdateTimes(file->vd, file->ni, EXT2_UPDATE_AMTIME); + + // Unlock + ext2Unlock(file->vd); + + return err; +} + +int ext2_fsync_r (struct _reent *r, int fd) +{ + ext2_log_trace("fd %p\n", (void *) fd); + + ext2_file_state* file = STATE(fd); + int ret = 0; + + // Sanity check + if (!file || !file->fd) { + r->_errno = EINVAL; + return -1; + } + + // Lock + ext2Lock(file->vd); + + // Sync the file (and its attributes) to disc + ret = ext2fs_file_flush(file->fd); + if (ret) + r->_errno = ret; + + // Unlock + ext2Unlock(file->vd); + + return ret; +} diff --git a/lib/libext2fs/orig/ext2file.h b/lib/libext2fs/orig/ext2file.h new file mode 100644 index 0000000..249ba52 --- /dev/null +++ b/lib/libext2fs/orig/ext2file.h @@ -0,0 +1,60 @@ +/** + * ext2file.c - devoptab file routines for EXT2-based devices. + * + * Copyright (c) 2006 Michael "Chishm" Chisholm + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _EXT2FILE_H +#define _EXT2FILE_H + +#include +#include "ext2_internal.h" + +/** + * ext2_file_state - File state + */ +typedef struct _ext2_file_state { + ext2_vd *vd; /* Volume this file belongs to */ + ext2_inode_t *ni; /* File inode */ + ext2_file_t fd; /* File descriptor */ + int flags; /* Opening flags */ + bool read; /* True if allowed to read from file */ + bool write; /* True if allowed to write to file */ + bool append; /* True if allowed to append to file */ +// bool compressed; /* True if file data is compressed */ +// bool encrypted; /* True if file data is encryted */ + struct _ext2_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _ext2_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} ext2_file_state; + +/* File state routines */ +void ext2CloseFile (ext2_file_state *file); + +/* Gekko devoptab file routines for EXT2-based devices */ +extern int ext2_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); +extern int ext2_close_r (struct _reent *r, int fd); +extern ssize_t ext2_write_r (struct _reent *r, int fd, const char *ptr, size_t len); +extern ssize_t ext2_read_r (struct _reent *r, int fd, char *ptr, size_t len); +extern off_t ext2_seek_r (struct _reent *r, int fd, off_t pos, int dir); +extern int ext2_fstat_r (struct _reent *r, int fd, struct stat *st); +extern int ext2_ftruncate_r (struct _reent *r, int fd, off_t len); +extern int ext2_fsync_r (struct _reent *r, int fd); + +#endif /* _EXT2FILE_H */ + diff --git a/lib/libext2fs/orig/ext2fs.h b/lib/libext2fs/orig/ext2fs.h new file mode 100644 index 0000000..7016486 --- /dev/null +++ b/lib/libext2fs/orig/ext2fs.h @@ -0,0 +1,1572 @@ +/* + * ext2fs.h --- ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _EXT2FS_EXT2FS_H +#define _EXT2FS_EXT2FS_H + +#ifdef __GNUC__ +#define EXT2FS_ATTR(x) __attribute__(x) +#else +#define EXT2FS_ATTR(x) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Non-GNU C compilers won't necessarily understand inline + */ +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define NO_INLINE_FUNCS +#endif + +/* + * Where the master copy of the superblock is located, and how big + * superblocks are supposed to be. We define SUPERBLOCK_SIZE because + * the size of the superblock structure is not necessarily trustworthy + * (some versions have the padding set up so that the superblock is + * 1032 bytes long). + */ +#define SUPERBLOCK_OFFSET 1024 +#define SUPERBLOCK_SIZE 1024 + +/* + * The last ext2fs revision level that this version of the library is + * able to support. + */ +#define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include + +#include "ext2_types.h" +#include "com_err.h" +#include "ext2_fs.h" +#include "ext3_extents.h" + +typedef __u32 ext2_ino_t; +typedef __u32 blk_t; +typedef __u64 blk64_t; +typedef __u32 dgrp_t; +typedef __u32 ext2_off_t; +typedef __u64 ext2_off64_t; +typedef __s64 e2_blkcnt_t; +typedef __u32 ext2_dirhash_t; + +#include "ext2_io.h" +#include "ext2_err.h" +#include "ext2_ext_attr.h" + +/* + * Portability help for Microsoft Visual C++ + */ +#ifdef _MSC_VER +#define EXT2_QSORT_TYPE int __cdecl +#else +#define EXT2_QSORT_TYPE int +#endif + +typedef struct struct_ext2_filsys *ext2_filsys; + +#define EXT2FS_MARK_ERROR 0 +#define EXT2FS_UNMARK_ERROR 1 +#define EXT2FS_TEST_ERROR 2 + +typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; + +#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) + + +/* + * Badblocks list definitions + */ + +typedef struct ext2_struct_u32_list *ext2_badblocks_list; +typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; + +typedef struct ext2_struct_u32_list *ext2_u32_list; +typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; + +/* old */ +typedef struct ext2_struct_u32_list *badblocks_list; +typedef struct ext2_struct_u32_iterate *badblocks_iterate; + +#define BADBLOCKS_FLAG_DIRTY 1 + +/* + * ext2_dblist structure and abstractions (see dblist.c) + */ +struct ext2_db_entry2 { + ext2_ino_t ino; + blk64_t blk; + e2_blkcnt_t blockcnt; +}; + +/* Ye Olde 32-bit version */ +struct ext2_db_entry { + ext2_ino_t ino; + blk_t blk; + int blockcnt; +}; + +typedef struct ext2_struct_dblist *ext2_dblist; + +#define DBLIST_ABORT 1 + +/* + * ext2_fileio definitions + */ + +#define EXT2_FILE_WRITE 0x0001 +#define EXT2_FILE_CREATE 0x0002 + +#define EXT2_FILE_MASK 0x00FF + +#define EXT2_FILE_BUF_DIRTY 0x4000 +#define EXT2_FILE_BUF_VALID 0x2000 + +typedef struct ext2_file *ext2_file_t; + +#define EXT2_SEEK_SET 0 +#define EXT2_SEEK_CUR 1 +#define EXT2_SEEK_END 2 + +/* + * Flags for the ext2_filsys structure and for ext2fs_open() + */ +#define EXT2_FLAG_RW 0x01 +#define EXT2_FLAG_CHANGED 0x02 +#define EXT2_FLAG_DIRTY 0x04 +#define EXT2_FLAG_VALID 0x08 +#define EXT2_FLAG_IB_DIRTY 0x10 +#define EXT2_FLAG_BB_DIRTY 0x20 +#define EXT2_FLAG_SWAP_BYTES 0x40 +#define EXT2_FLAG_SWAP_BYTES_READ 0x80 +#define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 +#define EXT2_FLAG_MASTER_SB_ONLY 0x200 +#define EXT2_FLAG_FORCE 0x400 +#define EXT2_FLAG_SUPER_ONLY 0x800 +#define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 +#define EXT2_FLAG_IMAGE_FILE 0x2000 +#define EXT2_FLAG_EXCLUSIVE 0x4000 +#define EXT2_FLAG_SOFTSUPP_FEATURES 0x8000 +#define EXT2_FLAG_NOFREE_ON_ERROR 0x10000 +#define EXT2_FLAG_64BITS 0x20000 +#define EXT2_FLAG_PRINT_PROGRESS 0x40000 +#define EXT2_FLAG_DIRECT_IO 0x80000 + +/* + * Special flag in the ext2 inode i_flag field that means that this is + * a new inode. (So that ext2_write_inode() can clear extra fields.) + */ +#define EXT2_NEW_INODE_FL 0x80000000 + +/* + * Flags for mkjournal + * + * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock + */ +#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 + +struct opaque_ext2_group_desc; + +struct struct_ext2_filsys { + errcode_t magic; + io_channel io; + int flags; + char * device_name; + struct ext2_super_block * super; + unsigned int blocksize; + int clustersize; + dgrp_t group_desc_count; + unsigned long desc_blocks; + struct opaque_ext2_group_desc * group_desc; + int inode_blocks_per_group; + ext2fs_inode_bitmap inode_map; + ext2fs_block_bitmap block_map; + /* XXX FIXME-64: not 64-bit safe, but not used? */ + errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); + errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); + errcode_t (*write_bitmaps)(ext2_filsys fs); + errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + ext2_badblocks_list badblocks; + ext2_dblist dblist; + __u32 stride; /* for mke2fs */ + struct ext2_super_block * orig_super; + struct ext2_image_hdr * image_header; + __u32 umask; + time_t now; + /* + * Reserved for future expansion + */ + __u32 reserved[7]; + + /* + * Reserved for the use of the calling application. + */ + void * priv_data; + + /* + * Inode cache + */ + struct ext2_inode_cache *icache; + io_channel image_io; + + /* + * More callback functions + */ + errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal, + blk64_t *ret); + void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse); +}; + +#include "bitops.h" + +/* + * Return flags for the block iterator functions + */ +#define BLOCK_CHANGED 1 +#define BLOCK_ABORT 2 +#define BLOCK_ERROR 4 + +/* + * Block interate flags + * + * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator + * function should be called on blocks where the block number is zero. + * This is used by ext2fs_expand_dir() to be able to add a new block + * to an inode. It can also be used for programs that want to be able + * to deal with files that contain "holes". + * + * BLOCK_FLAG_DEPTH_TRAVERSE indicates that the iterator function for + * the indirect, doubly indirect, etc. blocks should be called after + * all of the blocks containined in the indirect blocks are processed. + * This is useful if you are going to be deallocating blocks from an + * inode. + * + * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be + * called for data blocks only. + * + * BLOCK_FLAG_READ_ONLY is a promise by the caller that it will not + * modify returned block number. + * + * BLOCK_FLAG_NO_LARGE is for internal use only. It informs + * ext2fs_block_iterate2 that large files won't be accepted. + */ +#define BLOCK_FLAG_APPEND 1 +#define BLOCK_FLAG_HOLE 1 +#define BLOCK_FLAG_DEPTH_TRAVERSE 2 +#define BLOCK_FLAG_DATA_ONLY 4 +#define BLOCK_FLAG_READ_ONLY 8 + +#define BLOCK_FLAG_NO_LARGE 0x1000 + +/* + * Magic "block count" return values for the block iterator function. + */ +#define BLOCK_COUNT_IND (-1) +#define BLOCK_COUNT_DIND (-2) +#define BLOCK_COUNT_TIND (-3) +#define BLOCK_COUNT_TRANSLATOR (-4) + +/* + * Flags for ext2fs_move_blocks + */ +#define EXT2_BMOVE_GET_DBLIST 0x0001 +#define EXT2_BMOVE_DEBUG 0x0002 + +/* + * Generic (non-filesystem layout specific) extents structure + */ + +#define EXT2_EXTENT_FLAGS_LEAF 0x0001 +#define EXT2_EXTENT_FLAGS_UNINIT 0x0002 +#define EXT2_EXTENT_FLAGS_SECOND_VISIT 0x0004 + +struct ext2fs_extent { + blk64_t e_pblk; /* first physical block */ + blk64_t e_lblk; /* first logical block extent covers */ + __u32 e_len; /* number of blocks covered by extent */ + __u32 e_flags; /* extent flags */ +}; + +typedef struct ext2_extent_handle *ext2_extent_handle_t; +typedef struct ext2_extent_path *ext2_extent_path_t; + +/* + * Flags used by ext2fs_extent_get() + */ +#define EXT2_EXTENT_CURRENT 0x0000 +#define EXT2_EXTENT_MOVE_MASK 0x000F +#define EXT2_EXTENT_ROOT 0x0001 +#define EXT2_EXTENT_LAST_LEAF 0x0002 +#define EXT2_EXTENT_FIRST_SIB 0x0003 +#define EXT2_EXTENT_LAST_SIB 0x0004 +#define EXT2_EXTENT_NEXT_SIB 0x0005 +#define EXT2_EXTENT_PREV_SIB 0x0006 +#define EXT2_EXTENT_NEXT_LEAF 0x0007 +#define EXT2_EXTENT_PREV_LEAF 0x0008 +#define EXT2_EXTENT_NEXT 0x0009 +#define EXT2_EXTENT_PREV 0x000A +#define EXT2_EXTENT_UP 0x000B +#define EXT2_EXTENT_DOWN 0x000C +#define EXT2_EXTENT_DOWN_AND_LAST 0x000D + +/* + * Flags used by ext2fs_extent_insert() + */ +#define EXT2_EXTENT_INSERT_AFTER 0x0001 /* insert after handle loc'n */ +#define EXT2_EXTENT_INSERT_NOSPLIT 0x0002 /* insert may not cause split */ + +/* + * Flags used by ext2fs_extent_delete() + */ +#define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extnt gone */ + +/* + * Flags used by ext2fs_extent_set_bmap() + */ +#define EXT2_EXTENT_SET_BMAP_UNINIT 0x0001 + +/* + * Data structure returned by ext2fs_extent_get_info() + */ +struct ext2_extent_info { + int curr_entry; + int curr_level; + int num_entries; + int max_entries; + int max_depth; + int bytes_avail; + blk64_t max_lblk; + blk64_t max_pblk; + __u32 max_len; + __u32 max_uninit_len; +}; + +/* + * Flags for directory block reading and writing functions + */ +#define EXT2_DIRBLOCK_V2_STRUCT 0x0001 + +/* + * Return flags for the directory iterator functions + */ +#define DIRENT_CHANGED 1 +#define DIRENT_ABORT 2 +#define DIRENT_ERROR 3 + +/* + * Directory iterator flags + */ + +#define DIRENT_FLAG_INCLUDE_EMPTY 1 +#define DIRENT_FLAG_INCLUDE_REMOVED 2 + +#define DIRENT_DOT_FILE 1 +#define DIRENT_DOT_DOT_FILE 2 +#define DIRENT_OTHER_FILE 3 +#define DIRENT_DELETED_FILE 4 + +/* + * Inode scan definitions + */ +typedef struct ext2_struct_inode_scan *ext2_inode_scan; + +/* + * ext2fs_scan flags + */ +#define EXT2_SF_CHK_BADBLOCKS 0x0001 +#define EXT2_SF_BAD_INODE_BLK 0x0002 +#define EXT2_SF_BAD_EXTRA_BYTES 0x0004 +#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 +#define EXT2_SF_DO_LAZY 0x0010 + +/* + * ext2fs_check_if_mounted flags + */ +#define EXT2_MF_MOUNTED 1 +#define EXT2_MF_ISROOT 2 +#define EXT2_MF_READONLY 4 +#define EXT2_MF_SWAP 8 +#define EXT2_MF_BUSY 16 + +/* + * Ext2/linux mode flags. We define them here so that we don't need + * to depend on the OS's sys/stat.h, since we may be compiling on a + * non-Linux system. + */ +#define LINUX_S_IFMT 00170000 +#define LINUX_S_IFSOCK 0140000 +#define LINUX_S_IFLNK 0120000 +#define LINUX_S_IFREG 0100000 +#define LINUX_S_IFBLK 0060000 +#define LINUX_S_IFDIR 0040000 +#define LINUX_S_IFCHR 0020000 +#define LINUX_S_IFIFO 0010000 +#define LINUX_S_ISUID 0004000 +#define LINUX_S_ISGID 0002000 +#define LINUX_S_ISVTX 0001000 + +#define LINUX_S_IRWXU 00700 +#define LINUX_S_IRUSR 00400 +#define LINUX_S_IWUSR 00200 +#define LINUX_S_IXUSR 00100 + +#define LINUX_S_IRWXG 00070 +#define LINUX_S_IRGRP 00040 +#define LINUX_S_IWGRP 00020 +#define LINUX_S_IXGRP 00010 + +#define LINUX_S_IRWXO 00007 +#define LINUX_S_IROTH 00004 +#define LINUX_S_IWOTH 00002 +#define LINUX_S_IXOTH 00001 + +#define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) +#define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) +#define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) +#define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) +#define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) +#define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) +#define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) + +/* + * ext2 size of an inode + */ +#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) + +/* + * ext2_icount_t abstraction + */ +#define EXT2_ICOUNT_OPT_INCREMENT 0x01 + +typedef struct ext2_icount *ext2_icount_t; + +/* + * Flags for ext2fs_bmap + */ +#define BMAP_ALLOC 0x0001 +#define BMAP_SET 0x0002 + +/* + * Returned flags from ext2fs_bmap + */ +#define BMAP_RET_UNINIT 0x0001 + +/* + * Flags for imager.c functions + */ +#define IMAGER_FLAG_INODEMAP 1 +#define IMAGER_FLAG_SPARSEWRITE 2 + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + + +/* + * For ext2 compression support + */ +#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) -1) +#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) + +/* + * Features supported by this version of the library + */ +#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ + EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ + EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_RESIZE_INODE|\ + EXT2_FEATURE_COMPAT_DIR_INDEX|\ + EXT2_FEATURE_COMPAT_EXT_ATTR) + +/* This #ifdef is temporary until compression is fully supported */ +#ifdef ENABLE_COMPRESSION +#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL +/* If the below warning bugs you, then have + `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your + environment at configure time. */ + #warning "Compression support is experimental" +#endif +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT2_FEATURE_INCOMPAT_COMPRESSION|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER|\ + EXT3_FEATURE_INCOMPAT_EXTENTS|\ + EXT4_FEATURE_INCOMPAT_FLEX_BG|\ + EXT4_FEATURE_INCOMPAT_64BIT) +#else +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER|\ + EXT3_FEATURE_INCOMPAT_EXTENTS|\ + EXT4_FEATURE_INCOMPAT_FLEX_BG|\ + EXT4_FEATURE_INCOMPAT_64BIT) +#endif +#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ + EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ + EXT4_FEATURE_RO_COMPAT_GDT_CSUM) + +/* + * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed + * to ext2fs_openfs() + */ +#define EXT2_LIB_SOFTSUPP_INCOMPAT (0) +#define EXT2_LIB_SOFTSUPP_RO_COMPAT (EXT4_FEATURE_RO_COMPAT_BIGALLOC) + +/* + * function prototypes + */ + +/* alloc.c */ +extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, + ext2fs_inode_bitmap map, ext2_ino_t *ret); +extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret); +extern errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, + ext2fs_block_bitmap map, blk64_t *ret); +extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, + blk_t finish, int num, + ext2fs_block_bitmap map, + blk_t *ret); +extern errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, + blk64_t finish, int num, + ext2fs_block_bitmap map, + blk64_t *ret); +extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret); +extern errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, + char *block_buf, blk64_t *ret); +extern void ext2fs_set_alloc_block_callback(ext2_filsys fs, + errcode_t (*func)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret), + errcode_t (**old)(ext2_filsys fs, + blk64_t goal, + blk64_t *ret)); + +/* alloc_sb.c */ +extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap); +extern void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, + void (*func)(ext2_filsys fs, + blk64_t blk, + int inuse), + void (**old)(ext2_filsys fs, + blk64_t blk, + int inuse)); + +/* alloc_stats.c */ +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir); +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); +void ext2fs_block_alloc_stats2(ext2_filsys fs, blk64_t blk, int inuse); + +/* alloc_tables.c */ +extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); +extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap); + +/* badblocks.c */ +extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); +extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); +extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret); +extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); +extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); +extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); +extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); + +extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, + int size); +extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); +extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); +extern errcode_t + ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret); +extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, + blk_t *blk); +extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); +extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest); +extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, + ext2_badblocks_list bb2); +extern int ext2fs_u32_list_count(ext2_u32_list bb); + +/* bb_compat */ +extern errcode_t badblocks_list_create(badblocks_list *ret, int size); +extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); +extern int badblocks_list_test(badblocks_list bb, blk_t blk); +extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret); +extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); +extern void badblocks_list_iterate_end(badblocks_iterate iter); +extern void badblocks_list_free(badblocks_list bb); + +/* bb_inode.c */ +extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, + ext2_badblocks_list bb_list); + +/* bitmaps.c */ +extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); +extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret); +extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret); +extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend); +extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend); +extern errcode_t ext2fs_fudge_block_bitmap_end2(ext2fs_block_bitmap bitmap, + blk64_t end, blk64_t *oend); +extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); +extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); +extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); +extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap); +extern errcode_t ext2fs_resize_inode_bitmap2(__u64 new_end, + __u64 new_real_end, + ext2fs_inode_bitmap bmap); +extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap); +extern errcode_t ext2fs_resize_block_bitmap2(__u64 new_end, + __u64 new_real_end, + ext2fs_block_bitmap bmap); +extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2); +extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2); +extern errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *in); +extern errcode_t ext2fs_set_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *in); +extern errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, + ext2_ino_t start, unsigned int num, + void *out); +extern errcode_t ext2fs_get_inode_bitmap_range2(ext2fs_inode_bitmap bmap, + __u64 start, size_t num, + void *out); +extern errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *in); +extern errcode_t ext2fs_set_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *in); +extern errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, + blk_t start, unsigned int num, + void *out); +extern errcode_t ext2fs_get_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t start, size_t num, + void *out); + +/* blknum.c */ +extern dgrp_t ext2fs_group_of_blk2(ext2_filsys fs, blk64_t); +extern blk64_t ext2fs_group_first_block2(ext2_filsys fs, dgrp_t group); +extern blk64_t ext2fs_group_last_block2(ext2_filsys fs, dgrp_t group); +extern blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs, + struct ext2_inode *inode); +extern blk64_t ext2fs_inode_i_blocks(ext2_filsys fs, + struct ext2_inode *inode); +extern blk64_t ext2fs_blocks_count(struct ext2_super_block *super); +extern void ext2fs_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +extern blk64_t ext2fs_r_blocks_count(struct ext2_super_block *super); +extern void ext2fs_r_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_r_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +extern blk64_t ext2fs_free_blocks_count(struct ext2_super_block *super); +extern void ext2fs_free_blocks_count_set(struct ext2_super_block *super, + blk64_t blk); +extern void ext2fs_free_blocks_count_add(struct ext2_super_block *super, + blk64_t blk); +/* Block group descriptor accessor functions */ +extern struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs, + struct opaque_ext2_group_desc *gdp, + dgrp_t group); +extern blk64_t ext2fs_block_bitmap_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_block_bitmap_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern blk64_t ext2fs_inode_bitmap_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_inode_bitmap_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern blk64_t ext2fs_inode_table_loc(ext2_filsys fs, dgrp_t group); +extern void ext2fs_inode_table_loc_set(ext2_filsys fs, dgrp_t group, + blk64_t blk); +extern __u32 ext2fs_bg_free_blocks_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_free_blocks_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_free_inodes_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_free_inodes_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_used_dirs_count(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_used_dirs_count_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u32 ext2fs_bg_itable_unused(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_itable_unused_set(ext2_filsys fs, dgrp_t group, + __u32 n); +extern __u16 ext2fs_bg_flags(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_flags_zap(ext2_filsys fs, dgrp_t group); +extern int ext2fs_bg_flags_test(ext2_filsys fs, dgrp_t group, __u16 bg_flag); +extern void ext2fs_bg_flags_set(ext2_filsys fs, dgrp_t group, __u16 bg_flags); +extern void ext2fs_bg_flags_clear(ext2_filsys fs, dgrp_t group, __u16 bg_flags); +extern __u16 ext2fs_bg_checksum(ext2_filsys fs, dgrp_t group); +extern void ext2fs_bg_checksum_set(ext2_filsys fs, dgrp_t group, __u16 checksum); +extern blk64_t ext2fs_file_acl_block(const struct ext2_inode *inode); +extern void ext2fs_file_acl_block_set(struct ext2_inode *inode, blk64_t blk); + +/* block.c */ +extern errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data); +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); +errcode_t ext2fs_block_iterate3(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); + +/* bmap.c */ +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); +extern errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk64_t block, + int *ret_flags, blk64_t *phys_blk); + +#if 0 +/* bmove.c */ +extern errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags); +#endif + +/* check_desc.c */ +extern errcode_t ext2fs_check_desc(ext2_filsys fs); + +/* closefs.c */ +extern errcode_t ext2fs_close(ext2_filsys fs); +extern errcode_t ext2fs_flush(ext2_filsys fs); +extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); +extern errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, + dgrp_t group, + blk64_t *ret_super_blk, + blk64_t *ret_old_desc_blk, + blk64_t *ret_new_desc_blk, + blk_t *ret_used_blks); +extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg); +extern void ext2fs_update_dynamic_rev(ext2_filsys fs); + +/* csum.c */ +extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group); +extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group); +extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs); + +/* dblist.c */ + +extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); +extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); +extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt); +extern void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)); +extern void ext2fs_dblist_sort2(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)); +extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_dblist_iterate2(ext2_dblist dblist, + int (*func)(ext2_filsys fs, struct ext2_db_entry2 *db_info, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern errcode_t ext2fs_set_dir_block2(ext2_dblist dblist, ext2_ino_t ino, + blk64_t blk, e2_blkcnt_t blockcnt); +extern errcode_t ext2fs_copy_dblist(ext2_dblist src, + ext2_dblist *dest); +extern int ext2fs_dblist_count(ext2_dblist dblist); +extern blk64_t ext2fs_dblist_count2(ext2_dblist dblist); +extern errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, + struct ext2_db_entry **entry); +extern errcode_t ext2fs_dblist_get_last2(ext2_dblist dblist, + struct ext2_db_entry2 **entry); +extern errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist); + +/* dblist_dir.c */ +extern errcode_t + ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dirblock.c */ +extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); +extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags); +extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); +extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, + void *buf, int flags); + +/* dirhash.c */ +extern errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash); + + +/* dir_iterate.c */ +extern errcode_t ext2fs_get_rec_len(ext2_filsys fs, + struct ext2_dir_entry *dirent, + unsigned int *rec_len); +extern errcode_t ext2fs_set_rec_len(ext2_filsys fs, + unsigned int len, + struct ext2_dir_entry *dirent); +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dupfs.c */ +extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); + +/* expanddir.c */ +extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); + +/* ext_attr.c */ +extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, + void *data); +extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); +extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, + void *buf); +extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, + void *buf); +extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, + int adjust, __u32 *newcount); +extern errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, + int adjust, __u32 *newcount); + +/* extent.c */ +extern errcode_t ext2fs_extent_header_verify(void *ptr, int size); +extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, + ext2_extent_handle_t *handle); +extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + ext2_extent_handle_t *ret_handle); +extern void ext2fs_extent_free(ext2_extent_handle_t handle); +extern errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, + int flags, struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent); +extern errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, + blk64_t logical, blk64_t physical, + int flags); +extern errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags); +extern errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, + struct ext2_extent_info *info); +extern errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, + blk64_t blk); + +/* fileio.c */ +extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret); +extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret); +extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); +struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file); +extern errcode_t ext2fs_file_close(ext2_file_t file); +extern errcode_t ext2fs_file_flush(ext2_file_t file); +extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got); +extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written); +extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos); +extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos); +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); +extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); +extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); +extern errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size); + +/* finddev.c */ +extern char *ext2fs_find_block_device(dev_t device); + +/* flushb.c */ +extern errcode_t ext2fs_sync_device(int fd, int flushb); + +/* freefs.c */ +extern void ext2fs_free(ext2_filsys fs); +extern void ext2fs_free_dblist(ext2_dblist dblist); +extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); +extern void ext2fs_u32_list_free(ext2_u32_list bb); + +/* gen_bitmap.c */ +extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); +extern errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, + __u32 start, __u32 end, + __u32 real_end, + const char *descr, char *init_map, + ext2fs_generic_bitmap *ret); +extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret); +extern errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +extern void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap); +extern errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, + errcode_t magic, + errcode_t neq, + ext2_ino_t end, + ext2_ino_t *oend); +extern void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map); +extern errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, + __u32 new_end, + __u32 new_real_end, + ext2fs_generic_bitmap bmap); +extern errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +extern errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *out); +extern errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *in); + +/* gen_bitmap64.c */ +void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap); +errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char *descr, + ext2fs_generic_bitmap *ret); +errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); +void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap); +errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend); +void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap); +errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end); +errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2); +errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *out); +errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *in); + +/* getsize.c */ +extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks); +extern errcode_t ext2fs_get_device_size2(const char *file, int blocksize, + blk64_t *retblocks); + +/* getsectsize.c */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); +errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize); + +/* i_block.c */ +errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks); +errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, + blk64_t num_blocks); +errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b); + +/* imager.c */ +extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); + +/* ind_block.c */ +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); + +/* initialize.c */ +extern errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs); + +/* icount.c */ +extern void ext2fs_free_icount(ext2_icount_t icount); +extern errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, + int flags, ext2_icount_t *ret); +extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret); +extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret); +extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count); +extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); + +/* inode.c */ +extern errcode_t ext2fs_flush_icache(ext2_filsys fs); +extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, + ext2_ino_t *ino, + struct ext2_inode *inode, + int bufsize); +extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan); +extern void ext2fs_close_inode_scan(ext2_inode_scan scan); +extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode); +extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group); +extern void ext2fs_set_inode_callback + (ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + ext2_inode_scan scan, + dgrp_t group, + void * priv_data), + void *done_group_data); +extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags); +extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); +extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); + +/* inode_io.c */ +extern io_manager inode_io_manager; +extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name); +extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name); + +/* ismounted.c */ +extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); +extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen); + +/* punch.c */ +extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, blk64_t start, + blk64_t end); + +/* namei.c */ +extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode); +extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode); + +/* native.c */ +int ext2fs_native_flag(void); + +/* newdir.c */ +extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block); + +/* mkdir.c */ +extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name); + +/* mkjournal.c */ +extern errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, + blk_t *ret_blk, int *ret_count); +extern errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, + blk64_t *ret_blk, int *ret_count); +extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb); +extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, + ext2_filsys journal_dev); +extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, + int flags); +extern int ext2fs_default_journal_size(__u64 blocks); + +/* openfs.c */ +extern errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_channel * chan, + ext2_filsys *ret_fs); +extern errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_channel * chan, + ext2_filsys *ret_fs); +extern blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, + blk64_t group_block, dgrp_t i); +extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, + dgrp_t i); +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); + +/* get_pathname.c */ +extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name); + +/* link.c */ +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); + +/* read_bb.c */ +extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, + ext2_badblocks_list *bb_list); + +/* read_bb_file.c */ +extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)); +extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, + blk_t blk)); + +/* res_gdt.c */ +extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); + +/* swapfs.c */ +extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, + int has_header); +extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, + struct ext2_ext_attr_header *from_hdr); +extern void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, + struct ext2_ext_attr_entry *from_entry); +extern void ext2fs_swap_super(struct ext2_super_block * super); +extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); +extern void ext2fs_swap_group_desc2(ext2_filsys, struct ext2_group_desc *gdp); +extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize); +extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, + struct ext2_inode *f, int hostorder); + +/* valid_blk.c */ +extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); + +/* version.c */ +extern int ext2fs_parse_version_string(const char *ver_string); +extern int ext2fs_get_library_version(const char **ver_string, + const char **date_string); + +/* write_bb_file.c */ +extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags, + FILE *f); + + +/* inline functions */ +extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); +extern errcode_t ext2fs_get_memalign(unsigned long size, + unsigned long align, void *ptr); +extern errcode_t ext2fs_free_mem(void *ptr); +extern errcode_t ext2fs_resize_mem(unsigned long old_size, + unsigned long size, void *ptr); +extern void ext2fs_mark_super_dirty(ext2_filsys fs); +extern void ext2fs_mark_changed(ext2_filsys fs); +extern int ext2fs_test_changed(ext2_filsys fs); +extern void ext2fs_mark_valid(ext2_filsys fs); +extern void ext2fs_unmark_valid(ext2_filsys fs); +extern int ext2fs_test_valid(ext2_filsys fs); +extern void ext2fs_mark_ib_dirty(ext2_filsys fs); +extern void ext2fs_mark_bb_dirty(ext2_filsys fs); +extern int ext2fs_test_ib_dirty(ext2_filsys fs); +extern int ext2fs_test_bb_dirty(ext2_filsys fs); +extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); +extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); +extern blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group); +extern blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group); +extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode); +extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b); +extern __u64 ext2fs_div64_ceil(__u64 a, __u64 b); + +/* + * The actual inlined functions definitions themselves... + * + * If NO_INLINE_FUNCS is defined, then we won't try to do inline + * functions at all! + */ +#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) +#ifdef INCLUDE_INLINE_FUNCS +#define _INLINE_ extern +#else +#ifdef __GNUC__ +#define _INLINE_ extern __inline__ +#else /* For Watcom C */ +#define _INLINE_ extern inline +#endif +#endif + +#ifndef EXT2_CUSTOM_MEMORY_ROUTINES +#include +#include "mem_allocate.h" +/* + * Allocate memory + */ +_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr) +{ + void *pp; + + pp = mem_alloc(size); + if (!pp) + return EXT2_ET_NO_MEMORY; + memcpy(ptr, &pp, sizeof (pp)); + return 0; +} + +_INLINE_ errcode_t ext2fs_get_memalign(unsigned long size, + unsigned long align, void *ptr) +{ + void *pp; + + #ifdef HWRVL + pp = mem_align(32, size); + #else + pp = mem_alloc(size); + #endif + if (!pp) + return EXT2_ET_NO_MEMORY; + + memcpy(ptr, &pp, sizeof (pp)); + return 0; +} + +_INLINE_ errcode_t ext2fs_get_array(unsigned long count, unsigned long size, void *ptr) +{ + if (count && (-1UL)/countflags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark a filesystem as changed + */ +_INLINE_ void ext2fs_mark_changed(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem has changed + */ +_INLINE_ int ext2fs_test_changed(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_CHANGED); +} + +/* + * Mark a filesystem as valid + */ +_INLINE_ void ext2fs_mark_valid(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_VALID; +} + +/* + * Mark a filesystem as NOT valid + */ +_INLINE_ void ext2fs_unmark_valid(ext2_filsys fs) +{ + fs->flags &= ~EXT2_FLAG_VALID; +} + +/* + * Check to see if a filesystem is valid + */ +_INLINE_ int ext2fs_test_valid(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_VALID); +} + +/* + * Mark the inode bitmap as dirty + */ +_INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark the block bitmap as dirty + */ +_INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem's inode bitmap is dirty + */ +_INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_IB_DIRTY); +} + +/* + * Check to see if a filesystem's block bitmap is dirty + */ +_INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_BB_DIRTY); +} + +/* + * Return the group # of a block + */ +_INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) +{ + return ext2fs_group_of_blk2(fs, blk); +} +/* + * Return the group # of an inode number + */ +_INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) +{ + return (ino - 1) / fs->super->s_inodes_per_group; +} + +/* + * Return the first block (inclusive) in a group + */ +_INLINE_ blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group) +{ + return ext2fs_group_first_block2(fs, group); +} + +/* + * Return the last block (inclusive) in a group + */ +_INLINE_ blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group) +{ + return ext2fs_group_last_block2(fs, group); +} + +_INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode) +{ + return ext2fs_inode_data_blocks2(fs, inode); +} + +/* + * This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b) + */ +_INLINE_ unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b) +{ + if (!a) + return 0; + return ((a - 1) / b) + 1; +} + +_INLINE_ __u64 ext2fs_div64_ceil(__u64 a, __u64 b) +{ + if (!a) + return 0; + return ((a - 1) / b) + 1; +} + +#undef _INLINE_ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _EXT2FS_EXT2FS_H */ diff --git a/lib/libext2fs/orig/ext2fsP.h b/lib/libext2fs/orig/ext2fsP.h new file mode 100644 index 0000000..ab9ee76 --- /dev/null +++ b/lib/libext2fs/orig/ext2fsP.h @@ -0,0 +1,143 @@ +/* + * ext2fsP.h --- private header file for ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include "ext2fs.h" + +/* + * Badblocks list + */ +struct ext2_struct_u32_list { + int magic; + int num; + int size; + __u32 *list; + int badblocks_flags; +}; + +struct ext2_struct_u32_iterate { + int magic; + ext2_u32_list bb; + int ptr; +}; + + +/* + * Directory block iterator definition + */ +struct ext2_struct_dblist { + int magic; + ext2_filsys fs; + unsigned long long size; + unsigned long long count; + int sorted; + struct ext2_db_entry2 * list; +}; + +/* + * For directory iterators + */ +struct dir_context { + ext2_ino_t dir; + int flags; + char *buf; + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *priv_data; + errcode_t errcode; +}; + +/* + * Inode cache structure + */ +struct ext2_inode_cache { + void * buffer; + blk_t buffer_blk; + int cache_last; + int cache_size; + int refcount; + struct ext2_inode_cache_ent *cache; +}; + +struct ext2_inode_cache_ent { + ext2_ino_t ino; + struct ext2_inode inode; +}; + +/* Function prototypes */ + +extern int ext2fs_process_dir_block(ext2_filsys fs, + blk64_t *blocknr, + e2_blkcnt_t blockcnt, + blk64_t ref_block, + int ref_offset, + void *priv_data); + +/* Generic numeric progress meter */ + +struct ext2fs_numeric_progress_struct { + __u64 max; + int log_max; + int skip_progress; +}; + +extern void ext2fs_numeric_progress_init(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *label, __u64 max); +extern void ext2fs_numeric_progress_update(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + __u64 val); +extern void ext2fs_numeric_progress_close(ext2_filsys fs, + struct ext2fs_numeric_progress_struct * progress, + const char *message); + +/* + * 64-bit bitmap support + */ + +#define EXT2FS_BMAP64_BITARRAY 1 + +extern errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char * description, + ext2fs_generic_bitmap *bmap); + +extern void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap); + +extern errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); + +extern errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end); +extern errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend); +extern int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg); +extern errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int num, + void *in); +extern errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bitmap, + __u64 start, unsigned int num, + void *out); +extern int ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func); + +extern int ext2fs_mem_is_zero(const char *mem, size_t len); diff --git a/lib/libext2fs/orig/ext3_extents.h b/lib/libext2fs/orig/ext3_extents.h new file mode 100644 index 0000000..88fabc9 --- /dev/null +++ b/lib/libext2fs/orig/ext3_extents.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003,2004 Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#ifndef _LINUX_EXT3_EXTENTS +#define _LINUX_EXT3_EXTENTS + +/* + * ext3_inode has i_block array (total 60 bytes) + * first 4 bytes are used to store: + * - tree depth (0 mean there is no tree yet. all extents in the inode) + * - number of alive extents in the inode + */ + +/* + * this is extent on-disk structure + * it's used at the bottom of the tree + */ +struct ext3_extent { + __u32 ee_block; /* first logical block extent covers */ + __u16 ee_len; /* number of blocks covered by extent */ + __u16 ee_start_hi; /* high 16 bits of physical block */ + __u32 ee_start; /* low 32 bigs of physical block */ +}; + +/* + * this is index on-disk structure + * it's used at all the levels, but the bottom + */ +struct ext3_extent_idx { + __u32 ei_block; /* index covers logical blocks from 'block' */ + __u32 ei_leaf; /* pointer to the physical block of the next * + * level. leaf or next index could bet here */ + __u16 ei_leaf_hi; /* high 16 bits of physical block */ + __u16 ei_unused; +}; + +/* + * each block (leaves and indexes), even inode-stored has header + */ +struct ext3_extent_header { + __u16 eh_magic; /* probably will support different formats */ + __u16 eh_entries; /* number of valid entries */ + __u16 eh_max; /* capacity of store in entries */ + __u16 eh_depth; /* has tree real underlaying blocks? */ + __u32 eh_generation; /* generation of the tree */ +}; + +#define EXT3_EXT_MAGIC 0xf30a + +/* + * array of ext3_ext_path contains path to some extent + * creation/lookup routines use it for traversal/splitting/etc + * truncate uses it to simulate recursive walking + */ +struct ext3_ext_path { + __u32 p_block; + __u16 p_depth; + struct ext3_extent *p_ext; + struct ext3_extent_idx *p_idx; + struct ext3_extent_header *p_hdr; + struct buffer_head *p_bh; +}; + +/* + * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an + * initialized extent. This is 2^15 and not (2^16 - 1), since we use the + * MSB of ee_len field in the extent datastructure to signify if this + * particular extent is an initialized extent or an uninitialized (i.e. + * preallocated). + * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an + * uninitialized extent. + * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an + * uninitialized one. In other words, if MSB of ee_len is set, it is an + * uninitialized extent with only one special scenario when ee_len = 0x8000. + * In this case we can not have an uninitialized extent of zero length and + * thus we make it as a special case of initialized extent with 0x8000 length. + * This way we get better extent-to-group alignment for initialized extents. + * Hence, the maximum number of blocks we can have in an *initialized* + * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767). + */ +#define EXT_INIT_MAX_LEN (1UL << 15) +#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1) + +#define EXT_FIRST_EXTENT(__hdr__) \ + ((struct ext3_extent *) (((char *) (__hdr__)) + \ + sizeof(struct ext3_extent_header))) +#define EXT_FIRST_INDEX(__hdr__) \ + ((struct ext3_extent_idx *) (((char *) (__hdr__)) + \ + sizeof(struct ext3_extent_header))) +#define EXT_HAS_FREE_INDEX(__path__) \ + ((__path__)->p_hdr->eh_entries < (__path__)->p_hdr->eh_max) +#define EXT_LAST_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_entries - 1) +#define EXT_LAST_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_entries - 1) +#define EXT_MAX_EXTENT(__hdr__) \ + (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_max - 1) +#define EXT_MAX_INDEX(__hdr__) \ + (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_max - 1) + +#endif /* _LINUX_EXT3_EXTENTS */ + diff --git a/lib/libext2fs/orig/ext_attr.c b/lib/libext2fs/orig/ext_attr.c new file mode 100644 index 0000000..52664eb --- /dev/null +++ b/lib/libext2fs/orig/ext_attr.c @@ -0,0 +1,155 @@ +/* + * ext_attr.c --- extended attribute blocks + * + * Copyright (C) 2001 Andreas Gruenbacher, + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * ext2_xattr_hash_entry() + * + * Compute the hash of an extended attribute. + */ +__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) +{ + __u32 hash = 0; + char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry); + int n; + + for (n = 0; n < entry->e_name_len; n++) { + hash = (hash << NAME_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ + *name++; + } + + /* The hash needs to be calculated on the data in little-endian. */ + if (entry->e_value_block == 0 && entry->e_value_size != 0) { + __u32 *value = (__u32 *)data; + for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >> + EXT2_EXT_ATTR_PAD_BITS; n; n--) { + hash = (hash << VALUE_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ + ext2fs_le32_to_cpu(*value++); + } + } + + return hash; +} + +#undef NAME_HASH_SHIFT +#undef VALUE_HASH_SHIFT + +errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +{ + errcode_t retval; + + retval = io_channel_read_blk64(fs->io, block, 1, buf); + if (retval) + return retval; +#ifdef WORDS_BIGENDIAN + ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); +#endif + return 0; +} + +errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) +{ + return ext2fs_read_ext_attr2(fs, block, buf); +} + +errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +{ + errcode_t retval; + char *write_buf; + char *buf = NULL; + +#ifdef WORDS_BIGENDIAN + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + write_buf = buf; + ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); +#else + write_buf = (char *) inbuf; +#endif + retval = io_channel_write_blk64(fs->io, block, 1, write_buf); + if (buf) + ext2fs_free_mem(&buf); + if (!retval) + ext2fs_mark_changed(fs); + return retval; +} + +errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) +{ + return ext2fs_write_ext_attr2(fs, block, inbuf); +} + +/* + * This function adjusts the reference count of the EA block. + */ +errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + errcode_t retval; + struct ext2_ext_attr_header *header; + char *buf = 0; + + if ((blk >= ext2fs_blocks_count(fs->super)) || + (blk < fs->super->s_first_data_block)) + return EXT2_ET_BAD_EA_BLOCK_NUM; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + retval = ext2fs_read_ext_attr2(fs, blk, block_buf); + if (retval) + goto errout; + + header = (struct ext2_ext_attr_header *) block_buf; + header->h_refcount += adjust; + if (newcount) + *newcount = header->h_refcount; + + retval = ext2fs_write_ext_attr2(fs, blk, block_buf); + if (retval) + goto errout; + +errout: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount); +} diff --git a/lib/libext2fs/orig/extent.c b/lib/libext2fs/orig/extent.c new file mode 100644 index 0000000..5e07092 --- /dev/null +++ b/lib/libext2fs/orig/extent.c @@ -0,0 +1,2007 @@ +/* + * extent.c --- routines to implement extents support + * + * Copyright (C) 2007 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +/* + * Definitions to be dropped in lib/ext2fs/ext2fs.h + */ + +/* + * Private definitions + */ + +struct extent_path { + char *buf; + int entries; + int max_entries; + int left; + int visit_num; + int flags; + blk64_t end_blk; + void *curr; +}; + + +struct ext2_extent_handle { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode *inode; + struct ext2_inode inodebuf; + int type; + int level; + int max_depth; + struct extent_path *path; +}; + +struct ext2_extent_path { + errcode_t magic; + int leaf_height; + blk64_t lblk; +}; + +/* + * Useful Debugging stuff + */ + +#ifdef DEBUG +static void dbg_show_header(struct ext3_extent_header *eh) +{ + printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n", + ext2fs_le16_to_cpu(eh->eh_magic), + ext2fs_le16_to_cpu(eh->eh_entries), + ext2fs_le16_to_cpu(eh->eh_max), + ext2fs_le16_to_cpu(eh->eh_depth), + ext2fs_le32_to_cpu(eh->eh_generation)); +} + +static void dbg_show_index(struct ext3_extent_idx *ix) +{ + printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n", + ext2fs_le32_to_cpu(ix->ei_block), + ext2fs_le32_to_cpu(ix->ei_leaf), + ext2fs_le16_to_cpu(ix->ei_leaf_hi), + ext2fs_le16_to_cpu(ix->ei_unused)); +} + +static void dbg_show_extent(struct ext3_extent *ex) +{ + printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n", + ext2fs_le32_to_cpu(ex->ee_block), + ext2fs_le32_to_cpu(ex->ee_block) + + ext2fs_le16_to_cpu(ex->ee_len) - 1, + ext2fs_le16_to_cpu(ex->ee_len), + ext2fs_le32_to_cpu(ex->ee_start), + ext2fs_le16_to_cpu(ex->ee_start_hi)); +} + +static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) +{ + if (desc) + printf("%s: ", desc); + printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", + extent->e_lblk, extent->e_lblk + extent->e_len - 1, + extent->e_len, extent->e_pblk); + if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) + fputs("LEAF ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) + fputs("UNINIT ", stdout); + if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + fputs("2ND_VISIT ", stdout); + if (!extent->e_flags) + fputs("(none)", stdout); + fputc('\n', stdout); + +} + +#else +#define dbg_show_header(eh) do { } while (0) +#define dbg_show_index(ix) do { } while (0) +#define dbg_show_extent(ex) do { } while (0) +#define dbg_print_extent(desc, ex) do { } while (0) +#endif + +/* + * Verify the extent header as being sane + */ +errcode_t ext2fs_extent_header_verify(void *ptr, int size) +{ + int eh_max, entry_size; + struct ext3_extent_header *eh = ptr; + + dbg_show_header(eh); + if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) + return EXT2_ET_EXTENT_HEADER_BAD; + if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max)) + return EXT2_ET_EXTENT_HEADER_BAD; + if (eh->eh_depth == 0) + entry_size = sizeof(struct ext3_extent); + else + entry_size = sizeof(struct ext3_extent_idx); + + eh_max = (size - sizeof(*eh)) / entry_size; + /* Allow two extent-sized items at the end of the block, for + * ext4_extent_tail with checksum in the future. */ + if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) || + (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2))) + return EXT2_ET_EXTENT_HEADER_BAD; + + return 0; +} + + +/* + * Begin functions to handle an inode's extent information + */ +extern void ext2fs_extent_free(ext2_extent_handle_t handle) +{ + int i; + + if (!handle) + return; + + if (handle->path) { + for (i=1; i <= handle->max_depth; i++) { + if (handle->path[i].buf) + ext2fs_free_mem(&handle->path[i].buf); + } + ext2fs_free_mem(&handle->path); + } + ext2fs_free_mem(&handle); +} + +extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, + ext2_extent_handle_t *ret_handle) +{ + return ext2fs_extent_open2(fs, ino, NULL, ret_handle); +} + +extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + ext2_extent_handle_t *ret_handle) +{ + struct ext2_extent_handle *handle; + errcode_t retval; + int i; + struct ext3_extent_header *eh; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!inode) + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + + retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); + if (retval) + return retval; + memset(handle, 0, sizeof(struct ext2_extent_handle)); + + handle->ino = ino; + handle->fs = fs; + + if (inode) { + handle->inode = inode; + } else { + handle->inode = &handle->inodebuf; + retval = ext2fs_read_inode(fs, ino, handle->inode); + if (retval) + goto errout; + } + + eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; + + for (i=0; i < EXT2_N_BLOCKS; i++) + if (handle->inode->i_block[i]) + break; + if (i >= EXT2_N_BLOCKS) { + eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); + eh->eh_depth = 0; + eh->eh_entries = 0; + i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / + sizeof(struct ext3_extent); + eh->eh_max = ext2fs_cpu_to_le16(i); + handle->inode->i_flags |= EXT4_EXTENTS_FL; + } + + if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) { + retval = EXT2_ET_INODE_NOT_EXTENT; + goto errout; + } + + retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block)); + if (retval) + goto errout; + + handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); + handle->type = ext2fs_le16_to_cpu(eh->eh_magic); + + retval = ext2fs_get_mem(((handle->max_depth+1) * + sizeof(struct extent_path)), + &handle->path); + memset(handle->path, 0, + (handle->max_depth+1) * sizeof(struct extent_path)); + handle->path[0].buf = (char *) handle->inode->i_block; + + handle->path[0].left = handle->path[0].entries = + ext2fs_le16_to_cpu(eh->eh_entries); + handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); + handle->path[0].curr = 0; + handle->path[0].end_blk = + ((((__u64) handle->inode->i_size_high << 32) + + handle->inode->i_size + (fs->blocksize - 1)) + >> EXT2_BLOCK_SIZE_BITS(fs->super)); + handle->path[0].visit_num = 1; + handle->level = 0; + handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; + + *ret_handle = handle; + return 0; + +errout: + ext2fs_extent_free(handle); + return retval; +} + +/* + * This function is responsible for (optionally) moving through the + * extent tree and then returning the current extent + */ +errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, + int flags, struct ext2fs_extent *extent) +{ + struct extent_path *path, *newpath; + struct ext3_extent_header *eh; + struct ext3_extent_idx *ix = 0; + struct ext3_extent *ex; + errcode_t retval; + blk64_t blk; + blk64_t end_blk; + int orig_op, op; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + orig_op = op = flags & EXT2_EXTENT_MOVE_MASK; + +retry: + path = handle->path + handle->level; + if ((orig_op == EXT2_EXTENT_NEXT) || + (orig_op == EXT2_EXTENT_NEXT_LEAF)) { + if (handle->level < handle->max_depth) { + /* interior node */ + if (path->visit_num == 0) { + path->visit_num++; + op = EXT2_EXTENT_DOWN; + } else if (path->left > 0) + op = EXT2_EXTENT_NEXT_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_NEXT; + } else { + /* leaf node */ + if (path->left > 0) + op = EXT2_EXTENT_NEXT_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_NEXT; + } + if (op != EXT2_EXTENT_NEXT_SIB) { +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN) ? "down" : + ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); +#endif + } + } + + if ((orig_op == EXT2_EXTENT_PREV) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) { + if (handle->level < handle->max_depth) { + /* interior node */ + if (path->visit_num > 0 ) { + /* path->visit_num = 0; */ + op = EXT2_EXTENT_DOWN_AND_LAST; + } else if (path->left < path->entries-1) + op = EXT2_EXTENT_PREV_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_PREV; + } else { + /* leaf node */ + if (path->left < path->entries-1) + op = EXT2_EXTENT_PREV_SIB; + else if (handle->level > 0) + op = EXT2_EXTENT_UP; + else + return EXT2_ET_EXTENT_NO_PREV; + } + if (op != EXT2_EXTENT_PREV_SIB) { +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" : + ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); +#endif + } + } + + if (orig_op == EXT2_EXTENT_LAST_LEAF) { + if ((handle->level < handle->max_depth) && + (path->left == 0)) + op = EXT2_EXTENT_DOWN; + else + op = EXT2_EXTENT_LAST_SIB; +#ifdef DEBUG_GET_EXTENT + printf("<<<< OP = %s\n", + (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib"); +#endif + } + + switch (op) { + case EXT2_EXTENT_CURRENT: + ix = path->curr; + break; + case EXT2_EXTENT_ROOT: + handle->level = 0; + path = handle->path + handle->level; + case EXT2_EXTENT_FIRST_SIB: + path->left = path->entries; + path->curr = 0; + case EXT2_EXTENT_NEXT_SIB: + if (path->left <= 0) + return EXT2_ET_EXTENT_NO_NEXT; + if (path->curr) { + ix = path->curr; + ix++; + } else { + eh = (struct ext3_extent_header *) path->buf; + ix = EXT_FIRST_INDEX(eh); + } + path->left--; + path->curr = ix; + path->visit_num = 0; + break; + case EXT2_EXTENT_PREV_SIB: + if (!path->curr || + path->left+1 >= path->entries) + return EXT2_ET_EXTENT_NO_PREV; + ix = path->curr; + ix--; + path->curr = ix; + path->left++; + if (handle->level < handle->max_depth) + path->visit_num = 1; + break; + case EXT2_EXTENT_LAST_SIB: + eh = (struct ext3_extent_header *) path->buf; + path->curr = EXT_LAST_EXTENT(eh); + ix = path->curr; + path->left = 0; + path->visit_num = 0; + break; + case EXT2_EXTENT_UP: + if (handle->level <= 0) + return EXT2_ET_EXTENT_NO_UP; + handle->level--; + path--; + ix = path->curr; + if ((orig_op == EXT2_EXTENT_PREV) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) + path->visit_num = 0; + break; + case EXT2_EXTENT_DOWN: + case EXT2_EXTENT_DOWN_AND_LAST: + if (!path->curr ||(handle->level >= handle->max_depth)) + return EXT2_ET_EXTENT_NO_DOWN; + + ix = path->curr; + newpath = path + 1; + if (!newpath->buf) { + retval = ext2fs_get_mem(handle->fs->blocksize, + &newpath->buf); + if (retval) + return retval; + } + blk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) && + (handle->fs->io != handle->fs->image_io)) + memset(newpath->buf, 0, handle->fs->blocksize); + else { + retval = io_channel_read_blk64(handle->fs->io, + blk, 1, newpath->buf); + if (retval) + return retval; + } + handle->level++; + + eh = (struct ext3_extent_header *) newpath->buf; + + retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize); + if (retval) { + handle->level--; + return retval; + } + + newpath->left = newpath->entries = + ext2fs_le16_to_cpu(eh->eh_entries); + newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); + + if (path->left > 0) { + ix++; + newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block); + } else + newpath->end_blk = path->end_blk; + + path = newpath; + if (op == EXT2_EXTENT_DOWN) { + ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh); + path->curr = ix; + path->left = path->entries - 1; + path->visit_num = 0; + } else { + ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh); + path->curr = ix; + path->left = 0; + if (handle->level < handle->max_depth) + path->visit_num = 1; + } +#ifdef DEBUG_GET_EXTENT + printf("Down to level %d/%d, end_blk=%llu\n", + handle->level, handle->max_depth, + path->end_blk); +#endif + break; + default: + return EXT2_ET_OP_NOT_SUPPORTED; + } + + if (!ix) + return EXT2_ET_NO_CURRENT_NODE; + + extent->e_flags = 0; +#ifdef DEBUG_GET_EXTENT + printf("(Left %d)\n", path->left); +#endif + + if (handle->level == handle->max_depth) { + ex = (struct ext3_extent *) ix; + + extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) + + ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); + extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block); + extent->e_len = ext2fs_le16_to_cpu(ex->ee_len); + extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF; + if (extent->e_len > EXT_INIT_MAX_LEN) { + extent->e_len -= EXT_INIT_MAX_LEN; + extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + } else { + extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block); + if (path->left > 0) { + ix++; + end_blk = ext2fs_le32_to_cpu(ix->ei_block); + } else + end_blk = path->end_blk; + + extent->e_len = end_blk - extent->e_lblk; + } + if (path->visit_num) + extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT; + + if (((orig_op == EXT2_EXTENT_NEXT_LEAF) || + (orig_op == EXT2_EXTENT_PREV_LEAF)) && + (handle->level != handle->max_depth)) + goto retry; + + if ((orig_op == EXT2_EXTENT_LAST_LEAF) && + ((handle->level != handle->max_depth) || + (path->left != 0))) + goto retry; + + return 0; +} + +static errcode_t update_path(ext2_extent_handle_t handle) +{ + blk64_t blk; + errcode_t retval; + struct ext3_extent_idx *ix; + + if (handle->level == 0) { + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + } else { + ix = handle->path[handle->level - 1].curr; + blk = ext2fs_le32_to_cpu(ix->ei_leaf) + + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); + + retval = io_channel_write_blk64(handle->fs->io, + blk, 1, handle->path[handle->level].buf); + } + return retval; +} + +#if 0 +errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle, + ext2_extent_path_t *ret_path) +{ + ext2_extent_path_t save_path; + struct ext2fs_extent extent; + struct ext2_extent_info info; + errcode_t retval; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + return retval; + + retval = ext2fs_extent_get_info(handle, &info); + if (retval) + return retval; + + retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path); + if (retval) + return retval; + memset(save_path, 0, sizeof(struct ext2_extent_path)); + + save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH; + save_path->leaf_height = info.max_depth - info.curr_level - 1; + save_path->lblk = extent.e_lblk; + + *ret_path = save_path; + return 0; +} + +errcode_t ext2fs_extent_free_path(ext2_extent_path_t path) +{ + EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH); + + ext2fs_free_mem(&path); + return 0; +} +#endif + +/* + * Go to the node at leaf_level which contains logical block blk. + * + * leaf_level is height from the leaf node level, i.e. + * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc. + * + * If "blk" has no mapping (hole) then handle is left at last + * extent before blk. + */ +static errcode_t extent_goto(ext2_extent_handle_t handle, + int leaf_level, blk64_t blk) +{ + struct ext2fs_extent extent; + errcode_t retval; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); + if (retval) { + if (retval == EXT2_ET_EXTENT_NO_NEXT) + retval = EXT2_ET_EXTENT_NOT_FOUND; + return retval; + } + + if (leaf_level > handle->max_depth) { +#ifdef DEBUG + printf("leaf level %d greater than tree depth %d\n", + leaf_level, handle->max_depth); +#endif + return EXT2_ET_OP_NOT_SUPPORTED; + } + +#ifdef DEBUG + printf("goto extent ino %u, level %d, %llu\n", handle->ino, + leaf_level, blk); +#endif + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("root", &extent); +#endif + while (1) { + if (handle->max_depth - handle->level == leaf_level) { + /* block is in this &extent */ + if ((blk >= extent.e_lblk) && + (blk < extent.e_lblk + extent.e_len)) + return 0; + if (blk < extent.e_lblk) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_SIB, + &extent); + return EXT2_ET_EXTENT_NOT_FOUND; + } + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_SIB, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + return EXT2_ET_EXTENT_NOT_FOUND; + if (retval) + return retval; + continue; + } + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB, + &extent); + if (retval == EXT2_ET_EXTENT_NO_NEXT) + goto go_down; + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("next", &extent); +#endif + if (blk == extent.e_lblk) + goto go_down; + if (blk > extent.e_lblk) + continue; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB, + &extent); + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("prev", &extent); +#endif + + go_down: + retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN, + &extent); + if (retval) + return retval; + +#ifdef DEBUG_GOTO_EXTENTS + dbg_print_extent("down", &extent); +#endif + } +} + +errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, + blk64_t blk) +{ + return extent_goto(handle, 0, blk); +} + +/* + * Traverse back up to root fixing parents of current node as needed. + * + * If we changed start of first entry in a node, fix parent index start + * and so on. + * + * Safe to call for any position in node; if not at the first entry, + * will simply return. + */ +static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle) +{ + int retval = 0; + blk64_t start; + struct extent_path *path; + struct ext2fs_extent extent; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + + /* modified node's start block */ + start = extent.e_lblk; + + /* traverse up until index not first, or startblk matches, or top */ + while (handle->level > 0 && + (path->left == path->entries - 1)) { + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + if (retval) + goto done; + if (extent.e_lblk == start) + break; + path = handle->path + handle->level; + extent.e_len += (extent.e_lblk - start); + extent.e_lblk = start; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + update_path(handle); + } + + /* put handle back to where we started */ + retval = ext2fs_extent_goto(handle, start); +done: + return retval; +} + +errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, + int flags EXT2FS_ATTR((unused)), + struct ext2fs_extent *extent) +{ + struct extent_path *path; + struct ext3_extent_idx *ix; + struct ext3_extent *ex; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("extent replace: %u ", handle->ino); + dbg_print_extent(0, extent); +#endif + + if (handle->level == handle->max_depth) { + ex = path->curr; + + ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk); + ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); + ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); + if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) { + if (extent->e_len > EXT_UNINIT_MAX_LEN) + return EXT2_ET_EXTENT_INVALID_LENGTH; + ex->ee_len = ext2fs_cpu_to_le16(extent->e_len + + EXT_INIT_MAX_LEN); + } else { + if (extent->e_len > EXT_INIT_MAX_LEN) + return EXT2_ET_EXTENT_INVALID_LENGTH; + ex->ee_len = ext2fs_cpu_to_le16(extent->e_len); + } + } else { + ix = path->curr; + + ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); + ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); + ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk); + ix->ei_unused = 0; + } + update_path(handle); + return 0; +} + +/* + * allocate a new block, move half the current node to it, and update parent + * + * handle will be left pointing at original record. + */ +static errcode_t extent_node_split(ext2_extent_handle_t handle) +{ + errcode_t retval = 0; + blk64_t new_node_pblk; + blk64_t new_node_start; + blk64_t orig_lblk; + blk64_t goal_blk = 0; + int orig_height; + char *block_buf = NULL; + struct ext2fs_extent extent; + struct extent_path *path, *newpath = 0; + struct ext3_extent_header *eh, *neweh; + int tocopy; + int new_root = 0; + struct ext2_extent_info info; + + /* basic sanity */ + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("splitting node at level %d\n", handle->level); +#endif + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + + retval = ext2fs_extent_get_info(handle, &info); + if (retval) + goto done; + + /* save the position we were originally splitting... */ + orig_height = info.max_depth - info.curr_level; + orig_lblk = extent.e_lblk; + + /* Is there room in the parent for a new entry? */ + if (handle->level && + (handle->path[handle->level - 1].entries >= + handle->path[handle->level - 1].max_entries)) { + +#ifdef DEBUG + printf("parent level %d full; splitting it too\n", + handle->level - 1); +#endif + /* split the parent */ + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + if (retval) + goto done; + goal_blk = extent.e_pblk; + + retval = extent_node_split(handle); + if (retval) + goto done; + + /* get handle back to our original split position */ + retval = extent_goto(handle, orig_height, orig_lblk); + if (retval) + goto done; + } + + /* At this point, parent should have room for this split */ + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + /* extent header of the current node we'll split */ + eh = (struct ext3_extent_header *)path->buf; + + /* splitting root level means moving them all out */ + if (handle->level == 0) { + new_root = 1; + tocopy = ext2fs_le16_to_cpu(eh->eh_entries); + retval = ext2fs_get_mem(((handle->max_depth+2) * + sizeof(struct extent_path)), + &newpath); + if (retval) + goto done; + memset(newpath, 0, + ((handle->max_depth+2) * sizeof(struct extent_path))); + } else { + tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2; + } + +#ifdef DEBUG + printf("will copy out %d of %d entries at level %d\n", + tocopy, ext2fs_le16_to_cpu(eh->eh_entries), + handle->level); +#endif + + if (!tocopy) { +#ifdef DEBUG + printf("Nothing to copy to new block!\n"); +#endif + retval = EXT2_ET_CANT_SPLIT_EXTENT; + goto done; + } + + /* first we need a new block, or can do nothing. */ + block_buf = malloc(handle->fs->blocksize); + if (!block_buf) { + retval = ENOMEM; + goto done; + } + + if (!goal_blk) { + dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino); + __u8 log_flex = handle->fs->super->s_log_groups_per_flex; + + if (log_flex) + group = group & ~((1 << (log_flex)) - 1); + goal_blk = (group * handle->fs->super->s_blocks_per_group) + + handle->fs->super->s_first_data_block; + } + retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf, + &new_node_pblk); + if (retval) + goto done; + +#ifdef DEBUG + printf("will copy to new node at block %lu\n", + (unsigned long) new_node_pblk); +#endif + + /* Copy data into new block buffer */ + /* First the header for the new block... */ + neweh = (struct ext3_extent_header *) block_buf; + memcpy(neweh, eh, sizeof(struct ext3_extent_header)); + neweh->eh_entries = ext2fs_cpu_to_le16(tocopy); + neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize - + sizeof(struct ext3_extent_header)) / + sizeof(struct ext3_extent)); + + /* then the entries for the new block... */ + memcpy(EXT_FIRST_INDEX(neweh), + EXT_FIRST_INDEX(eh) + + (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy), + sizeof(struct ext3_extent_idx) * tocopy); + + new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block); + + /* ...and write the new node block out to disk. */ + retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1, + block_buf); + + if (retval) + goto done; + + /* OK! we've created the new node; now adjust the tree */ + + /* current path now has fewer active entries, we copied some out */ + if (handle->level == 0) { + memcpy(newpath, path, + sizeof(struct extent_path) * (handle->max_depth+1)); + handle->path = newpath; + newpath = path; + path = handle->path; + path->entries = 1; + path->left = path->max_entries - 1; + handle->max_depth++; + eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth); + } else { + path->entries -= tocopy; + path->left -= tocopy; + } + + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + /* this writes out the node, incl. the modified header */ + retval = update_path(handle); + if (retval) + goto done; + + /* now go up and insert/replace index for new node we created */ + if (new_root) { + retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent); + if (retval) + goto done; + + extent.e_lblk = new_node_start; + extent.e_pblk = new_node_pblk; + extent.e_len = handle->path[0].end_blk - extent.e_lblk; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else { + __u32 new_node_length; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); + /* will insert after this one; it's length is shorter now */ + new_node_length = new_node_start - extent.e_lblk; + extent.e_len -= new_node_length; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + + /* now set up the new extent and insert it */ + extent.e_lblk = new_node_start; + extent.e_pblk = new_node_pblk; + extent.e_len = new_node_length; + retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent); + if (retval) + goto done; + } + + /* get handle back to our original position */ + retval = extent_goto(handle, orig_height, orig_lblk); + if (retval) + goto done; + + /* new node hooked in, so update inode block count (do this here?) */ + handle->inode->i_blocks += handle->fs->blocksize / 512; + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + if (retval) + goto done; + +done: + if (newpath) + ext2fs_free_mem(&newpath); + free(block_buf); + + return retval; +} + +errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, + struct ext2fs_extent *extent) +{ + struct extent_path *path; + struct ext3_extent_idx *ix; + struct ext3_extent_header *eh; + errcode_t retval; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + printf("extent insert: %u ", handle->ino); + dbg_print_extent(0, extent); +#endif + + path = handle->path + handle->level; + + if (path->entries >= path->max_entries) { + if (flags & EXT2_EXTENT_INSERT_NOSPLIT) { + return EXT2_ET_CANT_INSERT_EXTENT; + } else { +#ifdef DEBUG + printf("node full (level %d) - splitting\n", + handle->level); +#endif + retval = extent_node_split(handle); + if (retval) + return retval; + path = handle->path + handle->level; + } + } + + eh = (struct ext3_extent_header *) path->buf; + if (path->curr) { + ix = path->curr; + if (flags & EXT2_EXTENT_INSERT_AFTER) { + ix++; + path->left--; + } + } else + ix = EXT_FIRST_INDEX(eh); + + path->curr = ix; + + if (path->left >= 0) + memmove(ix + 1, ix, + (path->left+1) * sizeof(struct ext3_extent_idx)); + path->left++; + path->entries++; + + eh = (struct ext3_extent_header *) path->buf; + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + + retval = ext2fs_extent_replace(handle, 0, extent); + if (retval) + goto errout; + + retval = update_path(handle); + if (retval) + goto errout; + + return 0; + +errout: + ext2fs_extent_delete(handle, 0); + return retval; +} + +/* + * Sets the physical block for a logical file block in the extent tree. + * + * May: map unmapped, unmap mapped, or remap mapped blocks. + * + * Mapping an unmapped block adds a single-block extent. + * + * Unmapping first or last block modifies extent in-place + * - But may need to fix parent's starts too in first-block case + * + * Mapping any unmapped block requires adding a (single-block) extent + * and inserting into proper point in tree. + * + * Modifying (unmapping or remapping) a block in the middle + * of an extent requires splitting the extent. + * - Remapping case requires new single-block extent. + * + * Remapping first or last block adds an extent. + * + * We really need extent adding to be smart about merging. + */ + +errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, + blk64_t logical, blk64_t physical, int flags) +{ + errcode_t ec, retval = 0; + int mapped = 1; /* logical is mapped? */ + int orig_height; + int extent_uninit = 0; + int prev_uninit = 0; + int next_uninit = 0; + int new_uninit = 0; + int max_len = EXT_INIT_MAX_LEN; + int has_prev, has_next; + blk64_t orig_lblk; + struct extent_path *path; + struct ext2fs_extent extent, next_extent, prev_extent; + struct ext2fs_extent newextent; + struct ext2_extent_info info; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + +#ifdef DEBUG + printf("set_bmap ino %u log %lld phys %lld flags %d\n", + handle->ino, logical, physical, flags); +#endif + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + + path = handle->path + handle->level; + + if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) { + new_uninit = 1; + max_len = EXT_UNINIT_MAX_LEN; + } + + /* if (re)mapping, set up new extent to insert */ + if (physical) { + newextent.e_len = 1; + newextent.e_pblk = physical; + newextent.e_lblk = logical; + newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF; + if (new_uninit) + newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + + /* special case if the extent tree is completely empty */ + if ((handle->max_depth == 0) && (path->entries == 0)) { + retval = ext2fs_extent_insert(handle, 0, &newextent); + return retval; + } + + /* save our original location in the extent tree */ + if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &extent))) { + if (retval != EXT2_ET_NO_CURRENT_NODE) + return retval; + memset(&extent, 0, sizeof(extent)); + } + if ((retval = ext2fs_extent_get_info(handle, &info))) + return retval; + orig_height = info.max_depth - info.curr_level; + orig_lblk = extent.e_lblk; + + /* go to the logical spot we want to (re/un)map */ + retval = ext2fs_extent_goto(handle, logical); + if (retval) { + if (retval == EXT2_ET_EXTENT_NOT_FOUND) { + retval = 0; + mapped = 0; + if (!physical) { +#ifdef DEBUG + printf("block %llu already unmapped\n", + logical); +#endif + goto done; + } + } else + goto done; + } + + /* + * This may be the extent *before* the requested logical, + * if it's currently unmapped. + * + * Get the previous and next leaf extents, if they are present. + */ + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); + if (retval) + goto done; + if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + extent_uninit = 1; + retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); + if (retval) { + has_next = 0; + if (retval != EXT2_ET_EXTENT_NO_NEXT) + goto done; + } else { + dbg_print_extent("set_bmap: next_extent", + &next_extent); + has_next = 1; + if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + next_uninit = 1; + } + retval = ext2fs_extent_goto(handle, logical); + if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) + goto done; + retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent); + if (retval) { + has_prev = 0; + if (retval != EXT2_ET_EXTENT_NO_PREV) + goto done; + } else { + has_prev = 1; + dbg_print_extent("set_bmap: prev_extent", + &prev_extent); + if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + prev_uninit = 1; + } + retval = ext2fs_extent_goto(handle, logical); + if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) + goto done; + + /* check if already pointing to the requested physical */ + if (mapped && (new_uninit == extent_uninit) && + (extent.e_pblk + (logical - extent.e_lblk) == physical)) { +#ifdef DEBUG + printf("physical block (at %llu) unchanged\n", logical); +#endif + goto done; + } + + if (!mapped) { +#ifdef DEBUG + printf("mapping unmapped logical block %llu\n", logical); +#endif + if ((logical == extent.e_lblk + extent.e_len) && + (physical == extent.e_pblk + extent.e_len) && + (new_uninit == extent_uninit) && + ((int) extent.e_len < max_len-1)) { + extent.e_len++; + retval = ext2fs_extent_replace(handle, 0, &extent); + } else if ((logical == extent.e_lblk - 1) && + (physical == extent.e_pblk - 1) && + (new_uninit == extent_uninit) && + ((int) extent.e_len < max_len - 1)) { + extent.e_len++; + extent.e_lblk--; + extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, &extent); + } else if (has_next && + (logical == next_extent.e_lblk - 1) && + (physical == next_extent.e_pblk - 1) && + (new_uninit == next_uninit) && + ((int) next_extent.e_len < max_len - 1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, + &next_extent); + if (retval) + goto done; + next_extent.e_len++; + next_extent.e_lblk--; + next_extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, &next_extent); + } else if (logical < extent.e_lblk) + retval = ext2fs_extent_insert(handle, 0, &newextent); + else + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + retval = ext2fs_extent_fix_parents(handle); + if (retval) + goto done; + } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) { +#ifdef DEBUG + printf("(re/un)mapping only block in extent\n"); +#endif + if (physical) { + retval = ext2fs_extent_replace(handle, 0, &newextent); + } else { + retval = ext2fs_extent_delete(handle, 0); + if (retval) + goto done; + ec = ext2fs_extent_fix_parents(handle); + if (ec != EXT2_ET_NO_CURRENT_NODE) + retval = ec; + } + + if (retval) + goto done; + } else if (logical == extent.e_lblk + extent.e_len - 1) { +#ifdef DEBUG + printf("(re/un)mapping last block in extent\n"); +#endif + if (physical) { + if (has_next && + (logical == (next_extent.e_lblk - 1)) && + (physical == (next_extent.e_pblk - 1)) && + (new_uninit == next_uninit) && + ((int) next_extent.e_len < max_len - 1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, &next_extent); + if (retval) + goto done; + next_extent.e_len++; + next_extent.e_lblk--; + next_extent.e_pblk--; + retval = ext2fs_extent_replace(handle, 0, + &next_extent); + if (retval) + goto done; + } else + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + /* Now pointing at inserted extent; move back to prev */ + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, + &extent); + if (retval) + goto done; + } + extent.e_len--; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else if (logical == extent.e_lblk) { +#ifdef DEBUG + printf("(re/un)mapping first block in extent\n"); +#endif + if (physical) { + if (has_prev && + (logical == (prev_extent.e_lblk + + prev_extent.e_len)) && + (physical == (prev_extent.e_pblk + + prev_extent.e_len)) && + (new_uninit == prev_uninit) && + ((int) prev_extent.e_len < max_len-1)) { + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_PREV_LEAF, &prev_extent); + if (retval) + goto done; + prev_extent.e_len++; + retval = ext2fs_extent_replace(handle, 0, + &prev_extent); + } else + retval = ext2fs_extent_insert(handle, + 0, &newextent); + if (retval) + goto done; + retval = ext2fs_extent_get(handle, + EXT2_EXTENT_NEXT_LEAF, + &extent); + if (retval) + goto done; + } + extent.e_pblk++; + extent.e_lblk++; + extent.e_len--; + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + } else { + __u32 orig_length; + +#ifdef DEBUG + printf("(re/un)mapping in middle of extent\n"); +#endif + /* need to split this extent; later */ + + orig_length = extent.e_len; + + /* shorten pre-split extent */ + extent.e_len = (logical - extent.e_lblk); + retval = ext2fs_extent_replace(handle, 0, &extent); + if (retval) + goto done; + /* insert our new extent, if any */ + if (physical) { + /* insert new extent after current */ + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + goto done; + } + /* add post-split extent */ + extent.e_pblk += extent.e_len + 1; + extent.e_lblk += extent.e_len + 1; + extent.e_len = orig_length - extent.e_len - 1; + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &extent); + if (retval) + goto done; + } + +done: + /* get handle back to its position */ + if (orig_height > handle->max_depth) + orig_height = handle->max_depth; /* In case we shortened the tree */ + extent_goto(handle, orig_height, orig_lblk); + return retval; +} + +errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags) +{ + struct extent_path *path; + char *cp; + struct ext3_extent_header *eh; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + if (!(handle->fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!handle->path) + return EXT2_ET_NO_CURRENT_NODE; + +#ifdef DEBUG + { + struct ext2fs_extent extent; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, + &extent); + if (retval == 0) { + printf("extent delete %u ", handle->ino); + dbg_print_extent(0, &extent); + } + } +#endif + + path = handle->path + handle->level; + if (!path->curr) + return EXT2_ET_NO_CURRENT_NODE; + + cp = path->curr; + + if (path->left) { + memmove(cp, cp + sizeof(struct ext3_extent_idx), + path->left * sizeof(struct ext3_extent_idx)); + path->left--; + } else { + struct ext3_extent_idx *ix = path->curr; + ix--; + path->curr = ix; + } + if (--path->entries == 0) + path->curr = 0; + + /* if non-root node has no entries left, remove it & parent ptr to it */ + if (path->entries == 0 && handle->level) { + if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) { + struct ext2fs_extent extent; + + retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, + &extent); + if (retval) + return retval; + + retval = ext2fs_extent_delete(handle, flags); + handle->inode->i_blocks -= handle->fs->blocksize / 512; + retval = ext2fs_write_inode(handle->fs, handle->ino, + handle->inode); + ext2fs_block_alloc_stats2(handle->fs, + extent.e_pblk, -1); + } + } else { + eh = (struct ext3_extent_header *) path->buf; + eh->eh_entries = ext2fs_cpu_to_le16(path->entries); + if ((path->entries == 0) && (handle->level == 0)) + eh->eh_depth = handle->max_depth = 0; + retval = update_path(handle); + } + return retval; +} + +errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, + struct ext2_extent_info *info) +{ + struct extent_path *path; + + EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); + + memset(info, 0, sizeof(struct ext2_extent_info)); + + path = handle->path + handle->level; + if (path) { + if (path->curr) + info->curr_entry = ((char *) path->curr - path->buf) / + sizeof(struct ext3_extent_idx); + else + info->curr_entry = 0; + info->num_entries = path->entries; + info->max_entries = path->max_entries; + info->bytes_avail = (path->max_entries - path->entries) * + sizeof(struct ext3_extent); + } + + info->curr_level = handle->level; + info->max_depth = handle->max_depth; + info->max_lblk = ((__u64) 1 << 32) - 1; + info->max_pblk = ((__u64) 1 << 48) - 1; + info->max_len = (1UL << 15); + info->max_uninit_len = (1UL << 15) - 1; + + return 0; +} + +#ifdef DEBUG + +#include "ss/ss.h" + +#include "debugfs.h" + +/* + * Hook in new commands into debugfs + */ +const char *debug_prog_name = "tst_extents"; +extern ss_request_table extent_cmds; +ss_request_table *extra_cmds = &extent_cmds; + +ext2_ino_t current_ino = 0; +ext2_extent_handle_t current_handle; + +int common_extent_args_process(int argc, char *argv[], int min_argc, + int max_argc, const char *cmd, + const char *usage, int flags) +{ + if (common_args_process(argc, argv, min_argc, max_argc, cmd, + usage, flags)) + return 1; + + if (!current_handle) { + com_err(cmd, 0, "Extent handle not open"); + return 1; + } + return 0; +} + +void do_inode(int argc, char *argv[]) +{ + ext2_ino_t inode; + int i; + struct ext3_extent_header *eh; + errcode_t retval; + + if (check_fs_open(argv[0])) + return; + + if (argc == 1) { + if (current_ino) + printf("Current inode is %d\n", current_ino); + else + printf("No current inode\n"); + return; + } + + if (common_inode_args_process(argc, argv, &inode, 0)) { + return; + } + + current_ino = 0; + + retval = ext2fs_extent_open(current_fs, inode, ¤t_handle); + if (retval) { + com_err(argv[1], retval, "while opening extent handle"); + return; + } + + current_ino = inode; + + printf("Loaded inode %d\n", current_ino); + + return; +} + +void generic_goto_node(char *cmd_name, int op) +{ + struct ext2fs_extent extent; + errcode_t retval; + + if (check_fs_open(cmd_name)) + return; + + if (!current_handle) { + com_err(cmd_name, 0, "Extent handle not open"); + return; + } + + retval = ext2fs_extent_get(current_handle, op, &extent); + if (retval) { + com_err(cmd_name, retval, 0); + return; + } + dbg_print_extent(0, &extent); +} + +void do_current_node(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); +} + +void do_root_node(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_ROOT); +} + +void do_last_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_LAST_LEAF); +} + +void do_first_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_FIRST_SIB); +} + +void do_last_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_LAST_SIB); +} + +void do_next_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT_SIB); +} + +void do_prev_sib(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV_SIB); +} + +void do_next_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT_LEAF); +} + +void do_prev_leaf(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV_LEAF); +} + +void do_next(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_NEXT); +} + +void do_prev(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_PREV); +} + +void do_up(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_UP); +} + +void do_down(int argc, char *argv[]) +{ + generic_goto_node(argv[0], EXT2_EXTENT_DOWN); +} + +void do_delete_node(int argc, char *argv[]) +{ + errcode_t retval; + int err; + + if (common_extent_args_process(argc, argv, 1, 1, "delete_node", + "", CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + retval = ext2fs_extent_delete(current_handle, 0); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + if (current_handle->path && current_handle->path[0].curr) + do_current_node(argc, argv); +} + +void do_replace_node(int argc, char *argv[]) +{ + const char *usage = "[--uninit] "; + errcode_t retval; + struct ext2fs_extent extent; + int err; + + if (common_extent_args_process(argc, argv, 3, 5, "replace_node", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + extent.e_flags = 0; + + if (!strcmp(argv[1], "--uninit")) { + argc--; + argv++; + extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + } + + if (argc != 4) { + fprintf(stderr, "Usage: %s %s\n", argv[0], usage); + return; + } + + extent.e_lblk = parse_ulong(argv[1], argv[0], "logical block", &err); + if (err) + return; + + extent.e_len = parse_ulong(argv[2], argv[0], "logical block", &err); + if (err) + return; + + extent.e_pblk = parse_ulong(argv[3], argv[0], "logical block", &err); + if (err) + return; + + retval = ext2fs_extent_replace(current_handle, 0, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_split_node(int argc, char *argv[]) +{ + errcode_t retval; + struct ext2fs_extent extent; + int err; + + if (common_extent_args_process(argc, argv, 1, 1, "split_node", + "", CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + retval = extent_node_split(current_handle); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_insert_node(int argc, char *argv[]) +{ + const char *usage = "[--after] [--uninit] "; + errcode_t retval; + struct ext2fs_extent extent; + char *cmd; + int err; + int flags = 0; + + if (common_extent_args_process(argc, argv, 3, 6, "insert_node", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + cmd = argv[0]; + + extent.e_flags = 0; + + while (argc > 2) { + if (!strcmp(argv[1], "--after")) { + argc--; + argv++; + flags |= EXT2_EXTENT_INSERT_AFTER; + continue; + } + if (!strcmp(argv[1], "--uninit")) { + argc--; + argv++; + extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; + continue; + } + break; + } + + if (argc != 4) { + fprintf(stderr, "usage: %s %s\n", cmd, usage); + return; + } + + extent.e_lblk = parse_ulong(argv[1], cmd, + "logical block", &err); + if (err) + return; + + extent.e_len = parse_ulong(argv[2], cmd, + "length", &err); + if (err) + return; + + extent.e_pblk = parse_ulong(argv[3], cmd, + "pysical block", &err); + if (err) + return; + + retval = ext2fs_extent_insert(current_handle, flags, &extent); + if (retval) { + com_err(cmd, retval, 0); + return; + } + do_current_node(argc, argv); +} + +void do_set_bmap(int argc, char **argv) +{ + const char *usage = "[--uninit] "; + errcode_t retval; + blk_t logical; + blk_t physical; + char *cmd = argv[0]; + int flags = 0; + int err; + + if (common_extent_args_process(argc, argv, 3, 5, "set_bmap", + usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) + return; + + if (argc > 2 && !strcmp(argv[1], "--uninit")) { + argc--; + argv++; + flags |= EXT2_EXTENT_SET_BMAP_UNINIT; + } + + if (argc != 3) { + fprintf(stderr, "Usage: %s %s\n", cmd, usage); + return; + } + + logical = parse_ulong(argv[1], cmd, + "logical block", &err); + if (err) + return; + + physical = parse_ulong(argv[2], cmd, + "physical block", &err); + if (err) + return; + + retval = ext2fs_extent_set_bmap(current_handle, logical, + (blk64_t) physical, flags); + if (retval) { + com_err(cmd, retval, 0); + return; + } + if (current_handle->path && current_handle->path[0].curr) + do_current_node(argc, argv); +} + +void do_print_all(int argc, char **argv) +{ + const char *usage = "[--leaf-only|--reverse|--reverse-leaf]"; + struct ext2fs_extent extent; + errcode_t retval; + errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT; + int op = EXT2_EXTENT_NEXT; + int first_op = EXT2_EXTENT_ROOT; + + + if (common_extent_args_process(argc, argv, 1, 2, "print_all", + usage, 0)) + return; + + if (argc == 2) { + if (!strcmp(argv[1], "--leaf-only")) + op = EXT2_EXTENT_NEXT_LEAF; + else if (!strcmp(argv[1], "--reverse")) { + op = EXT2_EXTENT_PREV; + first_op = EXT2_EXTENT_LAST_LEAF; + end_err = EXT2_ET_EXTENT_NO_PREV; + } else if (!strcmp(argv[1], "--reverse-leaf")) { + op = EXT2_EXTENT_PREV_LEAF; + first_op = EXT2_EXTENT_LAST_LEAF; + end_err = EXT2_ET_EXTENT_NO_PREV; + } else { + fprintf(stderr, "Usage: %s %s\n", argv[0], usage); + return; + } + } + + retval = ext2fs_extent_get(current_handle, first_op, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + dbg_print_extent(0, &extent); + + while (1) { + retval = ext2fs_extent_get(current_handle, op, &extent); + if (retval == end_err) + break; + + if (retval) { + com_err(argv[0], retval, 0); + return; + } + dbg_print_extent(0, &extent); + } +} + +void do_info(int argc, char **argv) +{ + struct ext2fs_extent extent; + struct ext2_extent_info info; + errcode_t retval; + + if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0)) + return; + + retval = ext2fs_extent_get_info(current_handle, &info); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + + retval = ext2fs_extent_get(current_handle, + EXT2_EXTENT_CURRENT, &extent); + if (retval) { + com_err(argv[0], retval, 0); + return; + } + + dbg_print_extent(0, &extent); + + printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n", + info.curr_entry, info.num_entries, info.max_entries, + info.bytes_avail, info.curr_level, info.max_depth); + printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk, + info.max_pblk); + printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len, + info.max_uninit_len); +} + +void do_goto_block(int argc, char **argv) +{ + struct ext2fs_extent extent; + errcode_t retval; + int op = EXT2_EXTENT_NEXT_LEAF; + blk64_t blk; + int level = 0, err; + + if (common_extent_args_process(argc, argv, 2, 3, "goto_block", + "block [level]", 0)) + return; + + if (strtoblk(argv[0], argv[1], &blk)) + return; + + if (argc == 3) { + level = parse_ulong(argv[2], argv[0], "level", &err); + if (err) + return; + } + + retval = extent_goto(current_handle, level, (blk64_t) blk); + + if (retval) { + com_err(argv[0], retval, + "while trying to go to block %llu, level %d", + (unsigned long long) blk, level); + return; + } + + generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); +} +#endif + diff --git a/lib/libext2fs/orig/fiemap.h b/lib/libext2fs/orig/fiemap.h new file mode 100644 index 0000000..30bf555 --- /dev/null +++ b/lib/libext2fs/orig/fiemap.h @@ -0,0 +1,68 @@ +/* + * FS_IOC_FIEMAP ioctl infrastructure. + * + * Some portions copyright (C) 2007 Cluster File Systems, Inc + * + * Authors: Mark Fasheh + * Kalpak Shah + * Andreas Dilger + */ + +#ifndef _LINUX_FIEMAP_H +#define _LINUX_FIEMAP_H + +struct fiemap_extent { + __u64 fe_logical; /* logical offset in bytes for the start of + * the extent from the beginning of the file */ + __u64 fe_physical; /* physical offset in bytes for the start + * of the extent from the beginning of the disk */ + __u64 fe_length; /* length in bytes for this extent */ + __u64 fe_reserved64[2]; + __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ + __u32 fe_reserved[3]; +}; + +struct fiemap { + __u64 fm_start; /* logical offset (inclusive) at + * which to start mapping (in) */ + __u64 fm_length; /* logical length of mapping which + * userspace wants (in) */ + __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ + __u32 fm_mapped_extents;/* number of extents that were mapped (out) */ + __u32 fm_extent_count; /* size of fm_extents array (in) */ + __u32 fm_reserved; + struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ +}; + +#ifndef FS_IOC_FIEMAP +#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) +#endif + +#define FIEMAP_MAX_OFFSET (~0ULL) + +#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ +#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ + +#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) + +#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ +#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ +#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. + * Sets EXTENT_UNKNOWN. */ +#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read + * while fs is unmounted */ +#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. + * Sets EXTENT_NO_BYPASS. */ +#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be + * block aligned. */ +#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. + * Sets EXTENT_NOT_ALIGNED.*/ +#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but + * no data (i.e. zero). */ +#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively + * support extents. Result + * merged for efficiency. */ + +#endif /* _LINUX_FIEMAP_H */ diff --git a/lib/libext2fs/orig/fileio.c b/lib/libext2fs/orig/fileio.c new file mode 100644 index 0000000..44c859b --- /dev/null +++ b/lib/libext2fs/orig/fileio.c @@ -0,0 +1,412 @@ +/* + * fileio.c --- Simple file I/O routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct ext2_file { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + __u64 pos; + blk64_t blockno; + blk64_t physblock; + char *buf; +}; + +#define BMAP_BUFFER (file->buf + fs->blocksize) + +errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret) +{ + ext2_file_t file; + errcode_t retval; + + /* + * Don't let caller create or open a file for writing if the + * filesystem is read-only. + */ + if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && + !(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); + if (retval) + return retval; + + memset(file, 0, sizeof(struct ext2_file)); + file->magic = EXT2_ET_MAGIC_EXT2_FILE; + file->fs = fs; + file->ino = ino; + file->flags = flags & EXT2_FILE_MASK; + + if (inode) { + memcpy(&file->inode, inode, sizeof(struct ext2_inode)); + } else { + retval = ext2fs_read_inode(fs, ino, &file->inode); + if (retval) + goto fail; + } + + retval = ext2fs_get_array(3, fs->blocksize, &file->buf); + if (retval) + goto fail; + + *ret = file; + return 0; + +fail: + if (file->buf) + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + return retval; +} + +errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret) +{ + return ext2fs_file_open2(fs, ino, NULL, flags, ret); +} + +/* + * This function returns the filesystem handle of a file from the structure + */ +ext2_filsys ext2fs_file_get_fs(ext2_file_t file) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return 0; + return file->fs; +} + +/* + * This function returns the pointer to the inode of a file from the structure + */ +struct ext2_inode *ext2fs_file_get_inode(ext2_file_t file) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return NULL; + return &file->inode; +} + +/* + * This function flushes the dirty block buffer out to disk if + * necessary. + */ +errcode_t ext2fs_file_flush(ext2_file_t file) +{ + errcode_t retval; + ext2_filsys fs; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_BUF_VALID) || + !(file->flags & EXT2_FILE_BUF_DIRTY)) + return 0; + + // Flushing out the new size - Dimok + ext2fs_write_inode(file->fs, file->ino, &file->inode); + + /* + * OK, the physical block hasn't been allocated yet. + * Allocate it. + */ + if (!file->physblock) { + retval = ext2fs_bmap2(fs, file->ino, &file->inode, + BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, + file->blockno, 0, &file->physblock); + if (retval) + return retval; + } + + retval = io_channel_write_blk(fs->io, file->physblock, + 1, file->buf); + if (retval) + return retval; + + file->flags &= ~EXT2_FILE_BUF_DIRTY; + + return retval; +} + +/* + * This function synchronizes the file's block buffer and the current + * file position, possibly invalidating block buffer if necessary + */ +static errcode_t sync_buffer_position(ext2_file_t file) +{ + blk_t b; + errcode_t retval; + + b = file->pos / file->fs->blocksize; + if (b != file->blockno) { + retval = ext2fs_file_flush(file); + if (retval) + return retval; + file->flags &= ~EXT2_FILE_BUF_VALID; + } + file->blockno = b; + return 0; +} + +/* + * This function loads the file's block buffer with valid data from + * the disk as necessary. + * + * If dontfill is true, then skip initializing the buffer since we're + * going to be replacing its entire contents anyway. If set, then the + * function basically only sets file->physblock and EXT2_FILE_BUF_VALID + */ +#define DONTFILL 1 +static errcode_t load_buffer(ext2_file_t file, int dontfill) +{ + ext2_filsys fs = file->fs; + errcode_t retval; + + if (!(file->flags & EXT2_FILE_BUF_VALID)) { + retval = ext2fs_bmap2(fs, file->ino, &file->inode, + BMAP_BUFFER, 0, file->blockno, 0, + &file->physblock); + if (retval) + return retval; + if (!dontfill) { + if (file->physblock) { + retval = io_channel_read_blk(fs->io, + file->physblock, + 1, file->buf); + if (retval) + return retval; + } else + memset(file->buf, 0, fs->blocksize); + } + file->flags |= EXT2_FILE_BUF_VALID; + } + return 0; +} + + +errcode_t ext2fs_file_close(ext2_file_t file) +{ + errcode_t retval; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + retval = ext2fs_file_flush(file); + + if (file->buf) + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + + return retval; +} + + +errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + __u64 left; + char *ptr = (char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + retval = load_buffer(file, 0); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > wanted) + c = wanted; + left = EXT2_I_SIZE(&file->inode) - file->pos ; + if (c > left) + c = left; + + memcpy(ptr, file->buf+start, c); + file->pos += c; + ptr += c; + count += c; + wanted -= c; + } + +fail: + if (got) + *got = count; + return retval; +} + + +errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + const char *ptr = (const char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_WRITE)) + return EXT2_ET_FILE_RO; + + while (nbytes > 0) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > nbytes) + c = nbytes; + + /* + * We only need to do a read-modify-update cycle if + * we're doing a partial write. + */ + retval = load_buffer(file, (c == fs->blocksize)); + if (retval) + goto fail; + + file->flags |= EXT2_FILE_BUF_DIRTY; + memcpy(file->buf+start, ptr, c); + file->pos += c; + ptr += c; + count += c; + nbytes -= c; + } + + // I don't see why changing size is my duty - Dimok + if(EXT2_I_SIZE(&file->inode) < file->pos) + { + file->inode.i_size = file->pos & 0xFFFFFFFF; + file->inode.i_size_high = (file->pos >> 32) & 0xFFFFFFFF; + } + +fail: + if (written) + *written = count; + return retval; +} + +errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos) +{ + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + if (whence == EXT2_SEEK_SET) + file->pos = offset; + else if (whence == EXT2_SEEK_CUR) + file->pos += offset; + else if (whence == EXT2_SEEK_END) + file->pos = EXT2_I_SIZE(&file->inode) + offset; + else + return EXT2_ET_INVALID_ARGUMENT; + + if (ret_pos) + *ret_pos = file->pos; + + return 0; +} + +errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos) +{ + __u64 loffset, ret_loffset = 0; + errcode_t retval; + + loffset = offset; + retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); + if (ret_pos) + *ret_pos = (ext2_off_t) ret_loffset; + return retval; +} + + +/* + * This function returns the size of the file, according to the inode + */ +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return EXT2_ET_MAGIC_EXT2_FILE; + *ret_size = EXT2_I_SIZE(&file->inode); + return 0; +} + +/* + * This function returns the size of the file, according to the inode + */ +ext2_off_t ext2fs_file_get_size(ext2_file_t file) +{ + __u64 size; + + if (ext2fs_file_get_lsize(file, &size)) + return 0; + if ((size >> 32) != 0) + return 0; + return size; +} + +/* + * This function sets the size of the file, truncating it if necessary + * + */ +errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size) +{ + ext2_off64_t old_size; + errcode_t retval; + blk64_t old_truncate, truncate_block; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + truncate_block = ((size + file->fs->blocksize - 1) >> + EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + old_size = file->inode.i_size + + (((blk64_t) file->inode.i_size_high) << 32); + old_truncate = ((old_size + file->fs->blocksize - 1) >> + EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1; + + file->inode.i_size = size & 0xffffffff; + file->inode.i_size_high = (size >> 32); + if (file->ino) { + retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); + if (retval) + return retval; + } + + if (truncate_block <= old_truncate) + return 0; + + return ext2fs_punch(file->fs, file->ino, &file->inode, 0, + truncate_block, ~0ULL); +} + +errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) +{ + return ext2fs_file_set_size2(file, size); +} diff --git a/lib/libext2fs/orig/finddev.c b/lib/libext2fs/orig/finddev.c new file mode 100644 index 0000000..cc2029f --- /dev/null +++ b/lib/libext2fs/orig/finddev.c @@ -0,0 +1,208 @@ +/* + * finddev.c -- this routine attempts to find a particular device in + * /dev + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#include +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_SYS_MKDEV_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct dir_list { + char *name; + struct dir_list *next; +}; + +/* + * This function adds an entry to the directory list + */ +static void add_to_dirlist(const char *name, struct dir_list **list) +{ + struct dir_list *dp; + + dp = malloc(sizeof(struct dir_list)); + if (!dp) + return; + dp->name = malloc(strlen(name)+1); + if (!dp->name) { + free(dp); + return; + } + strcpy(dp->name, name); + dp->next = *list; + *list = dp; +} + +/* + * This function frees a directory list + */ +static void free_dirlist(struct dir_list **list) +{ + struct dir_list *dp, *next; + + for (dp = *list; dp; dp = next) { + next = dp->next; + free(dp->name); + free(dp); + } + *list = 0; +} + +static int scan_dir(char *dirname, dev_t device, struct dir_list **list, + char **ret_path) +{ + DIR *dir; + struct dirent *dp; + char path[1024], *cp; + int dirlen; + struct stat st; + + dirlen = strlen(dirname); + if ((dir = opendir(dirname)) == NULL) + return errno; + dp = readdir(dir); + while (dp) { + if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) + goto skip_to_next; + if (dp->d_name[0] == '.' && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) + goto skip_to_next; + sprintf(path, "%s/%s", dirname, dp->d_name); + if (stat(path, &st) < 0) + goto skip_to_next; + if (S_ISDIR(st.st_mode)) + add_to_dirlist(path, list); + if (S_ISBLK(st.st_mode) && st.st_rdev == device) { + cp = malloc(strlen(path)+1); + if (!cp) { + closedir(dir); + return ENOMEM; + } + strcpy(cp, path); + *ret_path = cp; + goto success; + } + skip_to_next: + dp = readdir(dir); + } +success: + closedir(dir); + return 0; +} + +/* + * This function finds the pathname to a block device with a given + * device number. It returns a pointer to allocated memory to the + * pathname on success, and NULL on failure. + */ +char *ext2fs_find_block_device(dev_t device) +{ + struct dir_list *list = 0, *new_list = 0; + struct dir_list *current; + char *ret_path = 0; + + /* + * Add the starting directories to search... + */ + add_to_dirlist("/devices", &list); + add_to_dirlist("/devfs", &list); + add_to_dirlist("/dev", &list); + + while (list) { + current = list; + list = list->next; +#ifdef DEBUG + printf("Scanning directory %s\n", current->name); +#endif + scan_dir(current->name, device, &new_list, &ret_path); + free(current->name); + free(current); + if (ret_path) + break; + /* + * If we're done checking at this level, descend to + * the next level of subdirectories. (breadth-first) + */ + if (list == 0) { + list = new_list; + new_list = 0; + } + } + free_dirlist(&list); + free_dirlist(&new_list); + return ret_path; +} + + +#ifdef DEBUG +int main(int argc, char** argv) +{ + char *devname, *tmp; + int major, minor; + dev_t device; + const char *errmsg = "Couldn't parse %s: %s\n"; + + if ((argc != 2) && (argc != 3)) { + fprintf(stderr, "Usage: %s device_number\n", argv[0]); + fprintf(stderr, "\t: %s major minor\n", argv[0]); + exit(1); + } + if (argc == 2) { + device = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "device number", argv[1]); + exit(1); + } + } else { + major = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "major number", argv[1]); + exit(1); + } + minor = strtoul(argv[2], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "minor number", argv[2]); + exit(1); + } + device = makedev(major, minor); + printf("Looking for device 0x%04x (%d:%d)\n", device, + major, minor); + } + devname = ext2fs_find_block_device(device); + if (devname) { + printf("Found device! %s\n", devname); + free(devname); + } else { + printf("Couldn't find device.\n"); + } + return 0; +} + +#endif diff --git a/lib/libext2fs/orig/flushb.c b/lib/libext2fs/orig/flushb.c new file mode 100644 index 0000000..b6f161b --- /dev/null +++ b/lib/libext2fs/orig/flushb.c @@ -0,0 +1,74 @@ +/* + * flushb.c --- Hides system-dependent information for both syncing a + * device to disk and to flush any buffers from disk cache. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#include /* This may define BLKFLSBUF */ +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since + * not all portable header file does so for us. This really should be + * fixed in the glibc header files. (Recent glibcs appear to define + * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be + * defined anywhere portable.) Until then.... + */ +#ifdef __linux__ +#ifndef BLKFLSBUF +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#endif +#ifndef FDFLUSH +#define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ +#endif +#endif + +/* + * This function will sync a device/file, and optionally attempt to + * flush the buffer cache. The latter is basically only useful for + * system benchmarks and for torturing systems in burn-in tests. :) + */ +errcode_t ext2fs_sync_device(int fd, int flushb) +{ + /* + * We always sync the device in case we're running on old + * kernels for which we can lose data if we don't. (There + * still is a race condition for those kernels, but this + * reduces it greatly.) + */ + if (fsync (fd) == -1) + return errno; + + if (flushb) { + +#ifdef BLKFLSBUF + if (ioctl (fd, BLKFLSBUF, 0) == 0) + return 0; +#endif +#ifdef FDFLUSH + ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ +#endif + } + return 0; +} diff --git a/lib/libext2fs/orig/freefs.c b/lib/libext2fs/orig/freefs.c new file mode 100644 index 0000000..5c35bb6 --- /dev/null +++ b/lib/libext2fs/orig/freefs.c @@ -0,0 +1,112 @@ +/* + * freefs.c --- free an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); + +void ext2fs_free(ext2_filsys fs) +{ + if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) + return; + if (fs->image_io != fs->io) { + if (fs->image_io) + io_channel_close(fs->image_io); + } + if (fs->io) { + io_channel_close(fs->io); + } + if (fs->device_name) + ext2fs_free_mem(&fs->device_name); + if (fs->super) + ext2fs_free_mem(&fs->super); + if (fs->orig_super) + ext2fs_free_mem(&fs->orig_super); + if (fs->group_desc) + ext2fs_free_mem(&fs->group_desc); + if (fs->block_map) + ext2fs_free_block_bitmap(fs->block_map); + if (fs->inode_map) + ext2fs_free_inode_bitmap(fs->inode_map); + + if (fs->badblocks) + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + + if (fs->dblist) + ext2fs_free_dblist(fs->dblist); + + if (fs->icache) + ext2fs_free_inode_cache(fs->icache); + + fs->magic = 0; + + ext2fs_free_mem(&fs); +} + +/* + * Free the inode cache structure + */ +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) +{ + if (--icache->refcount) + return; + if (icache->buffer) + ext2fs_free_mem(&icache->buffer); + if (icache->cache) + ext2fs_free_mem(&icache->cache); + icache->buffer_blk = 0; + ext2fs_free_mem(&icache); +} + +/* + * This procedure frees a badblocks list. + */ +void ext2fs_u32_list_free(ext2_u32_list bb) +{ + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return; + + if (bb->list) + ext2fs_free_mem(&bb->list); + bb->list = 0; + ext2fs_free_mem(&bb); +} + +void ext2fs_badblocks_list_free(ext2_badblocks_list bb) +{ + ext2fs_u32_list_free((ext2_u32_list) bb); +} + + +/* + * Free a directory block list + */ +void ext2fs_free_dblist(ext2_dblist dblist) +{ + if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) + return; + + if (dblist->list) + ext2fs_free_mem(&dblist->list); + dblist->list = 0; + if (dblist->fs && dblist->fs->dblist == dblist) + dblist->fs->dblist = 0; + dblist->magic = 0; + ext2fs_free_mem(&dblist); +} + diff --git a/lib/libext2fs/orig/gekko_io.c b/lib/libext2fs/orig/gekko_io.c new file mode 100644 index 0000000..97da1d9 --- /dev/null +++ b/lib/libext2fs/orig/gekko_io.c @@ -0,0 +1,561 @@ +/** + * gekko_io.c - Gekko style disk io functions. + * + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gekko_io.h" +#include "bitops.h" +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_internal.h" +#include "disc_cache.h" +#include "mem_allocate.h" + +#define DEV_FD(dev) ((gekko_fd *) dev->private_data) + +/* Prototypes */ +static s64 device_gekko_io_readbytes(io_channel dev, s64 offset, s64 count, void *buf); +static bool device_gekko_io_readsectors(io_channel dev, sec_t sector, sec_t numSectors, void* buffer); +static s64 device_gekko_io_writebytes(io_channel dev, s64 offset, s64 count, const void *buf); +static bool device_gekko_io_writesectors(io_channel dev, sec_t sector, sec_t numSectors, const void* buffer); + +/** + * + */ +static errcode_t device_gekko_io_open(const char *name, int flags, io_channel *dev) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD((*dev)); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + // Start the device interface and ensure that it is inserted + if (!interface->startup()) { + ext2_log_trace("device failed to start\n"); + errno = EIO; + return -1; + } + if (!interface->isInserted()) { + ext2_log_trace("device media is not inserted\n"); + errno = EIO; + return -1; + } + + struct ext2_super_block * super = (struct ext2_super_block *) mem_alloc(SUPERBLOCK_SIZE); //1024 bytes + if(!super) + { + ext2_log_trace("no memory for superblock"); + errno = ENOMEM; + return -1; + } + + // Check that there is a valid EXT boot sector at the start of the device + if (!interface->readSectors(fd->startSector+SUPERBLOCK_OFFSET/BYTES_PER_SECTOR, SUPERBLOCK_SIZE/BYTES_PER_SECTOR, super)) + { + ext2_log_trace("read failure @ sector %d\n", fd->startSector); + errno = EROFS; + mem_free(super); + return -1; + } + + if(ext2fs_le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) + { + mem_free(super); + errno = EROFS; + return -1; + } + + // Parse the boot sector + fd->sectorSize = BYTES_PER_SECTOR; + fd->offset = 0; + fd->sectorCount = 0; + + switch(ext2fs_le32_to_cpu(super->s_log_block_size)) + { + case 1: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 2048 / (u64) BYTES_PER_SECTOR); + break; + case 2: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 4096 / (u64) BYTES_PER_SECTOR); + break; + case 3: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 8192 / (u64) BYTES_PER_SECTOR); + break; + default: + case 0: + fd->sectorCount = (sec_t) ((u64) ext2fs_le32_to_cpu(super->s_blocks_count) * (u64) 1024 / (u64) BYTES_PER_SECTOR); + break; + } + + mem_free(super); + + // Create the cache + fd->cache = cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); + + return 0; +} + +/** + * Flush data out and close volume + */ +static errcode_t device_gekko_io_close(io_channel dev) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + if(!(dev->flags & EXT2_FLAG_RW)) + return 0; + + // Flush and destroy the cache (if required) + if (fd->cache) { + cache_flush(fd->cache); + cache_destructor(fd->cache); + } + + return 0; +} + +/** + * + */ +static s64 device_gekko_io_readbytes(io_channel dev, s64 offset, s64 count, void *buf) +{ + ext2_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(offset < 0) + { + errno = EROFS; + return -1; + } + + if(!count) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this read + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // Don't read over the partitions limit + if(sec_start+sec_count > fd->startSector+fd->sectorCount) + { + ext2_log_trace("Error: read requested up to sector %lli while partition goes up to %lli\n", (s64) (sec_start+sec_count), (s64) (fd->startSector+fd->sectorCount)); + errno = EROFS; + return -1; + } + + // If this read happens to be on the sector boundaries then do the read straight into the destination buffer + + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Read from the device + ext2_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) + { + ext2_log_trace("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else read into a buffer and copy over only what was requested + } + else + { + + // Allocate a buffer to hold the read data + buffer = (u8*)mem_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + + // Read from the device + ext2_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); + ext2_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); + if (!device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { + ext2_log_trace("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + mem_free(buffer); + errno = EIO; + return -1; + } + + // Copy what was requested to the destination buffer + memcpy(buf, buffer + buffer_offset, count); + mem_free(buffer); + + } + + return count; +} + +/** + * + */ +static s64 device_gekko_io_writebytes(io_channel dev, s64 offset, s64 count, const void *buf) +{ + ext2_log_trace("dev %p, offset %lli, count %lli\n", dev, offset, count); + + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + if(!(dev->flags & EXT2_FLAG_RW)) + return -1; + + // Get the device interface + const DISC_INTERFACE* interface = fd->interface; + if (!interface) { + errno = ENODEV; + return -1; + } + + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) + return 0; + + sec_t sec_start = (sec_t) fd->startSector; + sec_t sec_count = 1; + u32 buffer_offset = (u32) (offset % fd->sectorSize); + u8 *buffer = NULL; + + // Determine the range of sectors required for this write + if (offset > 0) { + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); + } + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); + } + + // Don't write over the partitions limit + if(sec_start+sec_count > fd->startSector+fd->sectorCount) + { + ext2_log_trace("Error: write requested up to sector %lli while partition goes up to %lli\n", (s64) (sec_start+sec_count), (s64) (fd->startSector+fd->sectorCount)); + errno = EROFS; + return -1; + } + + // If this write happens to be on the sector boundaries then do the write straight to disc + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { + // Write to the device + ext2_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { + ext2_log_trace("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); + errno = EIO; + return -1; + } + // Else write from a buffer aligned to the sector boundaries + } + else + { + // Allocate a buffer to hold the write data + buffer = (u8 *) mem_alloc(sec_count * fd->sectorSize); + if (!buffer) { + errno = ENOMEM; + return -1; + } + // Read the first and last sectors of the buffer from disc (if required) + // NOTE: This is done because the data does not line up with the sector boundaries, + // we just read in the buffer edges where the data overlaps with the rest of the disc + if(buffer_offset != 0) + { + if (!device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ext2_log_trace("read failure @ sector %d\n", sec_start); + mem_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ext2_log_trace("read failure @ sector %d\n", sec_start + sec_count - 1); + mem_free(buffer); + errno = EIO; + return -1; + } + } + + // Copy the data into the write buffer + memcpy(buffer + buffer_offset, buf, count); + + // Write to the device + ext2_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); + if (!device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) { + ext2_log_trace("buffered write failure @ sector %d\n", sec_start); + mem_free(buffer); + errno = EIO; + return -1; + } + + // Free the buffer + mem_free(buffer); + } + + return count; +} + + +/** + * Read function wrap for I/O manager + */ +static errcode_t device_gekko_io_read64(io_channel dev, unsigned long long block, int count, void *buf) +{ + gekko_fd *fd = DEV_FD(dev); + s64 size = (count < 0) ? -count : count * dev->block_size; + fd->io_stats.bytes_read += size; + ext2_loff_t location = ((ext2_loff_t) block * dev->block_size) + fd->offset; + + s64 read = device_gekko_io_readbytes(dev, location, size, buf); + if(read != size) + return EXT2_ET_SHORT_READ; + else if(read < 0) + return EXT2_ET_BLOCK_BITMAP_READ; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_io_read(io_channel dev, unsigned long block, int count, void *buf) +{ + return device_gekko_io_read64(dev, block, count, buf); +} + +/** + * Write function wrap for I/O manager + */ +static errcode_t device_gekko_io_write64(io_channel dev, unsigned long long block, int count, const void *buf) +{ + gekko_fd *fd = DEV_FD(dev); + s64 size = (count < 0) ? -count : count * dev->block_size; + fd->io_stats.bytes_written += size; + + ext2_loff_t location = ((ext2_loff_t) block * dev->block_size) + fd->offset; + + s64 writen = device_gekko_io_writebytes(dev, location, size, buf); + if(writen != size) + return EXT2_ET_SHORT_WRITE; + else if(writen < 0) + return EXT2_ET_BLOCK_BITMAP_WRITE; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_io_write(io_channel dev, unsigned long block, int count, const void *buf) +{ + return device_gekko_io_write64(dev, block, count, buf); +} + + +static bool device_gekko_io_readsectors(io_channel dev, sec_t sector, sec_t numSectors, void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + // Read the sectors from disc (or cache, if enabled) + if (fd->cache) + return cache_readSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->readSectors(sector, numSectors, buffer); + + return false; +} + +static bool device_gekko_io_writesectors(io_channel dev, sec_t sector, sec_t numSectors, const void* buffer) +{ + // Get the device driver descriptor + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return false; + } + + // Write the sectors to disc (or cache, if enabled) + if (fd->cache) + return cache_writeSectors(fd->cache, sector, numSectors, buffer); + else + return fd->interface->writeSectors(sector, numSectors, buffer); + + return false; +} + +/** + * + */ +static errcode_t device_gekko_io_sync(io_channel dev) +{ + gekko_fd *fd = DEV_FD(dev); + ext2_log_trace("dev %p\n", dev); + + // Check that the device can be written to + if(!(dev->flags & EXT2_FLAG_RW)) + return -1; + + // Flush any sectors in the disc cache (if required) + if (fd->cache) { + if (!cache_flush(fd->cache)) { + errno = EIO; + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + } + + return EXT2_ET_OK; +} + +/** + * + */ +static errcode_t device_gekko_io_stat(io_channel dev, io_stats *stats) +{ + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + gekko_fd *fd = DEV_FD(dev); + + if (stats) + *stats = &fd->io_stats; + + return EXT2_ET_OK; +} + +static errcode_t device_gekko_set_blksize(io_channel dev, int blksize) +{ + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + + if (dev->block_size != blksize) + { + dev->block_size = blksize; + + return device_gekko_io_sync(dev); + } + + return EXT2_ET_OK; +} + +/** + * Set options. + */ +static errcode_t device_gekko_set_option(io_channel dev, const char *option, const char *arg) +{ + unsigned long long tmp; + char *end; + + gekko_fd *fd = DEV_FD(dev); + if (!fd) { + errno = EBADF; + return -1; + } + + EXT2_CHECK_MAGIC(dev, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!strcmp(option, "offset")) { + if (!arg) + return EXT2_ET_INVALID_ARGUMENT; + + tmp = strtoull(arg, &end, 0); + if (*end) + return EXT2_ET_INVALID_ARGUMENT; + fd->offset = tmp; + if (fd->offset < 0) + return EXT2_ET_INVALID_ARGUMENT; + return 0; + } + return EXT2_ET_INVALID_ARGUMENT; +} + +static errcode_t device_gekko_discard(io_channel channel, unsigned long long block, unsigned long long count) +{ + //!TODO as soon as it is implemented in the official lib + return 0; +} + +/** + * Device operations for working with gekko style devices and files. + */ +const struct struct_io_manager struct_gekko_io_manager = +{ + EXT2_ET_MAGIC_IO_MANAGER, + "Wii/GC I/O Manager", + device_gekko_io_open, + device_gekko_io_close, + device_gekko_set_blksize, + device_gekko_io_read, + device_gekko_io_write, + device_gekko_io_sync, + 0, + device_gekko_set_option, + device_gekko_io_stat, + device_gekko_io_read64, + device_gekko_io_write64, + device_gekko_discard, +}; + +io_manager gekko_io_manager = (io_manager) &struct_gekko_io_manager; diff --git a/lib/libext2fs/orig/gekko_io.h b/lib/libext2fs/orig/gekko_io.h new file mode 100644 index 0000000..16786c7 --- /dev/null +++ b/lib/libext2fs/orig/gekko_io.h @@ -0,0 +1,53 @@ +/* +* gekko_io.h - Platform specifics for device io. +* + * Copyright (c) 2009 Rhys "Shareese" Koedijk + * Copyright (c) 2010 Dimok +* +* This program/include file is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published +* by the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program/include file is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _GEKKO_IO_H +#define _GEKKO_IO_H + +#include +#include +#include "disc_cache.h" +#include "ext2fs.h" + +#define BYTES_PER_SECTOR 512 + +/** + * gekko_fd - Gekko device driver descriptor + */ +typedef struct _gekko_fd { + const DISC_INTERFACE* interface; /* Device disc interface */ + sec_t startSector; /* LBA of partition start */ + sec_t sectorCount; /* Total number of sectors in partition */ + u16 sectorSize; /* Device sector size (in bytes) */ + int flags; + int access_time; + ext2_loff_t offset; + struct struct_io_stats io_stats; + CACHE *cache; /* Cache */ + u32 cachePageCount; /* The number of pages in the cache */ + u32 cachePageSize; /* The number of sectors per cache page */ +} gekko_fd; + + +/* Gekko device driver i/o operations */ +extern io_manager gekko_io_manager; + +#endif /* _GEKKO_IO_H */ diff --git a/lib/libext2fs/orig/gen_bitmap.c b/lib/libext2fs/orig/gen_bitmap.c new file mode 100644 index 0000000..2ef1d82 --- /dev/null +++ b/lib/libext2fs/orig/gen_bitmap.c @@ -0,0 +1,560 @@ +/* + * gen_bitmap.c --- Generic (32-bit) bitmap routines + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2fsP.h" + +struct ext2fs_struct_generic_bitmap { + errcode_t magic; + ext2_filsys fs; + __u32 start, end; + __u32 real_end; + char * description; + char * bitmap; + errcode_t base_error_code; + __u32 reserved[7]; +}; + +#define EXT2FS_IS_32_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP)) + +#define EXT2FS_IS_64_BITMAP(bmap) \ + (((bmap)->magic == EXT2_ET_MAGIC_GENERIC_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_BLOCK_BITMAP64) || \ + ((bmap)->magic == EXT2_ET_MAGIC_INODE_BITMAP64)) + +/* + * Used by previously inlined function, so we have to export this and + * not change the function signature + */ +void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg) +{ +#ifndef OMIT_COM_ERR + if (bitmap->description) + com_err(0, bitmap->base_error_code+code, + "#%lu for %s", arg, bitmap->description); + else + com_err(0, bitmap->base_error_code + code, "#%lu", arg); +#endif +} + +static errcode_t check_magic(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap || !((bitmap->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || + (bitmap->magic == EXT2_ET_MAGIC_INODE_BITMAP) || + (bitmap->magic == EXT2_ET_MAGIC_BLOCK_BITMAP))) + return EXT2_ET_MAGIC_GENERIC_BITMAP; + return 0; +} + +errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, + __u32 start, __u32 end, __u32 real_end, + const char *descr, char *init_map, + ext2fs_generic_bitmap *ret) +{ + ext2fs_generic_bitmap bitmap; + errcode_t retval; + size_t size; + + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &bitmap); + if (retval) + return retval; + + bitmap->magic = magic; + bitmap->fs = fs; + bitmap->start = start; + bitmap->end = end; + bitmap->real_end = real_end; + switch (magic) { + case EXT2_ET_MAGIC_INODE_BITMAP: + bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; + break; + case EXT2_ET_MAGIC_BLOCK_BITMAP: + bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; + break; + default: + bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; + } + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); + if (retval) { + ext2fs_free_mem(&bitmap); + return retval; + } + strcpy(bitmap->description, descr); + } else + bitmap->description = 0; + + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + /* Round up to allow for the BT x86 instruction */ + size = (size + 7) & ~3; + retval = ext2fs_get_mem(size, &bitmap->bitmap); + if (retval) { + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap); + return retval; + } + + if (init_map) + memcpy(bitmap->bitmap, init_map, size); + else + memset(bitmap->bitmap, 0, size); + *ret = bitmap; + return 0; +} + +errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret) +{ + return ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_GENERIC_BITMAP, 0, + start, end, real_end, descr, 0, ret); +} + +errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + return (ext2fs_make_generic_bitmap(src->magic, src->fs, + src->start, src->end, + src->real_end, + src->description, src->bitmap, + dest)); +} + +void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) +{ + if (check_magic(bitmap)) + return; + + bitmap->magic = 0; + if (bitmap->description) { + ext2fs_free_mem(&bitmap->description); + bitmap->description = 0; + } + if (bitmap->bitmap) { + ext2fs_free_mem(&bitmap->bitmap); + bitmap->bitmap = 0; + } + ext2fs_free_mem(&bitmap); +} + +int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_test_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "test_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); + return 0; + } + return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_mark_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "mark_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); + return 0; + } + return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_unmark_generic_bmap(bitmap, bitno); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "mark_bitmap(%lu)", (unsigned long) bitno); + return 0; +#endif + } + + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); + return 0; + } + return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); +} + +__u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_get_generic_bmap_start(bitmap); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "get_bitmap_start"); + return 0; +#endif + } + + return bitmap->start; +} + +__u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + return ext2fs_get_generic_bmap_end(bitmap); + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "get_bitmap_end"); + return 0; +#endif + } + return bitmap->end; +} + +void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap) +{ + if (!EXT2FS_IS_32_BITMAP(bitmap)) { + if (EXT2FS_IS_64_BITMAP(bitmap)) { + ext2fs_warn_bitmap32(bitmap, __func__); + ext2fs_clear_generic_bmap(bitmap); + return; + } +#ifndef OMIT_COM_ERR + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "clear_generic_bitmap"); + return; +#endif + } + + memset(bitmap->bitmap, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} + +errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, + errcode_t magic, errcode_t neq, + ext2_ino_t end, ext2_ino_t *oend) +{ + EXT2_CHECK_MAGIC(bitmap, magic); + + if (end > bitmap->real_end) + return neq; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, + __u32 new_end, __u32 new_real_end, + ext2fs_generic_bitmap bmap) +{ + errcode_t retval; + size_t size, new_size; + __u32 bitno; + + if (!bmap || (bmap->magic != magic)) + return magic; + + /* + * If we're expanding the bitmap, make sure all of the new + * parts of the bitmap are zero. + */ + if (new_end > bmap->end) { + bitno = bmap->real_end; + if (bitno > new_end) + bitno = new_end; + for (; bitno > bmap->end; bitno--) + ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); + } + if (new_real_end == bmap->real_end) { + bmap->end = new_end; + return 0; + } + + size = ((bmap->real_end - bmap->start) / 8) + 1; + new_size = ((new_real_end - bmap->start) / 8) + 1; + + if (size != new_size) { + retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); + if (retval) + return retval; + } + if (new_size > size) + memset(bmap->bitmap + size, 0, new_size - size); + + bmap->end = new_end; + bmap->real_end = new_real_end; + return 0; +} + +errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2) +{ + blk_t i; + + if (!bm1 || bm1->magic != magic) + return magic; + if (!bm2 || bm2->magic != magic) + return magic; + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end) || + (memcmp(bm1->bitmap, bm2->bitmap, + (size_t) (bm1->end - bm1->start)/8))) + return neq; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_fast_test_block_bitmap(bm1, i) != + ext2fs_fast_test_block_bitmap(bm2, i)) + return neq; + + return 0; +} + +void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map) +{ + __u32 i, j; + + /* Protect loop from wrap-around if map->real_end is maxed */ + for (i=map->end+1, j = i - map->start; + i <= map->real_end && i > map->end; + i++, j++) + ext2fs_set_bit(j, map->bitmap); +} + +errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *out) +{ + if (!bmap || (bmap->magic != magic)) + return magic; + + if ((start < bmap->start) || (start+num-1 > bmap->real_end)) + return EXT2_ET_INVALID_ARGUMENT; + + memcpy(out, bmap->bitmap + (start >> 3), (num+7) >> 3); + return 0; +} + +errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, + errcode_t magic, + __u32 start, __u32 num, + void *in) +{ + if (!bmap || (bmap->magic != magic)) + return magic; + + if ((start < bmap->start) || (start+num-1 > bmap->real_end)) + return EXT2_ET_INVALID_ARGUMENT; + + memcpy(bmap->bitmap + (start >> 3), in, (num+7) >> 3); + return 0; +} + +/* + * Compare @mem to zero buffer by 256 bytes. + * Return 1 if @mem is zeroed memory, otherwise return 0. + */ +int ext2fs_mem_is_zero(const char *mem, size_t len) +{ + static const char zero_buf[256]; + + while (len >= sizeof(zero_buf)) { + if (memcmp(mem, zero_buf, sizeof(zero_buf))) + return 0; + len -= sizeof(zero_buf); + mem += sizeof(zero_buf); + } + /* Deal with leftover bytes. */ + if (len) + return !memcmp(mem, zero_buf, len); + return 1; +} + +/* + * Return true if all of the bits in a specified range are clear + */ +static int ext2fs_test_clear_generic_bitmap_range(ext2fs_generic_bitmap bitmap, + unsigned int start, + unsigned int len) +{ + size_t start_byte, len_byte = len >> 3; + unsigned int start_bit, len_bit = len % 8; + int first_bit = 0; + int last_bit = 0; + int mark_count = 0; + int mark_bit = 0; + int i; + const char *ADDR = bitmap->bitmap; + + start -= bitmap->start; + start_byte = start >> 3; + start_bit = start % 8; + + if (start_bit != 0) { + /* + * The compared start block number or start inode number + * is not the first bit in a byte. + */ + mark_count = 8 - start_bit; + if (len < 8 - start_bit) { + mark_count = (int)len; + mark_bit = len + start_bit - 1; + } else + mark_bit = 7; + + for (i = mark_count; i > 0; i--, mark_bit--) + first_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the first byte. + * If there is any marked bit, this function returns 0. + */ + if (first_bit & ADDR[start_byte]) + return 0; + else if (len <= 8 - start_bit) + return 1; + + start_byte++; + len_bit = (len - mark_count) % 8; + len_byte = (len - mark_count) >> 3; + } + + /* + * The compared start block number or start inode number is + * the first bit in a byte. + */ + if (len_bit != 0) { + /* + * The compared end block number or end inode number is + * not the last bit in a byte. + */ + for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--) + last_bit |= 1 << mark_bit; + + /* + * Compare blocks or inodes in the last byte. + * If there is any marked bit, this function returns 0. + */ + if (last_bit & ADDR[start_byte + len_byte]) + return 0; + else if (len_byte == 0) + return 1; + } + + /* Check whether all bytes are 0 */ + return ext2fs_mem_is_zero(ADDR + start_byte, len_byte); +} + +int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); + if ((block < bitmap->start) || (block+num-1 > bitmap->real_end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, + block, bitmap->description); + return 0; + } + return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) + bitmap, block, num); +} + +int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, + ino_t inode, int num) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); + if ((inode < bitmap->start) || (inode+num-1 > bitmap->real_end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST, + inode, bitmap->description); + return 0; + } + return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) + bitmap, inode, num); +} + +void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_fast_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_fast_clear_bit(block + i - bitmap->start, + bitmap->bitmap); +} diff --git a/lib/libext2fs/orig/gen_bitmap64.c b/lib/libext2fs/orig/gen_bitmap64.c new file mode 100644 index 0000000..9e7b298 --- /dev/null +++ b/lib/libext2fs/orig/gen_bitmap64.c @@ -0,0 +1,563 @@ +/* + * gen_bitmap64.c --- routines to read, write, and manipulate the new qinode and + * block bitmaps. + * + * Copyright (C) 2007, 2008 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "bmap64.h" + +/* + * Design of 64-bit bitmaps + * + * In order maintain ABI compatibility with programs that don't + * understand about 64-bit blocks/inodes, + * ext2fs_allocate_inode_bitmap() and ext2fs_allocate_block_bitmap() + * will create old-style bitmaps unless the application passes the + * flag EXT2_FLAG_64BITS to ext2fs_open(). If this flag is + * passed, then we know the application has been recompiled, so we can + * use the new-style bitmaps. If it is not passed, we have to return + * an error if trying to open a filesystem which needs 64-bit bitmaps. + * + * The new bitmaps use a new set of structure magic numbers, so that + * both the old-style and new-style interfaces can identify which + * version of the data structure was used. Both the old-style and + * new-style interfaces will support either type of bitmap, although + * of course 64-bit operation will only be possible when both the + * new-style interface and the new-style bitmap are used. + * + * For example, the new bitmap interfaces will check the structure + * magic numbers and so will be able to detect old-stype bitmap. If + * they see an old-style bitmap, they will pass it to the gen_bitmap.c + * functions for handling. The same will be true for the old + * interfaces as well. + * + * The new-style interfaces will have several different back-end + * implementations, so we can support different encodings that are + * appropriate for different applications. In general the default + * should be whatever makes sense, and what the application/library + * will use. However, e2fsck may need specialized implementations for + * its own uses. For example, when doing parent directory pointer + * loop detections in pass 3, the bitmap will *always* be sparse, so + * e2fsck can request an encoding which is optimized for that. + */ + +static void warn_bitmap(ext2fs_generic_bitmap bitmap, + int code, __u64 arg) +{ +#ifndef OMIT_COM_ERR + if (bitmap->description) + com_err(0, bitmap->base_error_code+code, + "#%llu for %s", arg, bitmap->description); + else + com_err(0, bitmap->base_error_code + code, "#%llu", arg); +#endif +} + + +errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic, + int type, __u64 start, __u64 end, + __u64 real_end, + const char *descr, + ext2fs_generic_bitmap *ret) +{ + ext2fs_generic_bitmap bitmap; + struct ext2_bitmap_ops *ops; + errcode_t retval; + + switch (type) { + case EXT2FS_BMAP64_BITARRAY: + ops = &ext2fs_blkmap64_bitarray; + break; + default: + return EINVAL; + } + + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &bitmap); + if (retval) + return retval; + + /* XXX factor out, repeated in copy_bmap */ + bitmap->magic = magic; + bitmap->fs = fs; + bitmap->start = start; + bitmap->end = end; + bitmap->real_end = real_end; + bitmap->bitmap_ops = ops; + switch (magic) { + case EXT2_ET_MAGIC_INODE_BITMAP64: + bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; + break; + case EXT2_ET_MAGIC_BLOCK_BITMAP64: + bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; + break; + default: + bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; + } + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); + if (retval) { + ext2fs_free_mem(&bitmap); + return retval; + } + strcpy(bitmap->description, descr); + } else + bitmap->description = 0; + + retval = bitmap->bitmap_ops->new_bmap(fs, bitmap); + if (retval) { + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap); + return retval; + } + + *ret = bitmap; + return 0; +} + +void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + ext2fs_free_generic_bitmap(bmap); + return; + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + bmap->bitmap_ops->free_bmap(bmap); + + if (bmap->description) { + ext2fs_free_mem(&bmap->description); + bmap->description = 0; + } + bmap->magic = 0; + ext2fs_free_mem(&bmap); +} + +errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + char *descr, *new_descr; + ext2fs_generic_bitmap new_bmap; + errcode_t retval; + + if (!src) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(src)) + return ext2fs_copy_generic_bitmap(src, dest); + + if (!EXT2FS_IS_64_BITMAP(src)) + return EINVAL; + + /* Allocate a new bitmap struct */ + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &new_bmap); + if (retval) + return retval; + + /* Copy all the high-level parts over */ + new_bmap->magic = src->magic; + new_bmap->fs = src->fs; + new_bmap->start = src->start; + new_bmap->end = src->end; + new_bmap->real_end = src->real_end; + new_bmap->bitmap_ops = src->bitmap_ops; + new_bmap->base_error_code = src->base_error_code; + + descr = src->description; + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &new_descr); + if (retval) { + ext2fs_free_mem(&new_bmap); + return retval; + } + strcpy(new_descr, descr); + new_bmap->description = new_descr; + } + + retval = src->bitmap_ops->copy_bmap(src, new_bmap); + if (retval) { + ext2fs_free_mem(&new_bmap->description); + ext2fs_free_mem(&new_bmap); + return retval; + } + + *dest = new_bmap; + + return 0; +} + +errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap, + __u64 new_end, + __u64 new_real_end) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) + return ext2fs_resize_generic_bitmap(bmap->magic, new_end, + new_real_end, bmap); + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->resize_bmap(bmap, new_end, new_real_end); +} + +errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap, + errcode_t neq, + __u64 end, __u64 *oend) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + ext2_ino_t tmp_oend; + int retval; + + retval = ext2fs_fudge_generic_bitmap_end(bitmap, bitmap->magic, + neq, end, &tmp_oend); + if (oend) + *oend = tmp_oend; + return retval; + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + if (end > bitmap->real_end) + return neq; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +__u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) + return ext2fs_get_generic_bitmap_start(bitmap); + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + return bitmap->start; +} + +__u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap) +{ + if (!bitmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bitmap)) + return ext2fs_get_generic_bitmap_end(bitmap); + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return EINVAL; + + return bitmap->end; +} + +void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap) +{ + if (EXT2FS_IS_32_BITMAP(bitmap)) + ext2fs_clear_generic_bitmap(bitmap); + + bitmap->bitmap_ops->clear_bmap (bitmap); +} + +int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, + EXT2FS_MARK_ERROR, 0xffffffff); + return 0; + } + return ext2fs_mark_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_MARK_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->mark_bmap(bitmap, arg); +} + +int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, + 0xffffffff); + return 0; + } + return ext2fs_unmark_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_UNMARK_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->unmark_bmap(bitmap, arg); +} + +int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap, + __u64 arg) +{ + if (!bitmap) + return 0; + + if (EXT2FS_IS_32_BITMAP(bitmap)) { + if (arg & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, + 0xffffffff); + return 0; + } + return ext2fs_test_generic_bitmap(bitmap, arg); + } + + if (!EXT2FS_IS_64_BITMAP(bitmap)) + return 0; + + if ((arg < bitmap->start) || (arg > bitmap->end)) { + warn_bitmap(bitmap, EXT2FS_TEST_ERROR, arg); + return 0; + } + + return bitmap->bitmap_ops->test_bmap(bitmap, arg); +} + +errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *in) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((start+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bmap, EXT2FS_UNMARK_ERROR, + 0xffffffff); + return EINVAL; + } + return ext2fs_set_generic_bitmap_range(bmap, bmap->magic, + start, num, in); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->set_bmap_range(bmap, start, num, in); +} + +errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap, + __u64 start, unsigned int num, + void *out) +{ + if (!bmap) + return EINVAL; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((start+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2(bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return EINVAL; + } + return ext2fs_get_generic_bitmap_range(bmap, bmap->magic, + start, num, out); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->get_bmap_range(bmap, start, num, out); +} + +errcode_t ext2fs_compare_generic_bmap(errcode_t neq, + ext2fs_generic_bitmap bm1, + ext2fs_generic_bitmap bm2) +{ + blk64_t i; + + if (!bm1 || !bm2) + return EINVAL; + if (bm1->magic != bm2->magic) + return EINVAL; + + /* Now we know both bitmaps have the same magic */ + if (EXT2FS_IS_32_BITMAP(bm1)) + return ext2fs_compare_generic_bitmap(bm1->magic, neq, bm1, bm2); + + if (!EXT2FS_IS_64_BITMAP(bm1)) + return EINVAL; + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end)) + return neq; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_test_generic_bmap(bm1, i) != + ext2fs_test_generic_bmap(bm2, i)) + return neq; + + return 0; +} + +void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap) +{ + __u64 start, num; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + ext2fs_set_generic_bitmap_padding(bmap); + return; + } + + start = bmap->end + 1; + num = bmap->real_end - bmap->end; + bmap->bitmap_ops->mark_bmap_extent(bmap, start, num); + /* XXX ought to warn on error */ +} + +int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return EINVAL; + + if (num == 1) + return !ext2fs_test_generic_bmap((ext2fs_generic_bitmap) + bmap, block); + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return EINVAL; + } + return ext2fs_test_block_bitmap_range( + (ext2fs_generic_bitmap) bmap, block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return EINVAL; + + return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num); +} + +void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return; + } + ext2fs_mark_block_bitmap_range((ext2fs_generic_bitmap) bmap, + block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + if ((block < bmap->start) || (block+num-1 > bmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bmap->description); + return; + } + + bmap->bitmap_ops->mark_bmap_extent(bmap, block, num); +} + +void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap, + blk64_t block, unsigned int num) +{ + if (!bmap) + return; + + if (EXT2FS_IS_32_BITMAP(bmap)) { + if ((block+num) & ~0xffffffffULL) { + ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap, + EXT2FS_UNMARK_ERROR, 0xffffffff); + return; + } + ext2fs_unmark_block_bitmap_range((ext2fs_generic_bitmap) bmap, + block, num); + } + + if (!EXT2FS_IS_64_BITMAP(bmap)) + return; + + if ((block < bmap->start) || (block+num-1 > bmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bmap->description); + return; + } + + bmap->bitmap_ops->unmark_bmap_extent(bmap, block, num); +} + +int ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func) +{ +#ifndef OMIT_COM_ERR + if (bitmap && bitmap->description) + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "called %s with 64-bit bitmap for %s", func, + bitmap->description); + else + com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP, + "called %s with 64-bit bitmap", func); +#endif + return 0; +} diff --git a/lib/libext2fs/orig/get_pathname.c b/lib/libext2fs/orig/get_pathname.c new file mode 100644 index 0000000..7ac14db --- /dev/null +++ b/lib/libext2fs/orig/get_pathname.c @@ -0,0 +1,160 @@ +/* + * get_pathname.c --- do directry/inode -> name translation + * + * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Library + * General Public License, version 2. + * %End-Header% + */ + +/* + * + * ext2fs_get_pathname(fs, dir, ino, name) + * + * This function translates takes two inode numbers into a + * string, placing the result in .